aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--README.md39
-rw-r--r--lua/buffer-browser.lua94
-rw-r--r--plugin/buffer-browser.vim13
3 files changed, 146 insertions, 0 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..70dcc7f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,39 @@
+# NVIM Buffer Browser Navigation
+
+`:b#` on steroids. Browse your buffers like you browse history in a browser.
+
+## Motivation
+
+While `:bnext` and `:bprev` allows you to browse between buffers, it is not
+very intuitive. Oftentimes I would jump to definitions, a different file
+through netrw, an org file and then when using bprev I would end up in a
+completely different file, because buffers are ordered by when they are opened.
+`:b#`, <Ctrl-I> and <Ctrl-O> works only assuming you didn't navigate one step
+further in the documentation.
+
+I wanted to be able to browse buffers in a way that is similar to how you
+browse tabs in a browser.
+
+## Usage
+
+Install through any of your favorite plugin managers. For example, with
+[lazy](https://www.lazyvim.org).
+
+This plugin implements two functions: `require('buffer-browser').next()` and
+`require('buffer-browser').prev()`.
+
+You can easily map these to whatever you want. Here is an example `init.lua`:
+```lua
+vim.api.nvim_set_keymap('n', '<leader>b[', require("buffer-browser").next(), {desc = "Next [B]uffer [[]"})
+vim.api.nvim_set_keymap('n', '<leader>b]', require("buffer-browser").prev(), {desc = "Previous [B]uffer []]"})
+```
+
+## Splits
+
+This plugin works with splits. If you have multiple splits open, the plugin
+will fork the split history and create a different "buffer history" for that
+particular split.
+
+## Credits
+
+The plugin is mostly based on [ton/vim-bufsurf](https://github.com/ton/vim-bufsurf), but rewritten in Lua.
diff --git a/lua/buffer-browser.lua b/lua/buffer-browser.lua
new file mode 100644
index 0000000..c6a41a8
--- /dev/null
+++ b/lua/buffer-browser.lua
@@ -0,0 +1,94 @@
+-- BUFFER BROWSER
+-- Author: Marc Coquand
+-- See readme for explanation on what it does.
+-- This code contains examples that can be used to test the code.
+-- Run them in `lua %`
+-- or with a code runner, like sniprun.
+local api = vim.api
+
+local function StateAppend(bufName, state, filters)
+ -- State has three fields, current = current buffer, previous = previous buffer, and future = future buffers
+ -- This function clears the future, and appends the current to the previous, and sets the current to the next
+
+ -- If filters matches the bufName, skip the buffer
+ if filters ~= nil then
+ for _, filter in ipairs(filters) do
+ if string.match(bufName, filter) then
+ return state
+ end
+ end
+ end
+
+ -- Clear the future
+ state.future = {}
+ -- Append the current to the previous
+ table.insert(state.previous, state.current)
+ -- Set the current to the next
+ state.current = bufName
+
+ return state
+end
+-- Test StateAppend
+-- local state = { current = 1, previous = { 2, 3 }, future = { 4, 5 } }
+-- print(StateAppend(6, state, {})['current'])
+-- -- > 6
+-- state = { current = 1, previous = { 2, 3 }, future = { 4, 5 } }
+-- print(StateAppend(6, state, {})['previous'][1])
+-- -- > 2
+-- state = { current = 1, previous = { 2, 3 }, future = { 4, 5 } }
+-- print(StateAppend(6, state, { 6 })['current'])
+-- -- > 1
+
+local function StateGoBack(state)
+ -- Go back in the state by setting current to the first element of previous, and appending the current to the future
+ -- Check if past is empty, if so return state as is
+ if #state.previous == 0 then
+ return state
+ end
+
+ table.insert(state.future, state.current)
+ state.current = table.remove(state.previous, 1)
+ return state
+end
+-- Test StateGoBack
+-- local state = { current = 1, previous = { 2, 3 }, future = { 4, 5 } }
+-- print(StateGoBack(StateGoBack(StateGoBack(state)))['current'])
+-- > 3
+
+local function StateGoForward(state)
+ -- Go forward in the state by setting current to the first element of future, and appending the current to the previous
+
+ -- Check if future is empty, if so return state as is
+ if #state.future == 0 then
+ return state
+ end
+
+ table.insert(state.previous, state.current)
+ state.current = table.remove(state.future, 1)
+
+ return state
+end
+-- Test StateGoForward
+-- local state = { current = 1, previous = { 2, 3 }, future = { 4, 5 } }
+-- print(StateGoForward(StateGoForward(StateGoForward(state)))['current'])
+-- > 5
+
+function BufferEnter()
+ local bufName = api.nvim_buf_get_name(0)
+ local state = vim.g.buffer_browser_state
+ local filters = vim.g.buffer_browser_filters
+ vim.g.buffer_browser_state = StateAppend(bufName, state, filters)
+end
+
+local BufferBrowserGroup = api.nvim_create_augroup('BufferBrowser')
+
+api.nvim_create_autocmd('WinEnter', {
+ '*',
+ 'lua BufferEnter()',
+ { group = BufferBrowserGroup }
+})
+api.nvim_create_autocmd('BufEnter', {
+ '*',
+ 'lua BufferEnter()',
+ { group = BufferBrowserGroup }
+})
diff --git a/plugin/buffer-browser.vim b/plugin/buffer-browser.vim
new file mode 100644
index 0000000..dfdb366
--- /dev/null
+++ b/plugin/buffer-browser.vim
@@ -0,0 +1,13 @@
+if exists('g:loaded_buffer_browser') | finish | endif " prevent loading file twice
+
+let s:save_cpo = &cpo " save user coptions
+set cpo&vim " reset them to defaults
+
+" command to run our plugin
+command! BufferBrowserNext lua require'buffer_browser'.next()
+command! BufferBrowserPrevious lua require'buffer_browser'.prev()
+
+let &cpo = s:save_cpo " and restore after
+unlet s:save_cpo
+
+let g:loaded_whid = 1