diff --git a/README.md b/README.md index b8b593f..38c50fd 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,42 @@ -# vim-buffet - -**Note: this plugin has been renamed from `vim-workspace` and thus has also +

+
+ Markdownify +
+ vim-buffet +
+

+ +
+ Brings you the IDE-like tabs into Vim, for easy navigation, and a nice, +customizable look. +
+ +
 
+ +

+ + + + +

+ +

+ Installation • + Usage • + Recommendations • + FAQ • + Credits • + License +

+ +**Note:** This plugin has been renamed from `vim-workspace` and thus has also different prefix for the configuration and commands. Please revisit the README and use the new names of the configuration parameters, highlight groups and -commands. Sorry for inconvenience.** +commands. Sorry for inconvenience. -If you are new to the Vim world, then welcome, and start by learning Vim's -notions of [buffers, windows](http://vimdoc.sourceforge.net/htmldoc/windows.html) -and [tabpages](http://vimdoc.sourceforge.net/htmldoc/tabpage.html). -But if you are an experienced Vim user, you might have got tired of `bn/bp/ls/Ctrl-^`. - -`vim-buffet` brings the IDE-like tabs into Vim, for easy navigation, and a nice, -customizable look. It takes your buffers and tabs, and shows them combined in the -tabline. With this you always have your list of buffers visible, at the same -time not losing visibility into tabs. Moreover, `vim-buffet` provides handy -commands to boost navigation as well as a list of options to customize how the -tabline appears. ## Introduction -

+Vim-buffet takes your buffers and tabs, and shows them combined in the +tabline. With this you always have your list of buffers visible, at the same +time not losing visibility into tabs. Moreover, `vim-buffet` provides handy +commands to boost navigation as well as a list of options to customize how the +tabline appears. + +If you are new to the Vim world, then welcome, and start by learning Vim's +notions of [buffers, windows](http://vimdoc.sourceforge.net/htmldoc/windows.html) +and [tabpages](http://vimdoc.sourceforge.net/htmldoc/tabpage.html). + +But if you are an experienced Vim user, you might have got tired of `bn/bp/ls/Ctrl-^`. + Take a look at the screenshot. The blue cuties are the tabpages. The tabpage that has the buffers list coming next, is the current tabpage. The gray items with names are the hidden/inactive buffers, and obviously, the green one is the @@ -47,6 +76,8 @@ below.* /> + + ## Installation Use your favourite plugin manager to install `vim-buffet`. If you do not have any @@ -59,7 +90,10 @@ Plug 'bagrat/vim-buffet' After installation, `vim-buffet` is enabled by default, so whenever you restart Vim, you will see the new tabline! -## Commands + + +## Usage +### Commands Apart from listing the buffers in the tabline, `vim-buffet` also provides some handy commands to manipulate the buffers: @@ -71,7 +105,7 @@ handy commands to manipulate the buffers: in the list that has unsaved changes, those will not be wiped. To ignore any changes and forcibly wipe all buffers except the current one, use `Bonly!`. -## Mappings +### Mappings Mappings for switching buffers are also provided. You just need to add the following mappings to your Vimrc file: @@ -92,134 +126,44 @@ nmap 0 BuffetSwitch(10) This will allow you to switch between buffers 1 - 10. You can get more `` mappings, or disable it completely, by configuring the `g:buffet_max_plug` option. -## Configuration +### Configuration There are some configuration options that make it possible to customize how the tabline works and looks like. -### Options - -The following is the list of available options, that should be set in your -Vimrc file, using `let = `: - -* `g:buffet_always_show_tabline` - if set to `0`, the tabline will only be shown if - there is more than one buffer or tab open. - - Default: - ``` - let g:buffet_always_show_tabline = 1 - ``` - -* `g:buffet_powerline_separators` - if set to `1`, use powerline separators - in between buffers and tabs in the tabline (see the first screenshot). - - Default: - ``` - let g:buffet_powerline_separators = 0 - ``` - -* `g:buffet_separator` - the character to be used for separating items in the tabline. - - Default: - ``` - let g:buffet_separator = "" - ``` - -* `g:buffet_show_index` - if set to `1`, show index before each buffer name. Index is - useful for switching between buffers quickly. - - Default: - ``` - let g:buffet_show_index = 0 - ``` - -* `g:buffet_max_plug` - the maximum number of `BuffetSwitch` provided. Mapping - will be disabled if the option is set to `0`. - - Default: - ``` - let g:buffet_max_plug = 10 - ``` - -* `g:buffet_use_devicons` - if set to `1` and - [`vim-devicons`](https://github.com/ryanoasis/vim-devicons) plugin is - installed, show file type icons for each buffer in the tabline. If the - `vim-devicons` plugin is not present, the option will automatically default to - `0`. - - Default: - ``` - let g:buffet_use_devicons = 1 - ``` - - *Note: you need to have `vim-devicons` loaded before `vim-buffet` in order to - make this work.* - -* `g:buffet_tab_icon` - the character to be used as an icon for the tab items - in the tabline. - - Default: - ``` - let g:buffet_tab_icon = "#" - ``` - -* `g:buffet_new_buffer_name` - the character to be shown as the name of a new - buffer. - - Default: - ``` - let g:buffet_new_buffer_name = "*" - ``` - -* `g:buffet_modified_icon` - the character to be shown by the name of - a modified buffer. - Default: - - ``` - let g:buffet_modified_icon = "+" - ``` - -* `g:buffet_left_trun_icon` - the character to be shown by the count of - truncated buffers on the left. - - Default: - ``` - let g:buffet_left_trunc_icon = "<" - ``` - -* `g:buffet_right_trun_icon` - the character to be shown by the count of - truncated buffers on the right. - - Default: - ``` - let g:buffet_right_trunc_icon = ">" - ``` - -* `g:buffet_hidden_buffers` - the types of buffers to hide from the tabline - - Default: - ``` - let g:buffet_hidden_buffers = ["terminal", "quickfix"] - ``` - Note: this has the side effect of making all matching buffers unlisted - -### Colors +#### Options + +| Options | Default | Descriptions | +| --- | --- | --- | +| `g:buffet_always_show_tabline` | 1 | Set to `0`, the tabline will only be shown if there is more than one buffer or tab open | +| `g:buffet_powerline_separators` | 0 | Set to `1`, use powerline separators in between buffers and tabs in the tabline (see the first screenshot) | +| `g:buffet_separator` | '' | The character to be used for separating items in the tabline | +| `g:buffet_show_index` | 0 | Set to `1`, show index before each buffer name. Index is useful for switching between buffers quickly | +| `g:buffet_max_plug` | 10 | The maximum number of `BuffetSwitch` provided. Mapping will be disabled if the option is set to `0` | +| `g:buffet_use_devicons` | 1 | If set to `1` and [`vim-devicons`](https://github.com/ryanoasis/vim-devicons) plugin is installed, show file type icons for each buffer in the tabline. If the `vim-devicons` plugin is not present, the option will automatically default to `0` (*Note: you need to have `vim-devicons` loaded before `vim-buffet` in order to make this work*) | +| `g:buffet_tab_icon` | '#' | The character to be used as an icon for the tab items in the tabline | +| `g:buffet_new_buffer_name` | "\*" | The character to be shown as the name of a new buffer | +| `g:buffet_modified_icon` | "+" | The character to be shown by the name of a modified buffer | +| `g:buffet_left_trun_icon` | "<" | - the character to be shown by the count of truncated buffers on the left | +| `g:buffet_right_trun_icon` | ">" | the character to be shown by the count of truncated buffers on the right | +| `g:buffet_hidden_buffers` | ["terminal", "quickfix"] | the types of buffers to hide from the tabline (*Note: This has the side effect of making all matching buffers unlisted)* | + +#### Colors Of course, you can customize the colors of your tabline, to make it awesome and yours. The following is the list of highlight groups, with self-explanatory names: -* `BuffetCurrentBuffer` - the current buffer. -* `BuffetActiveBuffer` - an active buffer (a non-current buffer visible in - a non-current window). -* `BuffetBuffer` - a non-current and non-active buffer. -* `BuffetModCurrentBuffer` - the current buffer when modified. -* `BuffetModActiveBuffer` - a modified active buffer (a non-current buffer visible in - a non-current window). -* `BuffetModBuffer` - a modified non-current and non-active buffer. -* `BuffetTrunc` - the truncation indicator (count of truncated buffers - from the left or right). -* `BuffetTab` - a tab. +| Highlights | Descriptions | +| --- | --- | +| `BuffetCurrentBuffer` | The current buffer. +| `BuffetActiveBuffer` | An active buffer (a non-current buffer visible in a non-current window) | +| `BuffetBuffer` | A non-current and non-active buffer. +| `BuffetModCurrentBuffer` | The current buffer when modified. +| `BuffetModActiveBuffer` | A modified active buffer (a non-current buffer visible in a non-current window). | +| `BuffetModBuffer` | A modified non-current and non-active buffer. +| `BuffetTrunc` | The truncation indicator (count of truncated buffers from the left or right) | +| `BuffetTab` | A tab | **Note:** To get your custom colors set, define a function with name `g:BuffetSetCustomColors` and place your highlight group definitions inside @@ -233,7 +177,9 @@ function! g:BuffetSetCustomColors() endfunction ``` -## Recommendations and FAQ + + +## Recommendations Here are some recommended mappings to boost your navigation experience: @@ -245,10 +191,11 @@ noremap :Bw! noremap :tabnew split ``` -### FAQ -#### **How do I get the look like in the screenshot?** +## FAQ +
How do I get the look like in the screenshot? +

First you will need a patched font, extended with `powerline` and `font-awesome` symbols. Also, you will need the [`vim-devicons`](https://github.com/ryanoasis/vim-devicons) installed, which @@ -265,17 +212,22 @@ let g:buffet_right_trunc_icon = "\uf0a9" *Note: you need to have `vim-devicons` loaded before `vim-buffet` in order to make this work.* +

+
-#### **How to have the current buffer open in a new tab instead of a new one?** - +
How to have the current buffer open in a new tab instead of a new one? +

Just add this mapping to your Vimrc: ``` map :tab split ``` +

+
-#### **I can only see the current active buffer in the tabline** +
I can only see the current active buffer in the tabline +

The reason is that you probably use some statusline plugin (e.g. lightline, airline) that also has tabline support, which overrides vim-buffet. All you need to do is disable the tabline of the statusline plugin. For Lightline, it should @@ -284,8 +236,16 @@ be something like this: ``` let g:lightline.enable.tabline = 0 ``` +

+
+ + + +## Credits +
The icon in the header is made by Freepik from www.flaticon.com
+ + ## License -See -[LICENSE](https://github.com/bagrat/vim-buffet/blob/master/LICENS://github.com/bagrat/vim-buffet/blob/master/LICENSE). +See [LICENSE](https://github.com/bagrat/vim-buffet/blob/master/LICENS://github.com/bagrat/vim-buffet/blob/master/LICENSE). diff --git a/autoload/buffet.vim b/autoload/buffet.vim index 7203b66..94b425f 100644 --- a/autoload/buffet.vim +++ b/autoload/buffet.vim @@ -16,417 +16,414 @@ let s:largest_buffer_id = 1 let s:path_separator = fnamemodify(getcwd(),':p')[-1:] function! buffet#update() - let largest_buffer_id = max([bufnr('$'), s:largest_buffer_id]) + let largest_buffer_id = max([bufnr('$'), s:largest_buffer_id]) - for buffer_id in range(1, largest_buffer_id) - " Check if we already keep track of this buffer - let is_present = 0 - if has_key(s:buffers, buffer_id) - let is_present = 1 + for buffer_id in range(1, largest_buffer_id) + " Check if we already keep track of this buffer + let is_tracked = has_key(s:buffers, buffer_id) ? 1 : 0 + + " Skip if a buffer with this id does not exist + if !buflisted(buffer_id) + if is_tracked + if buffer_id == s:last_current_buffer_id + let s:last_current_buffer_id = -1 endif - " Skip if a buffer with this id does not exist - if !buflisted(buffer_id) - if is_present - if buffer_id == s:last_current_buffer_id - let s:last_current_buffer_id = -1 - endif + " forget about this buffer + call remove(s:buffers, buffer_id) + call remove(s:buffer_ids, index(s:buffer_ids, buffer_id)) + let s:largest_buffer_id = max(s:buffer_ids) + endif - " forget about this buffer - call remove(s:buffers, buffer_id) - call remove(s:buffer_ids, index(s:buffer_ids, buffer_id)) - let s:largest_buffer_id = max(s:buffer_ids) - endif + continue + endif - continue - endif + " If this buffer is already tracked and listed, we're good. + " In case if it is the only buffer, still update, because an empty new + " buffer id is being replaced by a buffer for an existing file. + if is_tracked && len(s:buffers) > 1 + continue + endif - " If this buffer is already tracked and listed, we're good. - " In case if it is the only buffer, still update, because an empty new - " buffer id is being replaced by a buffer for an existing file. - if is_present && len(s:buffers) > 1 - continue - endif + " hide terminal and quickfix buffers + let buffer_type = getbufvar(buffer_id, "&buftype", "") + if index(g:buffet_hidden_buffers, buffer_type) >= 0 + call setbufvar(buffer_id, "&buflisted", 0) + continue + endif - " hide terminal and quickfix buffers - let buffer_type = getbufvar(buffer_id, "&buftype", "") - if index(g:buffet_hidden_buffers, buffer_type) >= 0 - call setbufvar(buffer_id, "&buflisted", 0) - continue - endif + let buffer_name = bufname(buffer_id) + let buffer_head = fnamemodify(buffer_name, ':p:h') + let buffer_tail = fnamemodify(buffer_name, ':t') - let buffer_name = bufname(buffer_id) - let buffer_head = fnamemodify(buffer_name, ':p:h') - let buffer_tail = fnamemodify(buffer_name, ':t') + " Initialize the buffer object + let buffer = {} + let buffer.head = split(buffer_head, s:path_separator) + let buffer.not_new = len(buffer_tail) + let buffer.tail = buffer.not_new ? buffer_tail : g:buffet_new_buffer_name - " Initialize the buffer object - let buffer = {} - let buffer.head = split(buffer_head, s:path_separator) - let buffer.not_new = len(buffer_tail) - let buffer.tail = buffer.not_new ? buffer_tail : g:buffet_new_buffer_name + " Update the buffers map + let s:buffers[buffer_id] = buffer - " Update the buffers map - let s:buffers[buffer_id] = buffer + if !is_tracked + " Update the buffer IDs list + call add(s:buffer_ids, buffer_id) + let s:largest_buffer_id = max([s:largest_buffer_id, buffer_id]) + endif + endfor - if !is_present - " Update the buffer IDs list - call add(s:buffer_ids, buffer_id) - let s:largest_buffer_id = max([s:largest_buffer_id, buffer_id]) - endif - endfor + let buffer_name_count = {} + " Set initial buffer name, and record occurrences + for buffer in values(s:buffers) + let buffer.index = -1 + let buffer.name = buffer.tail + let buffer.length = len(buffer.name) + + if buffer.not_new + let current_count = get(buffer_name_count, buffer.name, 0) + let buffer_name_count[buffer.name] = current_count + 1 + endif + endfor + + " Disambiguate buffer names with multiple occurrences + while len(filter(buffer_name_count, 'v:val > 1')) + let ambiguous = buffer_name_count let buffer_name_count = {} - " Set initial buffer name, and record occurrences for buffer in values(s:buffers) - let buffer.index = -1 - let buffer.name = buffer.tail + if has_key(ambiguous, buffer.name) + let buffer_path = buffer.head[buffer.index:] + call add(buffer_path, buffer.tail) + + let buffer.index -= 1 + let buffer.name = join(buffer_path, s:path_separator) let buffer.length = len(buffer.name) + endif - if buffer.not_new - let current_count = get(buffer_name_count, buffer.name, 0) - let buffer_name_count[buffer.name] = current_count + 1 - endif + if buffer.not_new + let current_count = get(buffer_name_count, buffer.name, 0) + let buffer_name_count[buffer.name] = current_count + 1 + endif endfor - - " Disambiguate buffer names with multiple occurrences - while len(filter(buffer_name_count, 'v:val > 1')) - let ambiguous = buffer_name_count - let buffer_name_count = {} - - for buffer in values(s:buffers) - if has_key(ambiguous, buffer.name) - let buffer_path = buffer.head[buffer.index:] - call add(buffer_path, buffer.tail) - - let buffer.index -= 1 - let buffer.name = join(buffer_path, s:path_separator) - let buffer.length = len(buffer.name) - endif - - if buffer.not_new - let current_count = get(buffer_name_count, buffer.name, 0) - let buffer_name_count[buffer.name] = current_count + 1 - endif - endfor - endwhile - - let current_buffer_id = bufnr('%') - if has_key(s:buffers, current_buffer_id) - let s:last_current_buffer_id = current_buffer_id - elseif s:last_current_buffer_id == -1 && len(s:buffer_ids) > 0 - let s:last_current_buffer_id = s:buffer_ids[0] - endif - - " Hide tabline if only one buffer and tab open - if !g:buffet_always_show_tabline && len(s:buffer_ids) == 1 && tabpagenr("$") == 1 - set showtabline=0 - endif + endwhile + + let current_buffer_id = bufnr('%') + if has_key(s:buffers, current_buffer_id) + let s:last_current_buffer_id = current_buffer_id + elseif s:last_current_buffer_id == -1 && len(s:buffer_ids) > 0 + let s:last_current_buffer_id = s:buffer_ids[0] + endif + + " Hide tabline if only one buffer and tab open + if !g:buffet_always_show_tabline && len(s:buffer_ids) == 1 && tabpagenr("$") == 1 + set showtabline=0 + endif endfunction function! s:GetVisibleRange(length_limit, buffer_padding) - let current_buffer_id = s:last_current_buffer_id + let current_buffer_id = s:last_current_buffer_id - if current_buffer_id == -1 - return [-1, -1] - endif + if current_buffer_id == -1 + return [-1, -1] + endif - let current_buffer_id_i = index(s:buffer_ids, current_buffer_id) + let current_buffer_id_i = index(s:buffer_ids, current_buffer_id) - let current_buffer = s:buffers[current_buffer_id] - let capacity = a:length_limit - current_buffer.length - a:buffer_padding - let left_i = current_buffer_id_i - let right_i = current_buffer_id_i + let current_buffer = s:buffers[current_buffer_id] + let capacity = a:length_limit - current_buffer.length - a:buffer_padding + let left_i = current_buffer_id_i + let right_i = current_buffer_id_i - for left_i in range(current_buffer_id_i - 1, 0, -1) - let buffer = s:buffers[s:buffer_ids[left_i]] - if (buffer.length + a:buffer_padding) <= capacity - let capacity = capacity - buffer.length - a:buffer_padding - else - let left_i = left_i + 1 - break - endif - endfor + for left_i in range(current_buffer_id_i - 1, 0, -1) + let buffer = s:buffers[s:buffer_ids[left_i]] + if (buffer.length + a:buffer_padding) <= capacity + let capacity = capacity - buffer.length - a:buffer_padding + else + let left_i = left_i + 1 + break + endif + endfor - for right_i in range(current_buffer_id_i + 1, len(s:buffers) - 1) - let buffer = s:buffers[s:buffer_ids[right_i]] - if (buffer.length + a:buffer_padding) <= capacity - let capacity = capacity - buffer.length - a:buffer_padding - else - let right_i = right_i - 1 - break - endif - endfor + for right_i in range(current_buffer_id_i + 1, len(s:buffers) - 1) + let buffer = s:buffers[s:buffer_ids[right_i]] + if (buffer.length + a:buffer_padding) <= capacity + let capacity = capacity - buffer.length - a:buffer_padding + else + let right_i = right_i - 1 + break + endif + endfor - return [left_i, right_i] + return [left_i, right_i] endfunction function! s:GetBufferElements(capacity, buffer_padding) - let [left_i, right_i] = s:GetVisibleRange(a:capacity, a:buffer_padding) - " TODO: evaluate if calling this ^ twice will get better visuals + let [left_i, right_i] = s:GetVisibleRange(a:capacity, a:buffer_padding) + " TODO: evaluate if calling this ^ twice will get better visuals - if left_i < 0 || right_i < 0 - return [] - endif + if left_i < 0 || right_i < 0 + return [] + endif - let buffer_elems = [] + let buffer_elems = [] - let trunced_left = left_i - if trunced_left - let left_trunc_elem = {} - let left_trunc_elem.type = "LeftTrunc" - let left_trunc_elem.value = g:buffet_left_trunc_icon . " " . trunced_left - call add(buffer_elems, left_trunc_elem) - endif + let trunced_left = left_i + if trunced_left + let left_trunc_elem = {} + let left_trunc_elem.type = "LeftTrunc" + let left_trunc_elem.value = g:buffet_left_trunc_icon . " " . trunced_left + call add(buffer_elems, left_trunc_elem) + endif - for i in range(left_i, right_i) - let buffer_id = s:buffer_ids[i] - let buffer = s:buffers[buffer_id] + for i in range(left_i, right_i) + let buffer_id = s:buffer_ids[i] + let buffer = s:buffers[buffer_id] - if buffer_id == winbufnr(0) - let type_prefix = "Current" - elseif bufwinnr(buffer_id) > 0 - let type_prefix = "Active" - else - let type_prefix = "" - endif + if buffer_id == winbufnr(0) + let type_prefix = "Current" + elseif bufwinnr(buffer_id) > 0 + let type_prefix = "Active" + else + let type_prefix = "" + endif - let elem = {} - let elem.index = i + 1 - let elem.value = buffer.name - let elem.buffer_id = buffer_id - let elem.is_modified = getbufvar(buffer_id, '&mod') + let elem = {} + let elem.index = i + 1 + let elem.value = buffer.name + let elem.buffer_id = buffer_id + let elem.is_modified = getbufvar(buffer_id, '&mod') - if elem.is_modified - let type_prefix = "Mod" . type_prefix - endif + if elem.is_modified + let type_prefix = "Mod" . type_prefix + endif - let elem.type = type_prefix . "Buffer" + let elem.type = type_prefix . "Buffer" - call add(buffer_elems, elem) - endfor + call add(buffer_elems, elem) + endfor - let trunced_right = (len(s:buffers) - right_i - 1) - if trunced_right > 0 - let right_trunc_elem = {} - let right_trunc_elem.type = "RightTrunc" - let right_trunc_elem.value = trunced_right . " " . g:buffet_right_trunc_icon - call add(buffer_elems, right_trunc_elem) - endif + let trunced_right = (len(s:buffers) - right_i - 1) + if trunced_right > 0 + let right_trunc_elem = {} + let right_trunc_elem.type = "RightTrunc" + let right_trunc_elem.value = trunced_right . " " . g:buffet_right_trunc_icon + call add(buffer_elems, right_trunc_elem) + endif - return buffer_elems + return buffer_elems endfunction function! s:GetAllElements(capacity, buffer_padding) - let last_tab_id = tabpagenr('$') - let current_tab_id = tabpagenr() - let buffer_elems = s:GetBufferElements(a:capacity, a:buffer_padding) - let tab_elems = [] - - for tab_id in range(1, last_tab_id) - let elem = {} - let elem.value = tab_id - let elem.type = "Tab" - call add(tab_elems, elem) - - if tab_id == current_tab_id - let tab_elems = tab_elems + buffer_elems - endif - endfor + let last_tab_id = tabpagenr('$') + let current_tab_id = tabpagenr() + let buffer_elems = s:GetBufferElements(a:capacity, a:buffer_padding) + let tab_elems = [] + + for tab_id in range(1, last_tab_id) + let elem = {} + let elem.value = tab_id + let elem.type = "Tab" + call add(tab_elems, elem) + + if tab_id == current_tab_id + let tab_elems = tab_elems + buffer_elems + endif + endfor - let end_elem = {"type": "End", "value": ""} - call add(tab_elems, end_elem) + let end_elem = {"type": "End", "value": ""} + call add(tab_elems, end_elem) - return tab_elems + return tab_elems endfunction function! s:IsBufferElement(element) - if index(g:buffet_buffer_types, a:element.type) >= 0 - return 1 - endif + if index(g:buffet_buffer_types, a:element.type) >= 0 + return 1 + endif - return 0 + return 0 endfunction function! s:Len(string) - let visible_singles = substitute(a:string, '[^\d0-\d127]', "-", "g") + let visible_singles = substitute(a:string, '[^\d0-\d127]', "-", "g") - return len(visible_singles) + return len(visible_singles) endfunction function! s:GetTypeHighlight(type) - return "%#" . g:buffet_prefix . a:type . "#" + return "%#" . g:buffet_prefix . a:type . "#" endfunction function! s:Render() - let sep_len = s:Len(g:buffet_separator) - - let tabs_count = tabpagenr("$") - let tabs_len = (1 + s:Len(g:buffet_tab_icon) + 1 + sep_len) * tabs_count + let sep_len = s:Len(g:buffet_separator) - let left_trunc_len = 1 + s:Len(g:buffet_left_trunc_icon) + 1 + 2 + 1 + sep_len - let right_trunc_len = 1 + 2 + 1 + s:Len(g:buffet_right_trunc_icon) + 1 + sep_len - let trunc_len = left_trunc_len + right_trunc_len + let tabs_count = tabpagenr("$") + let tabs_len = (1 + s:Len(g:buffet_tab_icon) + 1 + sep_len) * tabs_count - let capacity = &columns - tabs_len - trunc_len - 5 - let buffer_padding = 1 + (g:buffet_use_devicons ? 1+1 : 0) + 1 + sep_len + let left_trunc_len = 1 + s:Len(g:buffet_left_trunc_icon) + 1 + 2 + 1 + sep_len + let right_trunc_len = 1 + 2 + 1 + s:Len(g:buffet_right_trunc_icon) + 1 + sep_len + let trunc_len = left_trunc_len + right_trunc_len - let elements = s:GetAllElements(capacity, buffer_padding) + let capacity = &columns - tabs_len - trunc_len - 5 + let buffer_padding = 1 + (g:buffet_use_devicons ? 1+1 : 0) + 1 + sep_len - let render = "" - for i in range(0, len(elements) - 2) - let left = elements[i] - let elem = left - let right = elements[i + 1] + let elements = s:GetAllElements(capacity, buffer_padding) - if elem.type == "Tab" - let render = render . "%" . elem.value . "T" - elseif s:IsBufferElement(elem) && has("nvim") - let render = render . "%" . elem.buffer_id . "@SwitchToBuffer@" - endif + let render = "" + for i in range(0, len(elements) - 2) + let left = elements[i] + let elem = left + let right = elements[i + 1] - let highlight = s:GetTypeHighlight(elem.type) - let render = render . highlight + if elem.type == "Tab" + let render = render . "%" . elem.value . "T" + elseif s:IsBufferElement(elem) && has("nvim") + let render = render . "%" . elem.buffer_id . "@SwitchToBuffer@" + endif - if g:buffet_show_index && s:IsBufferElement(elem) - let render = render . " " . elem.index - endif + let highlight = s:GetTypeHighlight(elem.type) + let render = render . highlight - let icon = "" - if g:buffet_use_devicons && s:IsBufferElement(elem) - let icon = " " . WebDevIconsGetFileTypeSymbol(elem.value) - elseif elem.type == "Tab" - let icon = " " . g:buffet_tab_icon - endif + if g:buffet_show_index && s:IsBufferElement(elem) + let render = render . " " . elem.index + endif - let render = render . icon + let icon = "" + if g:buffet_use_devicons && s:IsBufferElement(elem) + let icon = " " . WebDevIconsGetFileTypeSymbol(elem.value) + elseif elem.type == "Tab" + let icon = " " . g:buffet_tab_icon + endif - if elem.type != "Tab" - let render = render . " " . elem.value - endif + let render = render . icon - if s:IsBufferElement(elem) - if elem.is_modified && g:buffet_modified_icon != "" - let render = render . g:buffet_modified_icon - endif - endif + if elem.type != "Tab" + let render = render . " " . elem.value + endif - let render = render . " " + if s:IsBufferElement(elem) + if elem.is_modified && g:buffet_modified_icon != "" + let render = render . g:buffet_modified_icon + endif + endif - let separator = g:buffet_has_separator[left.type][right.type] - let separator_hi = s:GetTypeHighlight(left.type . right.type) - let render = render . separator_hi . separator + let render = render . " " - if elem.type == "Tab" && has("nvim") - let render = render . "%T" - elseif s:IsBufferElement(elem) && has("nvim") - let render = render . "%T" - endif - endfor + let separator = g:buffet_has_separator[left.type][right.type] + let separator_hi = s:GetTypeHighlight(left.type . right.type) + let render = render . separator_hi . separator - if !has("nvim") - let render = render . "%T" + if elem.type == "Tab" && has("nvim") + let render = render . "%T" + elseif s:IsBufferElement(elem) && has("nvim") + let render = render . "%T" endif + endfor - let render = render . s:GetTypeHighlight("Buffer") + if !has("nvim") + let render = render . "%T" + endif - return render + let render = render . s:GetTypeHighlight("Buffer") + + return render endfunction function! buffet#render() - call buffet#update() - return s:Render() + call buffet#update() + return s:Render() endfunction function! s:GetBuffer(buffer) - if empty(a:buffer) && s:last_current_buffer_id >= 0 - let btarget = s:last_current_buffer_id - elseif a:buffer =~ '^\d\+$' - let btarget = bufnr(str2nr(a:buffer)) - else - let btarget = bufnr(a:buffer) - endif - - return btarget + if empty(a:buffer) && s:last_current_buffer_id >= 0 + let btarget = s:last_current_buffer_id + elseif a:buffer =~ '^\d\+$' + let btarget = bufnr(str2nr(a:buffer)) + else + let btarget = bufnr(a:buffer) + endif + + return btarget endfunction function! buffet#bswitch(index) - let i = str2nr(a:index) - 1 - if i < 0 || i > len(s:buffer_ids) - 1 - echohl ErrorMsg - echom "Invalid buffer index" - echohl None - return - endif - let buffer_id = s:buffer_ids[i] - execute 'silent buffer ' . buffer_id + let i = str2nr(a:index) - 1 + if i < 0 || i > len(s:buffer_ids) - 1 + echohl ErrorMsg + echom "Invalid buffer index" + echohl None + return + endif + let buffer_id = s:buffer_ids[i] + execute 'silent buffer ' . buffer_id endfunction " inspired and based on https://vim.fandom.com/wiki/Deleting_a_buffer_without_closing_the_window function! buffet#bwipe(bang, buffer) - let btarget = s:GetBuffer(a:buffer) - - let filters = get(g:, "buffet_bwipe_filters", []) - if type(filters) == type([]) - for f in filters - if function(f)(a:bang, btarget) > 0 - return - endif - endfor - endif - - if btarget < 0 - echohl ErrorMsg - call 'No matching buffer for ' . a:buffer - echohl None + let btarget = s:GetBuffer(a:buffer) + let filters = get(g:, "buffet_bwipe_filters", []) + if type(filters) == type([]) + for f in filters + if function(f)(a:bang, btarget) > 0 return + endif + endfor + endif + + if btarget < 0 + echohl ErrorMsg + call 'No matching buffer for ' . a:buffer + echohl None + + return + endif + + if empty(a:bang) && getbufvar(btarget, '&modified') + echohl ErrorMsg + echom 'No write since last change for buffer ' . btarget . " (add ! to override)" + echohl None + return + endif + + " IDs of windows that view target buffer which we will delete. + let wnums = filter(range(1, winnr('$')), 'winbufnr(v:val) == btarget') + + let wcurrent = winnr() + for w in wnums + " switch to window with ID 'w' + execute 'silent ' . w . 'wincmd w' + + let prevbuf = bufnr('#') + " if the previous buffer is another listed buffer, switch to it... + if prevbuf > 0 && buflisted(prevbuf) && prevbuf != btarget + buffer # + " ...otherwise just go to the previous buffer in the list. + else + bprevious endif - if empty(a:bang) && getbufvar(btarget, '&modified') - echohl ErrorMsg - echom 'No write since last change for buffer ' . btarget . " (add ! to override)" - echohl None - return + " if the 'bprevious' did not work, then just open a new buffer + if btarget == bufnr("%") + execute 'silent enew' . a:bang endif + endfor - " IDs of windows that view target buffer which we will delete. - let wnums = filter(range(1, winnr('$')), 'winbufnr(v:val) == btarget') - - let wcurrent = winnr() - for w in wnums - " switch to window with ID 'w' - execute 'silent ' . w . 'wincmd w' - - let prevbuf = bufnr('#') - " if the previous buffer is another listed buffer, switch to it... - if prevbuf > 0 && buflisted(prevbuf) && prevbuf != btarget - buffer # - " ...otherwise just go to the previous buffer in the list. - else - bprevious - endif - - " if the 'bprevious' did not work, then just open a new buffer - if btarget == bufnr("%") - execute 'silent enew' . a:bang - endif - endfor - - " finally wipe the tarbet buffer - execute 'silent bwipe' . a:bang . " " . btarget - " switch back to original window - execute 'silent ' . wcurrent . 'wincmd w' + " finally wipe the tarbet buffer + execute 'silent bwipe' . a:bang . " " . btarget + " switch back to original window + execute 'silent ' . wcurrent . 'wincmd w' endfunction function! buffet#bonly(bang, buffer) - let btarget = s:GetBuffer(a:buffer) + let btarget = s:GetBuffer(a:buffer) - for b in s:buffer_ids - if b == btarget - continue - endif + for b in s:buffer_ids + if b == btarget + continue + endif - call buffet#bwipe(a:bang, b) - endfor + call buffet#bwipe(a:bang, b) + endfor endfunction diff --git a/autoload/buffet/colors.vim b/autoload/buffet/colors.vim new file mode 100644 index 0000000..fe1dc36 --- /dev/null +++ b/autoload/buffet/colors.vim @@ -0,0 +1,101 @@ +function! buffet#colors#init_color_highlights() + " TODO: try to match user's colorscheme + " Issue: https://github.com/bagrat/vim-buffet/issues/5 + " if get(g:, "buffet_match_color_scheme", 1) + + hi! BuffetCurrentBuffer cterm=NONE ctermbg=2 ctermfg=8 guibg=#00FF00 guifg=#000000 + hi! BuffetActiveBuffer cterm=NONE ctermbg=10 ctermfg=2 guibg=#999999 guifg=#00FF00 + hi! BuffetBuffer cterm=NONE ctermbg=10 ctermfg=8 guibg=#999999 guifg=#000000 + + hi! link BuffetModCurrentBuffer BuffetCurrentBuffer + hi! link BuffetModActiveBuffer BuffetActiveBuffer + hi! link BuffetModBuffer BuffetBuffer + + hi! BuffetTrunc cterm=bold ctermbg=11 ctermfg=8 guibg=#999999 guifg=#000000 + hi! BuffetTab cterm=NONE ctermbg=4 ctermfg=8 guibg=#0000FF guifg=#000000 + + hi! link BuffetLeftTrunc BuffetTrunc + hi! link BuffetRightTrunc BuffetTrunc + hi! link BuffetEnd BuffetBuffer + + if exists("*g:BuffetSetCustomColors") + call g:BuffetSetCustomColors() + endif + + for left in keys(g:buffet_has_separator) + for right in keys(g:buffet_has_separator[left]) + let vim_mode = "cterm" + if has("gui") || has("termguicolors") + let vim_mode = "gui" + endif + + let left_hi = g:buffet_prefix . left + let right_hi = g:buffet_prefix . right + let left_bg = s:GetHiAttr(left_hi, 'bg') + let right_bg = s:GetHiAttr(right_hi, 'bg') + + if left_bg == "" + let left_bg = "NONE" + endif + + if right_bg == "" + let right_bg = "NONE" + endif + + let sep_hi = g:buffet_prefix . left . right + if left_bg != right_bg + let g:buffet_has_separator[left][right] = g:buffet_noseparator + + call s:SetHi(sep_hi, left_bg, right_bg) + else + let g:buffet_has_separator[left][right] = g:buffet_separator + + call s:LinkHi(sep_hi, left_hi) + endif + endfor + endfor +endfunction + +function! s:GetHiAttr(name, attr) + let vim_mode = "cterm" + let attr_suffix = "" + if has("gui") || has('termguicolors') + let vim_mode = "gui" + let attr_suffix = "#" + endif + + let value = synIDattr(synIDtrans(hlID(a:name)), a:attr . attr_suffix, vim_mode) + + return value +endfunction + +function! s:SetHi(name, fg, bg) + let vim_mode = "cterm" + if has("gui") || has("termguicolors") + let vim_mode = "gui" + endif + + let spec = "" + if a:fg != "" + let fg_spec = vim_mode . "fg=" . a:fg + let spec = fg_spec + endif + + if a:bg != "" + let bg_spec = vim_mode . "bg=" . a:bg + + if spec != "" + let bg_spec = " " . bg_spec + endif + + let spec = spec . bg_spec + endif + + if spec != "" + exec "silent hi! " . a:name . " " . spec + endif +endfunction + +function! s:LinkHi(name, target) + exec "silent hi! link " . a:name . " " . a:target +endfunction diff --git a/plugin/buffet.vim b/plugin/buffet.vim index 60f89e7..683a9c9 100644 --- a/plugin/buffet.vim +++ b/plugin/buffet.vim @@ -1,249 +1,122 @@ +" vim-buffet - IDE-like tabs +" Author: Bagrat Aznauryan +" Url: https://github.com/bagrat/vim-buffet +" License: MIT +" =========================================================================== + if exists("g:buffet_loaded") - finish + finish endif let g:buffet_loaded = 1 let g:buffet_always_show_tabline = get(g:, "buffet_always_show_tabline", 1) - -augroup buffet_show_tabline - autocmd! - autocmd VimEnter,BufAdd,TabEnter * set showtabline=2 -augroup END - -if has("gui") || has("termguicolors") - if !get(g:, "buffet_use_gui_tablne", 0) - set guioptions-=e - endif -endif - -if get(g:, "buffet_powerline_separators", 0) - let g:buffet_powerline_separators = 1 - let g:buffet_noseparator = "\ue0b0" - let g:buffet_separator = "\ue0b1" -else - let g:buffet_powerline_separators = 0 - let g:buffet_noseparator = get(g:, "buffet_noseparator", " ") - let g:buffet_separator = get(g:, "buffet_separator", "|") -endif - let g:buffet_show_index = get(g:, "buffet_show_index", 0) - let g:buffet_max_plug = get(g:, "buffet_max_plug", 10) +let g:buffet_hidden_buffers = get(g:, 'buffet_hidden_buffers', ['terminal', 'quickfix']) if get(g:, "buffet_use_devicons", 1) - if !exists("*WebDevIconsGetFileTypeSymbol") - let g:buffet_use_devicons = 0 - else - let g:buffet_use_devicons = 1 - endif + let g:buffet_use_devicons = exists("*WebDevIconsGetFileTypeSymbol") ? 1 : 0 else - let g:buffet_use_devicons = 0 -endif - -if !exists("g:buffet_modified_icon") - let g:buffet_modified_icon = "+" -endif - -if !exists("g:buffet_left_trunc_icon") - let g:buffet_left_trunc_icon = "<" + let g:buffet_use_devicons = 0 endif -if !exists("g:buffet_right_trunc_icon") - let g:buffet_right_trunc_icon = ">" -endif - -if !exists("g:buffet_new_buffer_name") - let g:buffet_new_buffer_name = "*" +if get(g:, "buffet_powerline_separators", 0) + let g:buffet_powerline_separators = 1 + let g:buffet_noseparator = "\ue0b0" + let g:buffet_separator = "\ue0b1" +else + let g:buffet_powerline_separators = 0 + let g:buffet_noseparator = get(g:, "buffet_noseparator", " ") + let g:buffet_separator = get(g:, "buffet_separator", "|") endif -if !exists("g:buffet_tab_icon") - let g:buffet_tab_icon = "#" -endif +let g:buffet_modified_icon = get(g:, 'buffet_modified_icon', '+') +let g:buffet_left_trunc_icon = get(g:, 'buffet_left_trunc_icon', '<') +let g:buffet_right_trunc_icon = get(g:, 'buffet_right_trunc_icon', '>') +let g:buffet_new_buffer_name = get(g:, 'buffet_new_buffer_name', '*') +let g:buffet_tab_icon = get(g:, 'buffet_tab_icon', '#') -if !exists("g:buffet_hidden_buffers") - let g:buffet_hidden_buffers = ["terminal", "quickfix"] +if has("gui") || has("termguicolors") + if !get(g:, "buffet_use_gui_tablne", 0) + set guioptions-=e + endif endif let g:buffet_prefix = "Buffet" let g:buffet_has_separator = { - \ "Tab": { - \ "Tab": g:buffet_separator, - \ "LeftTrunc": g:buffet_separator, - \ "End" : g:buffet_separator, - \ }, - \ "LeftTrunc": { - \ "Buffer": g:buffet_separator, - \ "CurrentBuffer": g:buffet_separator, - \ "ActiveBuffer": g:buffet_separator, - \ "ModBuffer": g:buffet_separator, - \ }, - \ "RightTrunc": { - \ "Tab": g:buffet_separator, - \ "End": g:buffet_separator, - \ }, - \ } + \ "Tab": { + \ "Tab": g:buffet_separator, + \ "LeftTrunc": g:buffet_separator, + \ "End" : g:buffet_separator, + \ }, + \ "LeftTrunc": { + \ "Buffer": g:buffet_separator, + \ "CurrentBuffer": g:buffet_separator, + \ "ActiveBuffer": g:buffet_separator, + \ "ModBuffer": g:buffet_separator, + \ }, + \ "RightTrunc": { + \ "Tab": g:buffet_separator, + \ "End": g:buffet_separator, + \ }} let g:buffet_buffer_types = [ - \ "Buffer", - \ "ActiveBuffer", - \ "CurrentBuffer", - \ "ModBuffer", - \ "ModActiveBuffer", - \ "ModCurrentBuffer", - \ ] + \ "Buffer", + \ "ActiveBuffer", + \ "CurrentBuffer", + \ "ModBuffer", + \ "ModActiveBuffer", + \ "ModCurrentBuffer"] for s:type in g:buffet_buffer_types - let g:buffet_has_separator["Tab"][s:type] = g:buffet_separator - let g:buffet_has_separator[s:type] = { - \ "RightTrunc": g:buffet_separator, - \ "Tab": g:buffet_separator, - \ "End": g:buffet_separator, - \ } - - for s:t in g:buffet_buffer_types - let g:buffet_has_separator[s:type][s:t] = g:buffet_separator - endfor + let g:buffet_has_separator["Tab"][s:type] = g:buffet_separator + let g:buffet_has_separator[s:type] = { + \ "RightTrunc": g:buffet_separator, + \ "Tab": g:buffet_separator, + \ "End": g:buffet_separator} + + for s:t in g:buffet_buffer_types + let g:buffet_has_separator[s:type][s:t] = g:buffet_separator + endfor endfor -function! s:GetHiAttr(name, attr) - let vim_mode = "cterm" - let attr_suffix = "" - if has("gui") || has('termguicolors') - let vim_mode = "gui" - let attr_suffix = "#" - endif - - let value = synIDattr(synIDtrans(hlID(a:name)), a:attr . attr_suffix, vim_mode) - - return value -endfunction - -function! s:SetHi(name, fg, bg) - let vim_mode = "cterm" - if has("gui") || has("termguicolors") - let vim_mode = "gui" - endif - - let spec = "" - if a:fg != "" - let fg_spec = vim_mode . "fg=" . a:fg - let spec = fg_spec - endif - - if a:bg != "" - let bg_spec = vim_mode . "bg=" . a:bg - - if spec != "" - let bg_spec = " " . bg_spec - endif - - let spec = spec . bg_spec - endif - - if spec != "" - exec "silent hi! " . a:name . " " . spec - endif -endfunction - -function! s:LinkHi(name, target) - exec "silent hi! link " . a:name . " " . a:target -endfunction - -function! s:SetColors() - " TODO: try to match user's colorscheme - " Issue: https://github.com/bagrat/vim-buffet/issues/5 - " if get(g:, "buffet_match_color_scheme", 1) - - hi! BuffetCurrentBuffer cterm=NONE ctermbg=2 ctermfg=8 guibg=#00FF00 guifg=#000000 - hi! BuffetActiveBuffer cterm=NONE ctermbg=10 ctermfg=2 guibg=#999999 guifg=#00FF00 - hi! BuffetBuffer cterm=NONE ctermbg=10 ctermfg=8 guibg=#999999 guifg=#000000 - - hi! link BuffetModCurrentBuffer BuffetCurrentBuffer - hi! link BuffetModActiveBuffer BuffetActiveBuffer - hi! link BuffetModBuffer BuffetBuffer - - hi! BuffetTrunc cterm=bold ctermbg=11 ctermfg=8 guibg=#999999 guifg=#000000 - hi! BuffetTab cterm=NONE ctermbg=4 ctermfg=8 guibg=#0000FF guifg=#000000 - - hi! link BuffetLeftTrunc BuffetTrunc - hi! link BuffetRightTrunc BuffetTrunc - hi! link BuffetEnd BuffetBuffer - - if exists("*g:BuffetSetCustomColors") - call g:BuffetSetCustomColors() - endif - - for left in keys(g:buffet_has_separator) - for right in keys(g:buffet_has_separator[left]) - let vim_mode = "cterm" - if has("gui") || has("termguicolors") - let vim_mode = "gui" - endif - - let left_hi = g:buffet_prefix . left - let right_hi = g:buffet_prefix . right - let left_bg = s:GetHiAttr(left_hi, 'bg') - let right_bg = s:GetHiAttr(right_hi, 'bg') - - if left_bg == "" - let left_bg = "NONE" - endif - - if right_bg == "" - let right_bg = "NONE" - endif - - let sep_hi = g:buffet_prefix . left . right - if left_bg != right_bg - let g:buffet_has_separator[left][right] = g:buffet_noseparator - - call s:SetHi(sep_hi, left_bg, right_bg) - else - let g:buffet_has_separator[left][right] = g:buffet_separator - - call s:LinkHi(sep_hi, left_hi) - endif - endfor - endfor -endfunction - -augroup buffet_set_colors - autocmd! - autocmd ColorScheme * call s:SetColors() -augroup end - " Set solors also at the startup -call s:SetColors() +call buffet#colors#init_color_highlights() if has("nvim") - function! SwitchToBuffer(buffer_id, clicks, btn, flags) - exec "silent buffer " . a:buffer_id - endfunction + function! SwitchToBuffer(buffer_id, clicks, btn, flags) + exec "silent buffer " . a:buffer_id + endfunction endif function! buffet#bwipe_nerdtree_filter(bang, buffer) - let is_in_nt = 0 - if exists("t:NERDTreeBufName") - let ntwinnr = bufwinnr(t:NERDTreeBufName) + let is_in_nt = 0 + if exists("t:NERDTreeBufName") + let ntwinnr = bufwinnr(t:NERDTreeBufName) - if ntwinnr == winnr() - let is_in_nt = 1 - endif + if ntwinnr == winnr() + let is_in_nt = 1 endif + endif - if is_in_nt - return 1 - endif + if is_in_nt + return 1 + endif endfunction let g:buffet_bwipe_filters = ["buffet#bwipe_nerdtree_filter"] for s:n in range(1, g:buffet_max_plug) - execute printf("noremap BuffetSwitch(%d) :call buffet#bswitch(%d)", s:n, s:n) + execute printf("noremap BuffetSwitch(%d) :call buffet#bswitch(%d)", s:n, s:n) endfor command! -bang -complete=buffer -nargs=? Bw call buffet#bwipe(, ) command! -bang -complete=buffer -nargs=? Bonly call buffet#bonly(, ) +augroup buffet + autocmd VimEnter,BufAdd,TabEnter * set showtabline=2 + autocmd ColorScheme * call buffet#colors#init_color_highlights() +augroup end + set tabline=%!buffet#render()