diff --git a/README.md b/README.md index 533a4cd..9f98d40 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,10 @@ # tabs.vim -A lightweight Vim plugin that replaces the native tabline with a clean, mode-aware bar: numbered tabs on the left, a color-coded mode pill on the right. Exposes public functions for terminal toggling, window management, and ecosystem integrations — no keybindings installed out of the box. +A lightweight Vim plugin that replaces the native tabline with a clean, mode-aware bar: numbered tabs on the left, a color-coded mode pill on the right. Exposes public functions for terminal toggling and window management — no keybindings installed out of the box. ## Requirements - Vim 8.2+ or Neovim 0.7+ -- Optional: [junegunn/fzf.vim](https://github.com/junegunn/fzf.vim) for file-picker integration -- Optional: [vim-flog](https://github.com/rbong/vim-flog) for git log integration ## Installation @@ -45,10 +43,12 @@ tnoremap " exit terminal mode " ── File operations ─────────────────────────────────────────────────────────── nnoremap gF :tabedit nnoremap fy :let @+ = expand("%:p") -nnoremap ft :call TabsVim_FzfOpenInTab() -" ── Git log ─────────────────────────────────────────────────────────────────── -nnoremap gg :call TabsVim_FlogInTab() +" ── Ecosystem integrations (direct calls — no plugin wrapper needed) ────────── +" fzf.vim: open file picker with tabedit as the sink +nnoremap ft :call fzf#vim#files('', fzf#vim#with_preview({'sink': 'tabedit'}), 0) +" vim-flog: open full git log in a new tab +nnoremap gg :Flogsplit -open-cmd=tabedit -all " ── Ecosystem buffer close (q → :tabclose) ─────────────────────────────────── " Must be set before the plugin loads/is sourced (e.g. before plug#end() when using vim-plug). @@ -66,8 +66,6 @@ let g:tabs_vim_tabclose_types = ['floggraph', 'git', 'diff'] | `TabsVim_NewTabTerm()` | Open a new terminal in its own tab | | `TabsVim_CloseOrHide()` | Close window; if last window prompt to quit; if terminal, hide it | | `TabsVim_RenameBuffer()` | Prompt to rename the current buffer | -| `TabsVim_FzfOpenInTab()` | Open fzf file picker with `tabedit` as the sink (requires fzf.vim) | -| `TabsVim_FlogInTab()` | Open vim-flog git log in a new tab (requires vim-flog) | ## Configuration diff --git a/docs/adrs/005.tabs-vim.ecosystem-support.md b/docs/adrs/005.tabs-vim.ecosystem-support.md index 2cf5d17..acf4dfc 100644 --- a/docs/adrs/005.tabs-vim.ecosystem-support.md +++ b/docs/adrs/005.tabs-vim.ecosystem-support.md @@ -1,35 +1,32 @@ -# 005. Ecosystem Support — FlogInTab and Ecosystem Buffer Close +# 005. Ecosystem Support — Tabclose Autocmd and Vimrc Recipes **SPEC:** ecosystem-support -**Status:** Accepted -**Last Updated:** 2026-04-05 +**Status:** Revised (2026-04-06 — see revision note below) +**Last Updated:** 2026-04-06 --- ## Decision -Add two ecosystem integration features to `plugin/tabs.vim`: +One ecosystem integration feature lives in `plugin/tabs.vim`: -1. **`TabsVim_FlogInTab()`** — opens vim-flog's full-repo git log in a new tab via `Flogsplit -open-cmd=tabedit -all`. Guards with `exists(':Flogsplit')` and emits a warning if vim-flog is not loaded. Exposed as a `TabsVim_*` function (rather than a vimrc one-liner) for the same discoverability reason as `TabsVim_FzfOpenInTab()` — users exploring the API should not need to inspect vim-flog's command syntax. +1. **`g:tabs_vim_tabclose_types` autocmd** — reads a user-supplied list of tokens at plugin load time and installs buffer-local `q` → `:tabclose` mappings for matching filetypes or conditions. Default is empty (no OOTB behavior). Supported tokens: `'floggraph'`, `'git'`, `'diff'`; any other string is treated as a `FileType` name. -2. **`g:tabs_vim_tabclose_types` autocmd** — reads a user-supplied list of tokens at plugin load time and installs buffer-local `q` → `:tabclose` mappings for matching filetypes or conditions. Default is empty (no OOTB behavior). Supported tokens: `'floggraph'`, `'git'`, `'diff'`; any other string is treated as a `FileType` name. +Third-party plugin calls (fzf.vim, vim-flog) are **not wrapped** by tabs.vim. They are documented as vimrc recipes in `ecosystem-support.md`. --- ## Context -Users who open ecosystem tool outputs (flog graph, fugitive commit view, vimdiff) in tabs consistently need two things: - -1. A single command to open the tool in a new tab (the same pattern `TabsVim_FzfOpenInTab()` already established for fzf). -2. A `q` binding to close the whole tab when done — the "modal overlay" mental model. - -Without plugin support, users must write three separate augroups in their vimrc, each guarding a different filetype or buffer condition. This is boilerplate that belongs in the plugin because: +Users who open ecosystem tool outputs (flog graph, fugitive commit view, vimdiff) in tabs need a `q` binding to close the whole tab when done — the "modal overlay" mental model. The `g:tabs_vim_tabclose_types` autocmd covers this because: - It is tab-scoped behavior (`:tabclose`, not `:q` or `:close`) - The same pattern repeats identically for every ecosystem tool - It is discoverable — users looking for tab integrations will find it here The `diff` condition (`WinEnter` + `&diff`) requires special handling (not a FileType) but is common enough to be a first-class supported token. +Wrapping third-party functions like `fzf#vim#files` and `Flogsplit` inside `TabsVim_*` functions was initially done for discoverability, but introduces undeclared optional dependencies and causes `exists()` autoload detection issues. These are one-liner vimrc mappings the user can own directly — no plugin wrapper adds meaningful value. + --- ## Considered Options @@ -47,8 +44,14 @@ The list-based approach keeps one config axis while remaining extensible. Any fi ## Consequences -- `TabsVim_FlogInTab()` is added to the Public Function API in `key-binding.md` under a new "Git Integration" section. - `g:tabs_vim_tabclose_types` is documented in both `ecosystem-support.md` and the recommended vimrc block in `key-binding.md`. - The plugin continues to install zero OOTB keybindings (ADR-004 is not affected); the autocmd only fires if the user sets `g:tabs_vim_tabclose_types`. - Users with existing manual augroups for `q` → `:tabclose` can migrate to the config or leave their vimrc unchanged — both work. -- `diff` handling uses `WinEnter` + `&diff` guard (matching the existing vimrc pattern) rather than a filetype, since vimdiff buffers don't have a dedicated filetype. +- `diff` handling uses `WinEnter` + `&diff` guard rather than a filetype, since vimdiff buffers don't have a dedicated filetype. +- fzf/flog integrations are documented as direct vimrc mappings in `ecosystem-support.md`; no `TabsVim_*` wrappers are provided. + +--- + +## Revision Note (2026-04-06) + +The original ADR-005 (2026-04-05) included `TabsVim_FlogInTab()` as a second plugin-side feature alongside `g:tabs_vim_tabclose_types`. It was removed because wrapping `Flogsplit` (and the earlier `TabsVim_FzfOpenInTab` wrapping `fzf#vim#files`) introduced undeclared optional dependencies and caused `exists()` autoload detection failures. The new position: third-party function calls are vimrc-owned; the plugin documents recipes but does not proxy them. diff --git a/docs/specs/ecosystem-support.md b/docs/specs/ecosystem-support.md index c361a61..3901088 100644 --- a/docs/specs/ecosystem-support.md +++ b/docs/specs/ecosystem-support.md @@ -1,39 +1,28 @@ # SPEC: Ecosystem Support -**Last Updated:** 2026-04-05 +**Last Updated:** 2026-04-06 --- ## Description -Defines tab-aware integration functions for common Vim ecosystem plugins. The plugin already exposes `TabsVim_FzfOpenInTab()` for fzf. This spec extends that pattern to cover vim-flog (git log viewer) and establishes a configurable autocmd for binding `q` → `:tabclose` in ecosystem tool buffers (flog graph, fugitive, vimdiff). +Defines how `tabs.vim` integrates with common Vim ecosystem plugins. The plugin does **not** wrap third-party plugin functions — those one-liner wrappers belong in the user's vimrc and are documented here as recipes. The one plugin-side feature is `g:tabs_vim_tabclose_types`, which wires `q` → `:tabclose` for specified buffer types. -The common workflow: open a tool's output in a dedicated tab, then press `q` to close the whole tab when done — the same mental model as a modal overlay. Without plugin support, users must replicate three separate augroups across their vimrc. - -**Persona:** Vim/Neovim users with fzf, vim-flog, and/or vim-fugitive in their setup who open those tools' outputs in tabs. +**Persona:** Vim/Neovim users with fzf, vim-flog, and/or vim-fugitive who open those tools' outputs in dedicated tabs. --- -## Features +## Design Boundary -| Feature | Description | ADR | Done? | -|---------|-------------|-----|-------| -| **`TabsVim_FlogInTab()`** | Open vim-flog git log in a new tab (`Flogsplit -open-cmd=tabedit -all`) | ADR-005 | ⬜ | -| **Ecosystem buffer close** | `g:tabs_vim_tabclose_types` list: buffer types/conditions that get `q` → `:tabclose` auto-wired | ADR-005 | ⬜ | +Third-party integrations (fzf, vim-flog, vimdiff) are **not wrapped** by tabs.vim. Wrapping them introduces undeclared optional dependencies and couples the plugin to unrelated workflows. The integration is instead documented as vimrc recipes — one-line mappings the user owns directly. --- -## Public Function API - -### Git Integration - -| Function | Description | -|----------|-------------| -| `TabsVim_FlogInTab()` | Open vim-flog full-repo git log in a new tab (requires vim-flog) | - -### Ecosystem Buffer Close +## Features -No function — controlled by config only (see below). +| Feature | Description | ADR | Done? | +|---------|-------------|-----|-------| +| **Ecosystem buffer close** | `g:tabs_vim_tabclose_types` list: buffer types that get `q` → `:tabclose` auto-wired | ADR-005 | ✅ | --- @@ -60,13 +49,33 @@ Users may pass any valid `FileType` name to cover other tools (e.g. `'fugitivebl --- -## Recommended vimrc Wiring +## Vimrc Recipes + +These are direct calls — no plugin wrapper needed. + +### fzf.vim: open file picker in a new tab + +```vim +" Requires: junegunn/fzf + junegunn/fzf.vim +nnoremap ft :call fzf#vim#files('', fzf#vim#with_preview({'sink': 'tabedit'}), 0) +``` + +### vim-flog: open git log in a new tab + +```vim +" Requires: rbong/vim-flog +nnoremap gg :Flogsplit -open-cmd=tabedit -all +``` + +### Full ecosystem wiring block ```vim -" ── Git log ────────────────────────────────────────────────────────────────── -nnoremap gg :call TabsVim_FlogInTab() +" ── Ecosystem integrations ──────────────────────────────────────────────────── +nnoremap ft :call fzf#vim#files('', fzf#vim#with_preview({'sink': 'tabedit'}), 0) +nnoremap gg :Flogsplit -open-cmd=tabedit -all " ── Ecosystem buffer close (q → :tabclose) ─────────────────────────────────── +" Must be set before plug#end() — read once at plugin load time. let g:tabs_vim_tabclose_types = ['floggraph', 'git', 'diff'] ``` @@ -75,4 +84,4 @@ let g:tabs_vim_tabclose_types = ['floggraph', 'git', 'diff'] ## Related - [key-binding.md](key-binding.md) — full public function API and vimrc wiring reference -- ADR-005 — decision record for this spec +- [ADR-005](../adrs/005.tabs-vim.ecosystem-support.md) — decision record for this spec (revised 2026-04-06: wrapper functions removed) diff --git a/docs/specs/key-binding.md b/docs/specs/key-binding.md index 46d8c73..6a627b4 100644 --- a/docs/specs/key-binding.md +++ b/docs/specs/key-binding.md @@ -1,6 +1,6 @@ # SPEC: Key Binding -**Last Updated:** 2026-04-04 +**Last Updated:** 2026-04-06 --- @@ -41,18 +41,6 @@ Only operations with real logic are exposed as `TabsVim_*` functions. Simple one | `TabsVim_CloseOrHide()` | Close the current window; if last window, prompt to quit Vim; if a terminal buffer, toggle-hide it instead | | `TabsVim_RenameBuffer()` | Prompt to rename the current buffer | -### FZF Integration - -| Function | Description | -|----------|-------------| -| `TabsVim_FzfOpenInTab()` | Open fzf file picker with `tabedit` as the sink (requires fzf.vim) | - -### Git Integration - -| Function | Description | -|----------|-------------| -| `TabsVim_FlogInTab()` | Open vim-flog full-repo git log in a new tab (requires vim-flog) | - --- ## Recommended vimrc Wiring @@ -85,10 +73,12 @@ nnoremap tt :call TabsVim_NewTabTerm() " ── File operations — native commands ──────────────────────────────────────── nnoremap gF :tabedit nnoremap fy :let @+ = expand("%:p") -nnoremap ft :call TabsVim_FzfOpenInTab() -" ── Git log ─────────────────────────────────────────────────────────────────── -nnoremap gg :call TabsVim_FlogInTab() +" ── Ecosystem integrations (direct calls — no plugin wrapper needed) ────────── +" fzf.vim: open file picker with tabedit as the sink +nnoremap ft :call fzf#vim#files('', fzf#vim#with_preview({'sink': 'tabedit'}), 0) +" vim-flog: open full git log in a new tab +nnoremap gg :Flogsplit -open-cmd=tabedit -all " ── Ecosystem buffer close (q → :tabclose) ─────────────────────────────────── " Must be set before plug#end() — read once at plugin load time. @@ -140,4 +130,4 @@ In normal mode, `` is the native "jump to tag under cursor" (ctags / cscope | **No OOTB keybindings** | Plugin installs zero keymaps; mouse DnD infrastructure only | ADR-004 | ✅ | | **Public function API** | All operations promoted to `TabsVim_*` public functions | ADR-004 | ✅ | | **Example vimrc block** | Ready-made mapping block for users to copy into vimrc | — | ✅ | -| **Ecosystem integrations** | `TabsVim_FlogInTab()` and `g:tabs_vim_tabclose_types` config | ADR-005 | ⬜ | +| **Ecosystem buffer close** | `g:tabs_vim_tabclose_types` wires `q` → `:tabclose` for specified buffer types | — | ✅ | diff --git a/docs/specs/tabs.vim.md b/docs/specs/tabs.vim.md index faa3f84..55b01d8 100644 --- a/docs/specs/tabs.vim.md +++ b/docs/specs/tabs.vim.md @@ -1,7 +1,7 @@ # tabs.vim **Version:** 0.1.0 -**Last Updated:** 2026-04-04 +**Last Updated:** 2026-04-06 **Status:** Active Development --- @@ -97,7 +97,7 @@ This is why `tabs.vim` supporting terminal operations (opening terminal in new t | Goal | Why | Approach | |------|-----|----------| | **Fast Navigation** | Reduce cognitive load when switching files | `` / `` for next/prev; `[1-9]` for direct jump | -| **Quick Creation** | Open files faster with consistent entry points | `ft` (fzf) → `tabedit`, `wt` for new empty tab | +| **Quick Creation** | Open files faster with consistent entry points | `wt` for new empty tab; fzf/flog wiring documented as vimrc recipes | | **Visual Clarity** | Make tabs the center of workflow | Color scheme integration, tab status in statusline | | **Ecosystem Integration** | Work with existing plugins (fzf, git, file tree) | Respect plugin output, use standard Vim events | | **Cross-Platform** | Work on macOS, Linux, and Windows with Vim/Neovim | Pure VimScript, no dependencies on external tools | @@ -123,7 +123,7 @@ tabs.vim/ All tab operations are pure Vim commands; no plugin state is maintained. Tab state is delegated to Vim's native tab list. Terminal buffer state is tracked minimally via module-level variables (`s:term_bufnr`, `s:vterm_bufnr`). **2. Plugin Composition** -Tabs plugin plays well with others (fzf, Fern, vim-fugitive). It doesn't reimplement file picking or git operations — it just routes output to tabs via `:tabedit`. +Tabs plugin plays well with others (fzf, Fern, vim-fugitive). It doesn't reimplement or wrap file picking / git operations — those one-liner calls belong in the user's vimrc. The plugin documents integration recipes but owns no third-party function calls. **3. Configuration Flexibility** All keybindings are configurable. Users can disable features (e.g., if they don't use tabs) without source code changes. @@ -132,7 +132,7 @@ All keybindings are configurable. Users can disable features (e.g., if they don' Dracula color scheme is the built-in default; the tab bar can show a mode pill (Normal/Insert/Visual/Replace/Command/Terminal) and can style the selected tab by mode, depending on `g:tabs_vim_mode_style`. Users may override any or all mode colors via `g:tabs_vim_colors`. **5. Vimrc Integration Boundary** -The plugin owns all behavior it directly triggers. Integration with third-party plugins (vim-flog, vim-fugitive, diff buffers) — for example, binding `q` to `:tabclose` in their buffer types — is intentionally left to the user's vimrc. These bindings are tab-aware but depend on optional external plugins; pulling them into tabs.vim would introduce undeclared dependencies and couple the plugin to unrelated workflows. +The plugin owns all behavior it directly triggers. Third-party function calls (e.g. `fzf#vim#files`, `Flogsplit`) are not wrapped — those one-liner mappings belong in the user's vimrc and are documented as recipes in `ecosystem-support.md`. The exception is `g:tabs_vim_tabclose_types`, which the plugin owns because it installs tab-scoped autocmd logic (`q` → `:tabclose`) that is boilerplate-identical across every ecosystem tool and carries no third-party function dependency. ### Color Configuration Contract @@ -258,7 +258,7 @@ let g:tabs_vim_colors = { ## Related Specs - [key-binding.md](key-binding.md) — public `TabsVim_*` function API, OOTB behavior contract, and recommended vimrc wiring -- [ecosystem-support.md](ecosystem-support.md) — tab-aware integrations for fzf, vim-flog, vim-fugitive, and vimdiff +- [ecosystem-support.md](ecosystem-support.md) — vimrc recipes for fzf/flog and plugin-side `g:tabs_vim_tabclose_types` --- @@ -270,3 +270,4 @@ let g:tabs_vim_colors = { | 2026-04-04 | Extend color config: plugin now owns TabLine/TabLineSel/TabLineFill via `tabline`, `tabline_sel`, `tabline_fill` keys | | 2026-04-04 | Add Vimrc Integration Boundary pattern; mark completed features; align Features table to template | | 2026-04-04 | Rename to `g:tabs_vim_mode_style` enum (`all` / `tabs` / `mode`) with `all` as default | +| 2026-04-06 | Clarify Vimrc Integration Boundary: plugin owns `g:tabs_vim_tabclose_types` autocmd; third-party function calls are vimrc recipes | diff --git a/plugin/tabs.vim b/plugin/tabs.vim index 5495539..4e0b365 100644 --- a/plugin/tabs.vim +++ b/plugin/tabs.vim @@ -97,28 +97,6 @@ endfunction " ECOSYSTEM """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -" fzf: open file picker with tabedit as the sink -function! TabsVim_FzfOpenInTab() abort - if !exists('*fzf#vim#files') || !exists('*fzf#vim#with_preview') - echohl WarningMsg - echo 'tabs.vim: TabsVim_FzfOpenInTab requires fzf.vim (fzf#vim#files not available)' - echohl None - return - endif - call fzf#vim#files('', fzf#vim#with_preview({'sink': 'tabedit'}), 0) -endfunction - -" vim-flog: open full-repo git log in a new tab -function! TabsVim_FlogInTab() abort - if !exists(':Flogsplit') - echohl WarningMsg - echo 'tabs.vim: TabsVim_FlogInTab requires vim-flog (:Flogsplit not available)' - echohl None - return - endif - Flogsplit -open-cmd=tabedit -all -endfunction - " g:tabs_vim_tabclose_types: wire q → :tabclose for specified buffer types. " Must be set before the plugin loads. Supported tokens: 'floggraph', 'git', " 'diff', or any FileType name matching ^\h\w*$.