From 7fd8334ce52fdf3cd3ab0fb510175b653e7e0c77 Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Fri, 28 Jan 2022 16:08:30 +0600 Subject: [PATCH 1/8] feat: Show icon and allow modifying case on filetype provider (#209) --- USAGE.md | 11 ++++++ lua/feline/providers/file.lua | 71 ++++++++++++++++++++++++----------- 2 files changed, 60 insertions(+), 22 deletions(-) diff --git a/USAGE.md b/USAGE.md index b9de661..2bbd88b 100644 --- a/USAGE.md +++ b/USAGE.md @@ -657,6 +657,17 @@ The `file_info` provider has some special provider options that can be passed th
Default: `'base-only'` +### File Type + +The file type provider has the following options: + +- `filetype_icon` (boolean): Whether the file type icon is shown alongside the file type. + Default: `false` +- `colored_icon` (boolean): Determines whether file icon should use color inherited from `nvim-web-devicons`.
+ Default: `true` +- `case` (string): The case of the file type string. Possible values are: `'uppercase'`, `'titlecase'` and `'lowercase'`.
+ Default: `'uppercase'` + ### Git The git providers all require [gitsigns.nvim](https://github.com/lewis6991/gitsigns.nvim/), make sure you have it installed when you use those providers, otherwise they'll have no output. diff --git a/lua/feline/providers/file.lua b/lua/feline/providers/file.lua index 1cc9866..1b28c1a 100644 --- a/lua/feline/providers/file.lua +++ b/lua/feline/providers/file.lua @@ -63,7 +63,24 @@ end function M.file_info(component, opts) local filename = api.nvim_buf_get_name(0) + local extension = fn.fnamemodify(filename, ':e') local type = opts.type or 'base-only' + local readonly_str, modified_str, icon + + -- Avoid loading nvim-web-devicons if an icon is provided already + if not component.icon then + local icon_str, icon_color = require('nvim-web-devicons').get_icon_color( + filename, + extension, + { default = true } + ) + + icon = { str = icon_str } + + if opts.colored_icon == nil or opts.colored_icon then + icon.hl = { fg = icon_color } + end + end if type == 'short-path' then filename = fn.pathshorten(filename) @@ -81,26 +98,6 @@ function M.file_info(component, opts) filename = fn.fnamemodify(filename, ':t') end - local extension = fn.fnamemodify(filename, ':e') - local readonly_str, modified_str - - local icon - - -- Avoid loading nvim-web-devicons if an icon is provided already - if not component.icon then - local icon_str, icon_color = require('nvim-web-devicons').get_icon_color( - filename, - extension, - { default = true } - ) - - icon = { str = icon_str } - - if opts.colored_icon == nil or opts.colored_icon then - icon.hl = { fg = icon_color } - end - end - if filename == '' then filename = 'unnamed' end @@ -142,8 +139,38 @@ function M.file_size() return string.format(index == 1 and '%g%s' or '%.2f%s', fsize, suffix[index]) end -function M.file_type() - return bo.filetype:upper() +function M.file_type(component, opts) + local filename = api.nvim_buf_get_name(0) + local extension = fn.fnamemodify(filename, ':e') + local filetype = bo.filetype + local icon + + -- Avoid loading nvim-web-devicons if an icon is provided already + if opts.filetype_icon then + if not component.icon then + local icon_str, icon_color = require('nvim-web-devicons').get_icon_color( + filename, + extension, + { default = true } + ) + + icon = { str = icon_str } + + if opts.colored_icon ~= false then + icon.hl = { fg = icon_color } + end + end + + filetype = ' ' .. filetype + end + + if opts.case == 'titlecase' then + filetype = filetype:gsub('%a', string.upper, 1) + elseif opts.case ~= 'lowercase' then + filetype = filetype:upper() + end + + return filetype, icon end function M.file_encoding() From 4181bc2db8fc6a8219f55da46928d840ab7c9261 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 28 Jan 2022 18:31:55 +0600 Subject: [PATCH 2/8] chore: generated vimdoc (#210) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- doc/feline.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/doc/feline.txt b/doc/feline.txt index b532523..58767de 100644 --- a/doc/feline.txt +++ b/doc/feline.txt @@ -884,6 +884,19 @@ through the provider `opts`:
Default: `'base-only'` +FILE TYPE ~ + +The file type provider has the following options: + + +- `filetype_icon` (boolean): Whether the file type icon is shown alongside the file type. + Default: `false` +- `colored_icon` (boolean): Determines whether file icon should use color inherited from `nvim-web-devicons`.
+ Default: `true` +- `case` (string): The case of the file type string. Possible values are: `'uppercase'`, `'titlecase'` and `'lowercase'`.
+ Default: `'uppercase'` + + GIT ~ The git providers all require gitsigns.nvim From 082174a4e52756e109d19ba959b1291b9683ef55 Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Fri, 11 Feb 2022 11:29:20 +0600 Subject: [PATCH 3/8] fix(providers/file_info): Remove unnecessary trailing space (#214) --- lua/feline/providers/file.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lua/feline/providers/file.lua b/lua/feline/providers/file.lua index 1b28c1a..134f7e0 100644 --- a/lua/feline/providers/file.lua +++ b/lua/feline/providers/file.lua @@ -108,6 +108,11 @@ function M.file_info(component, opts) readonly_str = '' end + -- Add a space at the beginning of the provider if there is an icon + if (icon and icon ~= '') or (component.icon and component.icon ~= '') then + readonly_str = ' ' .. readonly_str + end + if bo.modified then modified_str = opts.file_modified_icon or '●' @@ -118,7 +123,7 @@ function M.file_info(component, opts) modified_str = '' end - return string.format(' %s%s%s', readonly_str, filename, modified_str), icon + return string.format('%s%s%s', readonly_str, filename, modified_str), icon end function M.file_size() From 92806e96b2b6a84823f694449fc0ebb9857378dd Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Sat, 12 Feb 2022 12:12:41 +0600 Subject: [PATCH 4/8] fix: show "[No Name]" instead of "unnamed" for unnamed buffers (#217) To have more consistency with Neovim's built-in statusline configuration. --- lua/feline/providers/file.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/feline/providers/file.lua b/lua/feline/providers/file.lua index 134f7e0..818bce9 100644 --- a/lua/feline/providers/file.lua +++ b/lua/feline/providers/file.lua @@ -99,7 +99,7 @@ function M.file_info(component, opts) end if filename == '' then - filename = 'unnamed' + filename = '[No Name]' end if bo.readonly then From b89c508e0a1fa60b0ed090f4fdf45b18bd465a3b Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Sun, 13 Feb 2022 21:13:53 +0600 Subject: [PATCH 5/8] feat: add utility functions to check component truncation (#218) Ref: #170 Adds two utility functions, `require('feline').is_component_truncated` and `require('feline').is_component_hidden` to check if a component has been truncated or hidden. --- USAGE.md | 16 +++++++++++++ lua/feline/generator.lua | 51 ++++++++++++++++++++++++++++++++++++---- lua/feline/init.lua | 20 ++++++++++++++++ 3 files changed, 83 insertions(+), 4 deletions(-) diff --git a/USAGE.md b/USAGE.md index 2bbd88b..cd721e7 100644 --- a/USAGE.md +++ b/USAGE.md @@ -164,6 +164,18 @@ end If you omit the provider value, it will be set to an empty string. A component with no provider or an empty provider may be useful for things like [applying a highlight to section gaps](#highlight-section-gaps) or just having an icon or separator as a component. +#### Component name + +A component can optionally be given a name. While the component is not required to have a name and the name is mostly useless, it can be used to check if the component has been [truncated](#truncation). To give a component a name, just set its `name` value to a `string`, shown below: + +``` +local my_component = { + name = 'a_unique_name' +} +``` + +Two components inside the `active` or `inactive` table cannot share the same name, so make sure to give all components unique names. + #### Truncation Feline has an automatic smart truncation system where components can be automatically truncated if the statusline doesn't fit within the window. It can be useful if you want to make better use of screen space. It also allows you to better manage which providers are truncated, how they are truncated and in which order they are truncated. @@ -228,6 +240,10 @@ local high_priority_component = { Priority can also be set to a negative number, which can be used to make a component be truncated earlier than the ones with default priority. +##### Check if component is truncated or hidden + +If you give a component a `name`, you can check if that component has been truncated or hidden by Feline's smart truncation system through the utility functions, `require('feline').is_component_truncated` and `require('feline').is_component_hidden`. Both of these functions take two arguments, `winid` which is the window id of the window for which the component's truncation is being checked, the second is the `name` of the component. `is_component_truncated` returns `true` if a component has been truncated or hidden, and `is_component_hidden` returns `true` only if a component has been hidden. + #### Conditionally enable components The `enabled` value of a component can be a boolean or function. This value determines if the component is enabled or not. If false, the component is not shown in the statusline. For example: diff --git a/lua/feline/generator.lua b/lua/feline/generator.lua index 537bada..7faf235 100644 --- a/lua/feline/generator.lua +++ b/lua/feline/generator.lua @@ -6,6 +6,10 @@ local feline = require('feline') local M = { -- Cached highlights highlights = {}, + -- Used to check if a certain component is truncated + component_truncated = {}, + -- Used to check if a certain component is hidden + component_hidden = {}, } -- Return true if any pattern in tbl matches provided value @@ -320,6 +324,7 @@ end -- Wrapper around parse_component that handles any errors that happen while parsing the components -- and points to the location of the component in case of any errors local function parse_component_handle_errors( + winid, component, use_short_provider, statusline_type, @@ -331,10 +336,11 @@ local function parse_component_handle_errors( if not ok then api.nvim_err_writeln( string.format( - "Feline: error while processing component number %d on section %d of type '%s': %s", + "Feline: error while processing component number %d on section %d of type '%s' for window %d: %s", component_number, component_section, statusline_type, + winid, result ) ) @@ -358,6 +364,11 @@ end -- Generate statusline by parsing all components and return a string function M.generate_statusline(is_active) + local winid = api.nvim_get_current_win() + + M.component_truncated[winid] = {} + M.component_hidden[winid] = {} + if not feline.components or is_disabled() then return '' end @@ -388,8 +399,23 @@ function M.generate_statusline(is_active) component_widths[i] = {} for j, component in ipairs(section) do - local component_str = parse_component_handle_errors(component, false, statusline_type, i, j) + if component.name then + if M.component_truncated[winid][component.name] ~= nil then + api.nvim_err_writeln( + string.format( + 'Feline: error while parsing components for window %d: ' + .. "Multiple components with name '%s'", + winid, + component.name + ) + ) + return '' + end + M.component_truncated[winid][component.name] = false + M.component_hidden[winid][component.name] = false + end + local component_str = parse_component_handle_errors(winid, component, false, statusline_type, i, j) local component_width = get_component_width(component_str) component_strs[i][j] = component_str @@ -421,7 +447,14 @@ function M.generate_statusline(is_active) local component = sections[section][number] if component.short_provider then - local component_str = parse_component_handle_errors(component, true, statusline_type, section, number) + local component_str = parse_component_handle_errors( + winid, + component, + true, + statusline_type, + section, + number + ) local component_width = get_component_width(component_str) @@ -435,6 +468,10 @@ function M.generate_statusline(is_active) statusline_width = statusline_width - width_difference component_strs[section][number] = component_str component_widths[section][number] = component_width + + if component.name then + M.component_truncated[winid][component.name] = true + end end end @@ -449,11 +486,17 @@ function M.generate_statusline(is_active) if statusline_width > window_width then for _, indices in ipairs(component_indices) do local section, number = indices[1], indices[2] + local component = sections[section][number] - if sections[section][number].truncate_hide then + if component.truncate_hide then statusline_width = statusline_width - component_widths[section][number] component_strs[section][number] = '' component_widths[section][number] = 0 + + if component.name then + M.component_truncated[winid][component.name] = true + M.component_hidden[winid][component.name] = true + end end if statusline_width <= window_width then diff --git a/lua/feline/init.lua b/lua/feline/init.lua index 70503ec..dc9bd56 100644 --- a/lua/feline/init.lua +++ b/lua/feline/init.lua @@ -136,6 +136,26 @@ function M.use_theme(name_or_tbl) M.reset_highlights() end +-- Check if component with `name` in the statusline of window `winid` is truncated or hidden +function M.is_component_truncated(winid, name) + if gen.component_truncated[winid][name] == nil then + api.nvim_err_writeln(string.format("Component with name '%s' not found", name)) + return + end + + return gen.component_truncated[winid][name] +end + +-- Check if component with `name` in the statusline of window `winid` is hidden +function M.is_component_hidden(winid, name) + if gen.component_hidden[winid][name] == nil then + api.nvim_err_writeln(string.format("Component with name '%s' not found", name)) + return + end + + return gen.component_hidden[winid][name] +end + -- Setup Feline using the provided configuration options function M.setup(config) -- Check if Neovim version is 0.5 or greater From 29edc00833ef5f745e9bab475170c6e965374d98 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 13 Feb 2022 21:15:47 +0600 Subject: [PATCH 6/8] chore: generated vimdoc (#219) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- doc/feline.txt | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/doc/feline.txt b/doc/feline.txt index 58767de..5156966 100644 --- a/doc/feline.txt +++ b/doc/feline.txt @@ -236,6 +236,28 @@ with no provider or an empty provider may be useful for things like |feline-applying-a-highlight-to-section-gaps| or just having an icon or separator as a component. + *feline-Component-name* + +Component name A component can optionally be given a + name. While the component is not + required to have a name and the name is + mostly useless, it can be used to check + if the component has been + |feline-truncated|. To give a component + a name, just set its `name` value to a + `string`, shown below: + + +> + local my_component = { + name = 'a_unique_name' + } +< + + +Two components inside the `active` or `inactive` table cannot share the same +name, so make sure to give all components unique names. + *feline-Truncation* Truncation Feline has an automatic smart truncation @@ -332,6 +354,18 @@ are truncated later on. For example: Priority can also be set to a negative number, which can be used to make a component be truncated earlier than the ones with default priority. +CHECK IF COMPONENT IS TRUNCATED OR HIDDEN + +If you give a component a `name`, you can check if that component has been +truncated or hidden by Feline’s smart truncation system through the utility +functions, `require('feline').is_component_truncated` and +`require('feline').is_component_hidden`. Both of these functions take two +arguments, `winid` which is the window id of the window for which the +component’s truncation is being checked, the second is the `name` of the +component. `is_component_truncated` returns `true` if a component has been +truncated or hidden, and `is_component_hidden` returns `true` only if a +component has been hidden. + *feline-Conditionally-enable-components* Conditionally enable components The `enabled` value of a component can From 5bda845570391aac54c82ebbe2adfc45ddf98969 Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Sun, 13 Feb 2022 22:04:51 +0600 Subject: [PATCH 7/8] feat: add support for conditional components (#220) Adds a `conditional_components` key to the setup function, which allows conditionally using a components table. Closes #141 --- USAGE.md | 31 +++++++++++++++++++++++++++++++ lua/feline/defaults.lua | 3 +++ lua/feline/generator.lua | 24 ++++++++++++++++++++++-- lua/feline/init.lua | 2 ++ 4 files changed, 58 insertions(+), 2 deletions(-) diff --git a/USAGE.md b/USAGE.md index cd721e7..ec85acc 100644 --- a/USAGE.md +++ b/USAGE.md @@ -480,6 +480,37 @@ Now that you know about the components table and how Feline components work, you - `preset` - Set it to use a preconfigured statusline. Currently it can be equal to either `default` for the default statusline or `noicon` for the default statusline without icons. You don't have to put any of the other values if you use a preset, but if you do, your settings will override the preset's settings. To see more info such as how to modify a preset to build a statusline, see: [Modifying an existing preset](#3.-modifying-an-existing-preset) - `components` - The [components table](#components). +- `conditional_components` - An array-like table containing conditionally enabled components tables, each element of the table must be a components table with an additional key, `condition`, which would be a function without arguments that returns a boolean value. If the function returns `true` for a certain window, then that components table will be used for the statusline of that window instead of the default components table. If multiple conditional components match a certain window, the first one in the table will be used. An example usage of this option is shown below: + +```lua +conditional_components = { + { + -- Only use this components table for the 2nd window + condition = function() + return vim.api.nvim_win_get_number(0) == 2 + end, + active = { + -- Components used for active window + }, + inactive = { + -- Components used for inactive windows + }, + }, + { + -- Only use this components table for buffers of filetype 'lua' + condition = function() + return vim.api.nvim_buf_get_option(0, 'filetype') == 'lua' + end, + active = { + -- Components used for active window + }, + inactive = { + -- Components used for inactive windows + }, + } +} +``` + - `custom_providers` - A table containing user-defined [provider functions](#component-providers). For example: ```lua diff --git a/lua/feline/defaults.lua b/lua/feline/defaults.lua index 3228ecc..05f65ad 100644 --- a/lua/feline/defaults.lua +++ b/lua/feline/defaults.lua @@ -92,6 +92,9 @@ return { components = { type = 'table', }, + conditional_components = { + type = 'table', + }, preset = { type = 'string', }, diff --git a/lua/feline/generator.lua b/lua/feline/generator.lua index 7faf235..963041d 100644 --- a/lua/feline/generator.lua +++ b/lua/feline/generator.lua @@ -364,15 +364,35 @@ end -- Generate statusline by parsing all components and return a string function M.generate_statusline(is_active) + local components local winid = api.nvim_get_current_win() M.component_truncated[winid] = {} M.component_hidden[winid] = {} - if not feline.components or is_disabled() then + if is_disabled() then return '' end + -- If a condition for one of the conditional components is satisfied, use those components + if feline.conditional_components then + for _, v in ipairs(feline.conditional_components) do + if v.condition() then + components = v + break + end + end + end + + -- If none of the conditional components match, use the default components + if not components then + if feline.components then + components = feline.components + else + return '' + end + end + local statusline_type if is_active and not is_forced_inactive() then @@ -381,7 +401,7 @@ function M.generate_statusline(is_active) statusline_type = 'inactive' end - local sections = feline.components[statusline_type] + local sections = components[statusline_type] if not sections then return '' diff --git a/lua/feline/init.lua b/lua/feline/init.lua index dc9bd56..6af8d01 100644 --- a/lua/feline/init.lua +++ b/lua/feline/init.lua @@ -209,6 +209,8 @@ function M.setup(config) M.use_preset(preset) end + M.conditional_components = config.conditional_components + -- Ensures custom quickfix statusline isn't loaded g.qf_disable_statusline = true From a318fbe4a9650af4bb8edf7288c491cd2d922bb8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 13 Feb 2022 22:06:47 +0600 Subject: [PATCH 8/8] chore: generated vimdoc (#221) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- doc/feline.txt | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/doc/feline.txt b/doc/feline.txt index 5156966..3d1d80c 100644 --- a/doc/feline.txt +++ b/doc/feline.txt @@ -680,6 +680,40 @@ listed below: - `preset` - Set it to use a preconfigured statusline. Currently it can be equal to either `default` for the default statusline or `noicon` for the default statusline without icons. You don’t have to put any of the other values if you use a preset, but if you do, your settings will override the preset’s settings. To see more info such as how to modify a preset to build a statusline, see: |feline-modifying-an-existing-preset| - `components` - The |feline-components-table|. +- `conditional_components` - An array-like table containing conditionally enabled components tables, each element of the table must be a components table with an additional key, `condition`, which would be a function without arguments that returns a boolean value. If the function returns `true` for a certain window, then that components table will be used for the statusline of that window instead of the default components table. If multiple conditional components match a certain window, the first one in the table will be used. An example usage of this option is shown below: + + +> + conditional_components = { + { + -- Only use this components table for the 2nd window + condition = function() + return vim.api.nvim_win_get_number(0) == 2 + end, + active = { + -- Components used for active window + }, + inactive = { + -- Components used for inactive windows + }, + }, + { + -- Only use this components table for buffers of filetype 'lua' + condition = function() + return vim.api.nvim_buf_get_option(0, 'filetype') == 'lua' + end, + active = { + -- Components used for active window + }, + inactive = { + -- Components used for inactive windows + }, + } + } +< + + + - `custom_providers` - A table containing user-defined |feline-provider-functions|. For example: