Skip to content

Commit

Permalink
Feature: 'pinned' buffers similar to chrome tabs (#145)
Browse files Browse the repository at this point in the history
* Feature: 'pinned' buffers similar to chrome tabs

* Add help docs for BufferPin command and options

* Remove the pin_status option
  • Loading branch information
stevearc authored Aug 3, 2021
1 parent 28319dd commit f677f1d
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 21 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ nnoremap <silent> <A-6> :BufferGoto 6<CR>
nnoremap <silent> <A-7> :BufferGoto 7<CR>
nnoremap <silent> <A-8> :BufferGoto 8<CR>
nnoremap <silent> <A-9> :BufferLast<CR>
" Pin/unpin buffer
nnoremap <silent> <A-p> :BufferPin<CR>
" Close buffer
nnoremap <silent> <A-c> :BufferClose<CR>
" Wipeout buffer
Expand Down Expand Up @@ -234,6 +236,7 @@ let bufferline.icon_separator_active = '▎'
let bufferline.icon_separator_inactive = '▎'
let bufferline.icon_close_tab = ''
let bufferline.icon_close_tab_modified = '●'
let bufferline.icon_pinned = '車'
" Sets the maximum padding width with which to surround each tab.
let bufferline.maximum_padding = 4
Expand Down Expand Up @@ -301,6 +304,7 @@ vim.g.bufferline = {
icon_separator_inactive = '',
icon_close_tab = '',
icon_close_tab_modified = '',
icon_pinned = '',

-- Sets the maximum padding width with which to surround each tab
maximum_padding = 1,
Expand Down
8 changes: 7 additions & 1 deletion doc/barbar.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ The name of each command should be descriptive enough for you to use it.
nnoremap <silent> <A-7> :BufferGoto 7<CR>
nnoremap <silent> <A-8> :BufferGoto 8<CR>
nnoremap <silent> <A-9> :BufferLast<CR>
" Pin/unpin buffer
nnoremap <silent> <A-p> :BufferPin<CR>
" Close buffer
nnoremap <silent> <A-c> :BufferClose<CR>
Expand Down Expand Up @@ -199,6 +200,11 @@ Here are the groups that you should define if you'd like to style Barbar.
The button used to close the tab when it has been modified since
last save.

*g:bufferline.icon_pinned*
`g:bufferline.icon_pinned` string (default '車')

The icon used to indicate that a buffer is pinned.

*g:bufferline.closable*
`g:bufferline.closable` boolean (default v:true)

Expand Down
6 changes: 6 additions & 0 deletions lua/bufferline/layout.lua
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ local function calculate_buffers_width(state, base_width)
+ 1 -- space-after-buffer-index
end

if state.is_pinned(buffer_number) then
width = width
+ 1 -- spacing after filename
+ strwidth(opts.icon_pinned)
end

if opts.closable then
width = width
+ strwidth(not nvim.buf_get_option(buffer_number, 'modified') -- close-icon
Expand Down
10 changes: 10 additions & 0 deletions lua/bufferline/render.lua
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,10 @@ local function render(update_names)
local iconPrefix = ''
local icon = ''

-- The pin icon
local pinPrefix = ''
local pin = ''

if has_numbers then
local number_text = tostring(i)
bufferIndexPrefix = hl('Buffer' .. status .. 'Index')
Expand All @@ -173,6 +177,11 @@ local function render(update_names)
iconPrefix = has_icon_custom_colors and hl('Buffer' .. status .. 'Icon') or hlName and hl(hlName) or namePrefix
icon = iconChar .. ' '
end

if state.is_pinned(buffer_number) then
pinPrefix = namePrefix
pin = ' ' .. icons.pinned
end
end

local closePrefix = ''
Expand Down Expand Up @@ -211,6 +220,7 @@ local function render(update_names)
{iconPrefix, icon},
{jumpLetterPrefix, jumpLetter},
{namePrefix, name},
{pinPrefix, pin},
{'', padding},
{'', ' '},
{closePrefix, close},
Expand Down
92 changes: 72 additions & 20 deletions lua/bufferline/state.lua
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ local ANIMATION_OPEN_DURATION = 150
local ANIMATION_OPEN_DELAY = 50
local ANIMATION_CLOSE_DURATION = 100
local ANIMATION_SCROLL_DURATION = 200
local PIN = "bufferline_pin"

--------------------------------
-- Section: Application state --
Expand Down Expand Up @@ -56,6 +57,32 @@ function m.get_buffer_data(id)
return m.buffers_by_id[id]
end

-- Pinned buffers

local function is_pinned(bufnr)
local ok, val = pcall(vim.api.nvim_buf_get_var, bufnr, PIN)
return ok and val
end

local function sort_pins_to_left()
local pinned = {}
local unpinned = {}
for _, bufnr in ipairs(m.buffers) do
if is_pinned(bufnr) then
table.insert(pinned, bufnr)
else
table.insert(unpinned, bufnr)
end
end
m.buffers = vim.list_extend(pinned, unpinned)
end

local function toggle_pin(bufnr)
bufnr = bufnr or 0
vim.api.nvim_buf_set_var(bufnr, PIN, not is_pinned(bufnr))
sort_pins_to_left()
vim.fn["bufferline#update"]()
end

-- Scrolling

Expand Down Expand Up @@ -140,6 +167,8 @@ local function open_buffers(new_buffers)
end
end

sort_pins_to_left()

-- We're done if there is no animations
if vim.g.bufferline.animation == false then
return
Expand Down Expand Up @@ -338,6 +367,7 @@ local function move_buffer(from_idx, to_idx)
local bufnr = m.buffers[from_idx]
table.remove(m.buffers, from_idx)
table.insert(m.buffers, to_idx, bufnr)
sort_pins_to_left()

vim.fn['bufferline#update']()
end
Expand Down Expand Up @@ -457,34 +487,54 @@ end

-- Ordering

local function with_pin_order(order_func)
return function(a, b)
local a_pinned = is_pinned(a)
local b_pinned = is_pinned(b)
if a_pinned and not b_pinned then
return true
elseif b_pinned and not a_pinned then
return false
else
return order_func(a, b)
end
end
end

local function is_relative_path(path)
return fnamemodify(path, ':p') ~= path
end

local function order_by_directory()
table.sort(m.buffers, function(a, b)
local na = bufname(a)
local nb = bufname(b)
local ra = is_relative_path(na)
local rb = is_relative_path(nb)
if ra and not rb then
return true
end
if not ra and rb then
return false
end
return na < nb
end)
vim.fn['bufferline#update']()
table.sort(
m.buffers,
with_pin_order(function(a, b)
local na = bufname(a)
local nb = bufname(b)
local ra = is_relative_path(na)
local rb = is_relative_path(nb)
if ra and not rb then
return true
end
if not ra and rb then
return false
end
return na < nb
end)
)
vim.fn["bufferline#update"]()
end

local function order_by_language()
table.sort(m.buffers, function(a, b)
local na = fnamemodify(bufname(a), ':e')
local nb = fnamemodify(bufname(b), ':e')
return na < nb
end)
vim.fn['bufferline#update']()
table.sort(
m.buffers,
with_pin_order(function(a, b)
local na = fnamemodify(bufname(a), ":e")
local nb = fnamemodify(bufname(b), ":e")
return na < nb
end)
)
vim.fn["bufferline#update"]()
end


Expand Down Expand Up @@ -556,11 +606,13 @@ m.close_all_but_current = close_all_but_current
m.close_buffers_right = close_buffers_right
m.close_buffers_left = close_buffers_left

m.is_pinned = is_pinned
m.move_current_buffer_to = move_current_buffer_to
m.move_current_buffer = move_current_buffer
m.goto_buffer = goto_buffer
m.goto_buffer_relative = goto_buffer_relative

m.toggle_pin = toggle_pin
m.order_by_directory = order_by_directory
m.order_by_language = order_by_language

Expand Down
2 changes: 2 additions & 0 deletions plugin/bufferline.vim
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ command! -count -bang BufferMovePrevious call s:move_current_buffer(-v:cou
command! -nargs=1 -bang BufferMove call s:move_current_buffer_to(<f-args>)

command! -bang BufferPick call bufferline#pick_buffer()
command! BufferPin lua require'bufferline.state'.toggle_pin()

command! -bang BufferOrderByDirectory call bufferline#order_by_directory()
command! -bang BufferOrderByLanguage call bufferline#order_by_language()
Expand Down Expand Up @@ -96,6 +97,7 @@ let s:DEFAULT_OPTIONS = {
\ 'exclude_name': v:null,
\ 'icon_close_tab': '',
\ 'icon_close_tab_modified': '',
\ 'icon_pinned': '',
\ 'icon_separator_active': '',
\ 'icon_separator_inactive': '',
\ 'icons': v:true,
Expand Down

0 comments on commit f677f1d

Please sign in to comment.