From 832f9c2500ff920f6c91bfc1d3f5637bf198d808 Mon Sep 17 00:00:00 2001 From: Vladimir Popov Date: Wed, 15 Nov 2023 20:06:15 +0400 Subject: [PATCH] docs(readme): improve readme and add Tools.md --- ExComponent.md | 85 --------------- README.md | 127 ++++++++++++++++------ Tools.md | 234 ++++++++++++++++++++++++++++++++++++++++ lua/lualine/ex/init.lua | 22 ++-- lua/lualine/ex/lsp.lua | 6 +- 5 files changed, 344 insertions(+), 130 deletions(-) delete mode 100644 ExComponent.md create mode 100644 Tools.md diff --git a/ExComponent.md b/ExComponent.md deleted file mode 100644 index cc2f4e0..0000000 --- a/ExComponent.md +++ /dev/null @@ -1,85 +0,0 @@ -# ExComponent - -**Note:** _this plugin is under develop, and API of the `lualine.ex.component` -can be changed._ - -The `lualine.ex.component` is an abstract class, which extends the -`lualine.component` and adds few additional features. - -Implementation of OOP ideas is taken from the -[classic](https://github.com/rxi/classic/blob/master/classic.lua) project. - -### Create your own component - -To create your own ex component you should extends the `lualine.ex.component`: - -```lua -local Spell = require('lualine.ex.component'):extend() -``` - -The `lualine.ex.component` class inherits all options from the original lualine class such as `icon`, -`fmt`, `color`, `cond`, etc, plus adds few new. You can define default values for options on extending -`lualine.ex.component`: - -```lua -local Spell = require('lualine.ex.component'):extend({ - -- An icon for the component - icon = '⅍', -}) -``` - -All specified options will be available at the `.default_options` -(`Spell.default_options` in the example above). - -### Disabled state - -The `ExComponent` has the 'disabled' state. This state means that a component -is not active, but an icon still should be shown with 'disabled' color. The -difference between cases when the component property `cond = false` and the -`is_enabled = false` is that in the first case a component will not be rendered -at all, but in the second case only the icon with `disabled_color` will be -shown: - -```lua -local Spell = require('lualine.ex.component'):extend({ - -- An icon for the component - icon = '⅍', - - -- A function or boolean to check is the component enabled or not: - is_enabled = function(component) - return vim.wo.spell - end -}) -``` - -The `disabled_color` can be specified in the same manner as the `color` for the -component. The disabled color for the whole component and for the icon may be specified -separately. - -```lua -local Spell = require('lualine.ex.component'):extend({ - -- An icon for the component - icon = '⅍', - - -- The function to check is component enabled or not: - is_enabled = function(component) - return vim.wo.spell - end - - -- The color for the disabled component: - disabled_color = { fg = 'grey' } - - -- The color for the icon of the disabled component: - disabled_icon_color = { fg = 'grey' } -}) -``` - -### Render circle - -TODO: Describe the sequence of methods invocation during render a component - -#### Initialization hooks - -TODO - - diff --git a/README.md b/README.md index 01e644b..17a37d8 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,24 @@ [![lualine-ex ci](https://github.com/dokwork/lualine-ex/actions/workflows/ci.yml/badge.svg)](https://github.com/dokwork/lualine-ex/actions/workflows/ci.yml) This is a [plugin](https://github.com/nvim-lualine/lualine.nvim/wiki/Plugins) -for [lualine.nvim](https://github.com/nvim-lualine/lualine.nvim) -with additional [components](#provided-components), and an extended class of the `lualine.component` with -additional functionality (see [ExComponent.md](ExComponent.md)). +for `lualine.nvim` with additional components. -## Installation +## 📒 Contents: + - [📥 Installation](#installation) + - [🔧 New common component options](#new-common-component-options) + - [🧩 Provided components](#provided-components) + - [ex.spellcheck](#ex.spellcheck) + - [ex.cwd](#ex.cwd) + - [ex.relative_filename](#ex.relative_filename) + - [ex.git.branch](#ex.git.branch) + - [ex.lsp.single](#ex.lsp.single) + - [ex.lsp.all](#ex.lsp.all) + - [🛠️ Tools](#tools) -This is not a plugin for vim. So, it's reasonable to install it as dependency for `lualine.nvim`. +## 📥 Installation + +This is not a plugin for neovim. This is a plugin for _plugin_ for neovim ㋡. +So, it's reasonable to install it as dependency for `lualine.nvim`: ### [Lazy](https://github.com/folke/lazy.nvim) @@ -36,23 +47,34 @@ use { } } ``` + _It may be reasonable to use particular tag to avoid breaking changes._ + +## 🔧 New common component options -## New common component options +Every provided component has additional options: ```lua { - -- A function or boolean to check is the component enabled or not: + -- The function or boolean to check is the component enabled or not: is_enabled = true - -- A color for the disabled component: + -- The color for the disabled component: disabled_color = { fg = 'grey' } - -- A color for the icon of the disabled component: + -- The color for the icon of the disabled component: disabled_icon_color = { fg = 'grey' } + + -- The different default for the option: + draw_empty = true } ``` -## Provided components +This plugin introduces a new `disabled` state. This state means that a component is not active, but +an icon still should be shown with `disabled` color. The difference between `cond = false` and the +`is_enabled = false` is that in the first case a component will not be rendered at all, but in the +second case only the icon with `disabled_color` will be shown. + +## 🧩 Provided components _Most of the components use icons from a [patched nerd font](https://www.nerdfonts.com/) by default._ @@ -112,7 +134,7 @@ sections = { **Reduction algorithm** -The absolute value of the {depth} will be decreased until the length of the path become less then +The absolute value of the {depth} will be decreased until the length of the path becomes less then {max_length}. @@ -131,15 +153,15 @@ system relative to the `cwd`: Some path may be very long and takes significant part in the statusline. It's possible to specify the {max_length} of the filename. To achieve that the follow algorithm is used: -- every part of the path is shorten till the {shorten.length} except parts from the {shorten.exclude} +- every part of the path is shortened till the {shorten.length} except parts from the {shorten.exclude} list; - then the {shorten.length} will be repeatedly decreased until 1 or until the {max_length} will be achieved; -- if it's not enough then the {exclude} setting will be ignored and all parts will be shorten; +- if it's not enough then the {exclude} setting will be ignored and all parts will be shortened; - if the result is still longer than {max_length} than only the file name will be used with the prefix {filename_only_prefix}. -Example of the shorten filename with follow options `{ shorten: { length = 3, exclude = { 1 } } }`: +Example of the shortened filename with follow options `{ shorten: { length = 3, exclude = { 1 } } }`: | Space for component enough to show ...| Component example | | :--- | ---: | @@ -152,9 +174,9 @@ Example of the shorten filename with follow options `{ shorten: { length = 3, ex The {max_length} may be a number, or a function which receives the current component value and returns a number: - - Every value less than 0 means that the filename never should be shorten; - - Zero means that filename should be always shorten; - - A value more or equal to 1 represents a length, after which the filename should be shorten; + - Every value less than 0 means that the filename never should be shortened; + - Zero means that filename should be always shortened; + - A value more or equal to 1 represents a length, after which the filename should be shortened; - A value between 0 and 1 represents a fraction of the current window width if the {laststatus} == 2, or a fraction of the terminal width. @@ -164,11 +186,13 @@ returns a number: -- The prefix which is used when the current file is outside cwd external_prefix = nil, - -- The prefix which is used when the length of the filename after shorten + -- The prefix which is used when the length of the filename after shortening -- is longer than {max_length} filename_only_prefix = '…/', - -- The max length of the component value. + -- The max length of the component value. It may be a number or a function. + -- If it's function, then it will be invoked with actual value, and should + -- return a number: -- < 0 - never shorten; -- 0 - always shorten; -- > 0 and < 1 - shorten when longer than {max_length} * {vim.o.columns} @@ -182,15 +206,15 @@ returns a number: shorten = { -- The count of letters, which will be taken from every part of the path lenght = 5, - -- The list of indexes of filename parts, which should not be shorten at all - -- (the file name { -1 } is always excluded) + -- The list of indexes of filename parts, which should not be shortened + -- at all (the file name { -1 } is always excluded) exclude = nil }, } ``` -_`ex.relative_filename` component doesn't provide options to show file states, because it easily -possible to do with standard approach:_ +_`ex.relative_filename` component doesn't provide options to show file states, +because it is easily possible to do with standard approach:_ ```lua -- readonly mode indicator example: @@ -211,7 +235,7 @@ possible to do with standard approach:_ | :---: | :---: | :---: | | | | | -This component shows a name of the git branch for the current working directory. The color of this +This component shows a name of a git branch for a current working directory. The color of this component depends on the state of the git worktree. The component can show different states of the git worktree: @@ -219,6 +243,7 @@ git worktree: - `committed` means that everything is committed, and no one tracked file is changed; - `disabled` means that the `cwd` is not under git control. +**Default configuration:** ```lua sections = { lualine_a = { @@ -240,10 +265,35 @@ sections = { }, -- The color for the disabled component: - disabled_color = { fg = 'grey' } + disabled_color = { fg = 'grey' }, -- The color for the icon of the disabled component: - disabled_icon_color = { fg = 'grey' } + disabled_icon_color = { fg = 'grey' }, + + -- It can be a function which receive an actual component value, and should return a number; + -- or it can be a number: + -- * any number >= 1 is max count of symbols in the branch name + -- * a number between 0 and 1 means fraction of the {vim.o.columns} + -- for {laststatus} == 3, and fraction of the {vim.api.nvim_win_get_width(0)} + -- in other cases. + -- When this option is defined, a component value will be cropped if it's longer then + -- a value of this property. + max_length = nil, + + -- Follow options actual only if {max_length} is defined: + crop = { + -- The string which will be used instead of cropped part. + stub = '…', + + -- The side from which a value should be cropped. It may be 'left' or 'right'. + -- If not specified, result depends on the component section: + -- 'right' for a|b|c + -- 'left' for x|y|z + side = nil + } + + -- The {ex.crop} function is default {fmt} implementation. + fmt = ex.crop } } } @@ -263,11 +313,11 @@ If no one icon was found for the lsp client neither in `icons`, nor in `nvim-wev will be used. For the case, when no one server is run, the component is in disabled state and has the `lsp_is_off` icon. -An icon should be either string, or a table with following format: the `[1]` element must be a string with +An icon should be either a string or a table with following format: the `[1]` element must be a string with icon's symbol; the optional element `color` should be one of: a name of a color, or a color in #RGB format, or a table with `fg` color. -**NOTE:** the icon's property `color` with type of string for any of an icon from the `icons` has +**NOTE:** the icon's property `color` with the type of string for any icon from the `icons` has different meaning comparing to usual `lualine` colors. It should be a name of a *color* **not** a *highlight group*. @@ -318,8 +368,6 @@ The component in _disabled_ state has a color specified in the option `disabled_ If no one lsp client is run, the component shows only `lsp_is_off` icon. -You may double click by this component to close all not used lsp clients. - The `ex.lsp.all` component has the same options as the [ex.lsp.single](#exlspsingle) component, with few additional: @@ -337,9 +385,26 @@ sections = { -- If true then every closed client will be echoed: notify_enabled = true - -- A name of highlight group which should be used in echo: + -- The name of highlight group which should be used in echo: notify_hl = 'Comment' } } } ``` + +You may double click by this component to stop all not used lsp clients. +Also, you can use the function to close not used clients outside the component +(see [Tools > stop_unused_clients](Tools.md/#stop_unused_clients)), or change the `on_click` handler: + +```lua +-- close on Ctrl+single click +on_click = function(clicks, button, modified) + if modified == 'c' and clicks == 1 then + require('lualine.ex.lsp').stop_unused_clients() + end +end +``` + +## 🛠️ Tools +This plugin provide additional tools to help you create your own components. Read more details here: +[Tools.md](Tools.md). diff --git a/Tools.md b/Tools.md new file mode 100644 index 0000000..039a7b9 --- /dev/null +++ b/Tools.md @@ -0,0 +1,234 @@ +# 🛠️ Tools + +## Contents + + - [ExComponent](#excomponent) + - [Helpful functions](#helpful-functions) + - [Make a demo](#make-a-demo) + +## ExComponent + +**Note:** _this plugin is under develop, and API of the `lualine.ex.component` +can be changed._ + +The `lualine.ex.component` is an abstract class, which extends the +`lualine.component` and adds a few additional features. + +Implementation of OOP ideas is taken from the +[classic](https://github.com/rxi/classic/blob/master/classic.lua) project. + + The best way to become familiar with `ex.component` is create your own + component using it. As example, we will create a simple component to show the + `vim.bo.spelllang` value. + +### Creating a new component + +To create your own `ex.component` you should extends the `lualine.ex.component` +inside `lua/components` directory of your plugin: + +```lua +-- lua/components/spellcheck.lua +local Spell = require('lualine.ex.component'):extend() +``` + +The `lualine.ex.component` class inherits all options from the original lualine +class such as `icon`, `fmt`, `color`, `cond`, etc, plus adds a few new. You can +define default values for options on extending `lualine.ex.component`: + +```lua +local Spell = require('lualine.ex.component'):extend({ + -- The icon of the component + icon = '⅍', +}) +``` + +All specified options will be available at the `.default_options` +(`Spell.default_options` in the example above). + +### Disabled state + +The `ExComponent` has a new `disabled` state. This state means that a component +is not active, but the icon still should be shown with `disabled` color. The +difference between cases when the component property `cond = false` and the +`is_enabled = false` is that in the first case a component will not be rendered +at all, but in the second case only the icon with `disabled_color` will be +shown: + +```lua +local Spell = require('lualine.ex.component'):extend({ + -- An icon for the component + icon = '⅍', + + -- A function or boolean to check is the component enabled or not: + is_enabled = function(component) + return vim.wo.spell + end +}) +``` + +The `disabled_color` can be specified in the same manner as the `color` for the +component. The disabled color for the whole component and for the icon may be +specified separately. + +```lua +local Spell = require('lualine.ex.component'):extend({ + -- An icon for the component + icon = '⅍', + + -- The function to check is component enabled or not: + is_enabled = function(component) + return vim.wo.spell + end + + -- The color for the disabled component: + disabled_color = { fg = 'grey' } + + -- The color for the icon of the disabled component: + disabled_icon_color = { fg = 'grey' } +}) +``` + +### Provide component's value + +To provide the value (or status in terms of the lualine) of the component, you +should override the `update_status` method: + +```lua +local Spell = require('lualine.ex.component'):extend({ +... +} +function Spell:update_status() + return vim.o.spell and vim.bo.spelllang or '' +end +``` + +After that, you may use your component as all other default components from the +lualine: + +```lua +sections = { + lualine_a = { + 'spellcheck' + } +} +``` + +### Render circle + +TODO: Describe the sequence of methods invocation during render a component + +#### Initialization hooks + +TODO + + +## Helpful functions + +The `lualine.ex` plugin provides a few functions, which can help you to define +your own components. + +### `lsp.stop_unused_clients()` + +```lua +require('lualine.ex.lsp').stop_unused_clients({opts}) +``` + +Iterates over active lsp clients and stops every client without attached +buffers. This function is used as default `on_click` handler for the +`ex.lsp.all` component. + +**Parameters:** + + * {opts} (table) optional additional options: + * notify_enabled (boolean) turns on notifications. The notifications + are implemented as call the {echohl} function. + Default to `false`. + * notify_hl (string) the name of the highlight group which will + be used to show notification. Default to 'Comment'. + + +### `crop()` + +```lua +require('lualine.ex').crop({str}, {cmp}) +``` + +Crops the component when its length longer than {max_length} option (if it's +specified). + +**Parameters:** + + * {str} (string) the current value of the component. + * {cmp} (table) the component object with follow options: + * max_length (number) the maximum count of symbols in the component, + after which the component will be cropped. If it's absent + or less or equal zero, the function returns `nil`. Default + to `nil`. + * crop (table) crop options: + * stub (string) a string which will be used instead of cropped + part. Default to '…'. + * side ('left' | 'right') a side from which a value will be + cropped. If absent, it will be calculated from the + component's section: for sections a,b,c a component value + will be cropped from the left; for sections x,y,z from the + right. + +**Return:** + +Original or cropped string. + +### `max_length()` + +```lua +require('lualine.ex').max_length({ln}, {str}) +``` + +Resolves a {max_length} option of a component. The result depends on {ln}. + +**Parameters:** + + * {ln} (number) a count of symbols or fraction of the statusline width. + * if {ln} is a function, it will be invoked with {str} parameter, + and result will be used as describet below; + * if {ln} is a number > 0 and < 1, and {laststatus} == 3 then + this function calculates a fraction of the {vim.o.columns}: + `math.floor(ln * vim.o.columns)`; + * if {lng} is number > 0 and < 1, and {laststatus} ~= 3 then + this function calculates a fraction of the + {vim.api.nvim_win_get_width(0)}: + `math.floor(ln * vim.api.nvim_win_get_width(0))`; + * all other numbers will be returned as is; + * if {ln} is not number then nil will be returned. + * {str} (string) an optional parameter which will be passed to the {ln}, + if it's a function. + +**Return:** + +An integer number. + +## Make a demo + +You may try every component from this repo in the separate nvim instance. To do +this run in terminal: + +```sh +make demo component= +``` + +Where the `` is the same string as should be used in the +lualine configuration. For example: `ex.cwd`. + +Also, it's possible to pass a custom component options to the demo: + +```sh +make demo component= component_opts='' +``` + +The `` should correspond to the lua table with component options. +For example: + +```sh +make demo component=ex.cwd component_opts='{ "depth": 1 }' +``` + +![demo]() diff --git a/lua/lualine/ex/init.lua b/lua/lualine/ex/init.lua index 5bbe031..a80bf90 100644 --- a/lua/lualine/ex/init.lua +++ b/lua/lualine/ex/init.lua @@ -51,29 +51,29 @@ M.merge = function(dest, source, already_visited) end ---Resolves a {max_length} option of a component. ---- - if {lng} is a function, it invokes that function with the {value} parameter; ---- - if {lng} is number > 0 and < 1, and {laststatus} == 3 then this function +--- - if {ln} is a function, it invokes that function with the {value} parameter; +--- - if {ln} is number > 0 and < 1, and {laststatus} == 3 then this function --- calculates a fraction of the {vim.o.columns}; ---- - if {lng} is number > 0 and < 1, and {laststatus} ~= 3 then this function +--- - if {ln} is number > 0 and < 1, and {laststatus} ~= 3 then this function --- calculates a fraction of the {vim.api.nvim_win_get_width(0)}; --- - all other numbers will be returned as is; ---- - in case of all other types the nill will be returned. +--- - in case of all other types the nil will be returned. --- ----@param lng number|fun(value: string) an initial setting for the max_length. +---@param ln number|fun(value: string) an initial setting for the max_length. ---@param str? string an actual component value which will be passed to the {lng} --- if it's a function. ---@return integer | nil -M.max_length = function(lng, str) - lng = (type(lng) == 'function') and lng(str) or lng - if type(lng) ~= 'number' then +M.max_length = function(ln, str) + ln = (type(ln) == 'function') and ln(str) or ln + if type(ln) ~= 'number' then return nil end - if lng > 0 and lng < 1 then + if ln > 0 and ln < 1 then return math.floor( - lng * (vim.o.laststatus == 3 and vim.o.columns or vim.api.nvim_win_get_width(0)) + ln * (vim.o.laststatus == 3 and vim.o.columns or vim.api.nvim_win_get_width(0)) ) else - return lng + return ln end end diff --git a/lua/lualine/ex/lsp.lua b/lua/lualine/ex/lsp.lua index a8b0395..9cc11ae 100644 --- a/lua/lualine/ex/lsp.lua +++ b/lua/lualine/ex/lsp.lua @@ -1,9 +1,9 @@ local M = {} ---Iterates over active lsp clients and stops every client without attached buffers. ----@param opts table ----@field notify_enabled boolean true enables echo about every stopped client. ----@field notify_hl HighlightGroup a name of the highlight which should be used in echo. +---@param opts? table +---@field notify_enabled? boolean true enables echo about every stopped client. +---@field notify_hl? HighlightGroup a name of the highlight which should be used in echo. M.stop_unused_clients = function(opts) opts = opts or {} local hl = opts.notify_hl or 'Comment'