Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -45,10 +43,12 @@ tnoremap <C-]> <C-\><C-n> " exit terminal mode
" ── File operations ───────────────────────────────────────────────────────────
nnoremap <silent> gF :tabedit <cfile><CR>
nnoremap <silent> <leader>fy :let @+ = expand("%:p")<CR>
nnoremap <silent> <leader>ft :call TabsVim_FzfOpenInTab()<CR>

" ── Git log ───────────────────────────────────────────────────────────────────
nnoremap <silent> <leader>gg :call TabsVim_FlogInTab()<CR>
" ── Ecosystem integrations (direct calls — no plugin wrapper needed) ──────────
" fzf.vim: open file picker with tabedit as the sink
nnoremap <silent> <leader>ft :call fzf#vim#files('', fzf#vim#with_preview({'sink': 'tabedit'}), 0)<CR>
" vim-flog: open full git log in a new tab
nnoremap <silent> <leader>gg :Flogsplit -open-cmd=tabedit -all<CR>

" ── Ecosystem buffer close (q → :tabclose) ───────────────────────────────────
" Must be set before the plugin loads/is sourced (e.g. before plug#end() when using vim-plug).
Expand All @@ -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

Expand Down
31 changes: 17 additions & 14 deletions docs/adrs/005.tabs-vim.ecosystem-support.md
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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.
57 changes: 33 additions & 24 deletions docs/specs/ecosystem-support.md
Original file line number Diff line number Diff line change
@@ -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 | ✅ |
Comment thread
jesse23 marked this conversation as resolved.

---

Expand All @@ -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 <silent> <leader>ft :call fzf#vim#files('', fzf#vim#with_preview({'sink': 'tabedit'}), 0)<CR>
```

### vim-flog: open git log in a new tab

```vim
" Requires: rbong/vim-flog
nnoremap <silent> <leader>gg :Flogsplit -open-cmd=tabedit -all<CR>
```

### Full ecosystem wiring block

```vim
" ── Git log ──────────────────────────────────────────────────────────────────
nnoremap <silent> <leader>gg :call TabsVim_FlogInTab()<CR>
" ── Ecosystem integrations ────────────────────────────────────────────────────
nnoremap <silent> <leader>ft :call fzf#vim#files('', fzf#vim#with_preview({'sink': 'tabedit'}), 0)<CR>
nnoremap <silent> <leader>gg :Flogsplit -open-cmd=tabedit -all<CR>

" ── 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']
```

Expand All @@ -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)
24 changes: 7 additions & 17 deletions docs/specs/key-binding.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# SPEC: Key Binding

**Last Updated:** 2026-04-04
**Last Updated:** 2026-04-06

---

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -85,10 +73,12 @@ nnoremap <silent> <leader>tt :call TabsVim_NewTabTerm()<CR>
" ── File operations — native commands ────────────────────────────────────────
nnoremap <silent> gF :tabedit <cfile><CR>
nnoremap <silent> <leader>fy :let @+ = expand("%:p")<CR>
nnoremap <silent> <leader>ft :call TabsVim_FzfOpenInTab()<CR>

" ── Git log ───────────────────────────────────────────────────────────────────
nnoremap <silent> <leader>gg :call TabsVim_FlogInTab()<CR>
" ── Ecosystem integrations (direct calls — no plugin wrapper needed) ──────────
" fzf.vim: open file picker with tabedit as the sink
nnoremap <silent> <leader>ft :call fzf#vim#files('', fzf#vim#with_preview({'sink': 'tabedit'}), 0)<CR>
" vim-flog: open full git log in a new tab
nnoremap <silent> <leader>gg :Flogsplit -open-cmd=tabedit -all<CR>
Comment thread
jesse23 marked this conversation as resolved.

" ── Ecosystem buffer close (q → :tabclose) ───────────────────────────────────
" Must be set before plug#end() — read once at plugin load time.
Expand Down Expand Up @@ -140,4 +130,4 @@ In normal mode, `<C-]>` 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 | — | |
11 changes: 6 additions & 5 deletions docs/specs/tabs.vim.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# tabs.vim

**Version:** 0.1.0
**Last Updated:** 2026-04-04
**Last Updated:** 2026-04-06
**Status:** Active Development

---
Expand Down Expand Up @@ -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 | `<Tab>` / `<S-Tab>` for next/prev; `<leader>[1-9]` for direct jump |
| **Quick Creation** | Open files faster with consistent entry points | `<leader>ft` (fzf) → `tabedit`, `<leader>wt` for new empty tab |
| **Quick Creation** | Open files faster with consistent entry points | `<leader>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 |
Comment thread
jesse23 marked this conversation as resolved.
| **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 |
Expand All @@ -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.
Expand All @@ -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

Expand Down Expand Up @@ -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`

---

Expand All @@ -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 |
22 changes: 0 additions & 22 deletions plugin/tabs.vim
Original file line number Diff line number Diff line change
Expand Up @@ -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*$.
Expand Down
Loading