Skip to content

Commit

Permalink
Improve ex.lsp.single and ex.lsp.all components (#6)
Browse files Browse the repository at this point in the history
* docs: improve readme

* fix: remove options from pre and post init args

* refactor: simplify ex.lsp component implementation using ex.component

* fix: match_rendered_component method

* docs: improve docs

* chore: improve tools

* refactor: separate ex.lsp.all and ex.lsp.single

* test: add tests for the ex.lsp.single component

* test: tests for ex.lsp.all

* refactor!: rewrite deep_merge as merge

* feat!: introduce hls cache

* refactor: move hls cache to the ex.component

* refactor!: remove is_enabled from the ex.component options

* improve: actualize components on update

* chore: improve readme and makefile

* fix: ci
  • Loading branch information
vladimir-popov authored Jan 7, 2023
1 parent 02cb0f5 commit 515c51d
Show file tree
Hide file tree
Showing 19 changed files with 956 additions and 469 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ jobs:
- name: Install dependencies
run: |
git clone --depth 1 https://github.com/nvim-lua/plenary.nvim ~/.local/share/nvim/site/pack/vendor/start/plenary.nvim
git clone --depth 1 https://github.com/nvim-tree/nvim-web-devicons ~/.local/share/nvim/site/pack/vendor/start/nvim-web-devicons
git clone --depth 1 https://github.com/nvim-lualine/lualine.nvim ~/.local/share/nvim/site/pack/vendor/start/lualine.nvim
ln -s $(pwd) ~/.local/share/nvim/site/pack/vendor/start
Expand Down
22 changes: 12 additions & 10 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,22 @@

help:
@echo "make [command]\nCommands:"
@echo " 'help' to show this help."
@echo " 'test' to run tests."
@echo " 'fmt' to format lua files."
@echo " 'validate' to prepare for pushing changes. It sequentially runs 'fmt' and 'test' tasks."
@echo " 'reinit' to prepare neovim with test configuration of the statusline."
@echo " 'demo' to run neovim with test configuration of the statusline."
@echo " If someting is going wrong, try to use 'reinit' command"
@echo " help to show this help."
@echo " test to run tests."
@echo " fmt to format lua files."
@echo " build to prepare for pushing changes. It sequentially runs 'fmt' and 'test' tasks."
@echo " demo to run neovim with test configuration of the statusline."
@echo " If someting is going wrong, try to use 'prepare' command."
@echo " prepare to prepare neovim with test configuration of the statusline."
@echo "\nHints:\n"
@echo "You can run separate test file or tests from a subdirectory passing 'only' parameter:"
@echo "> make test only=components/git_branch_spec.lua"
@echo "or"
@echo "> make test only=components"
@echo "\nTo turn on debug logs use 'DEBUG_PLENARY' with minimal log level or `true`:"
@echo "> make test DEBUG_PLENARY=true"
@echo "\nTo try the specific component in demo you can specify its name:"
@echo "> make demo component=ex.lsp.all"
@echo "\nYou can pass any argument to the demo task. For example,it can be a path to file which should be opened:"
@echo "> make demo path=<path to file>"

Expand All @@ -31,7 +33,7 @@ test:
demo:
tests/try.sh $(path)

reinit:
tests/try.sh --reinit
prepare:
tests/try.sh --prepare

validate: fmt test
build: fmt test
184 changes: 164 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,102 @@ with additional [components](#Components), and an extended class of the `lualine
use {
'nvim-lualine/lualine.nvim',
requires = {
{ 'nvim-lua/plenary.nvim' },
{ 'dokwork/lualine-ex' },
{ 'nvim-lua/plenary.nvim' },
{ 'kyazdani42/nvim-web-devicons', opt = true },
}
}
```

## Components

### ex.lsp.single

This component shows a name and appropriate icon of the first active lsp client for the current
buffer.

An icon and a color are taken from the `icons` table or `nvim-wev-devicons` plugin (if it's installed).
If no one icon was found for the lsp client neither in `icons`, nor in `nvim-wev-devicons`, the `unknown` icon
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
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
different meaning comparing to usual `lualine` colors. It should be a name of a *color* **not** a
*highlight group*.

```lua
sections = {
lualine_a = {
{
'ex.lsp.single',

icons = {
-- Default icon for any unknow server:
unknown = '?',

-- Default icon for a case, when no one server is run:
lsp_is_off = '',

-- Example of the icon for a client, which doesn't have an icon in `nvim-web-devicons`:
['null-ls'] = { 'N', color = 'magenta' }
}

-- If true then the name of the client will be ommited, and only an icon used:
icons_only = false,

-- The color for the disabled component:
disabled_color = { fg = 'grey' }

-- The color for the icon of the disabled component:
disabled_icon_color = { fg = 'grey' }
}
}
}
```

### ex.lsp.all

This component provides information about status of all run LSP servers. Every server has its own
color and icon, which can be taken from the option `icons` or plugin `nvim-wev-devicons` (if it's
installed).

When some of already run servers is not active for the current buffer, it is in _disabled_ state.
The component in _disabled_ state has a color specified in the option `disabled_color`.

If no on lsp client is run, the component shows only `lsp_is_off` icon.

The `ex.lsp.all` component has the same options as the `[ex.lsp.single](#ex_lsp_single)` component,
with additional option `only_attached`, which can be used to show only attached to the current buffer
clients:

```lua
sections = {
lualine_a = {
{
'ex.lsp.all',

-- Extends options from the `ex.lsp.single`

-- If true then only clients attached to the current buffer will be shown:
only_attached = false,
}
}
}
```

### ex.git.branch

This component provides a name of the git branch for the current working directory.
The color of this component depends on the state of the git worktree. The component
can show different states:
- `changed` means that at least one uncommitted change exists
- `commited` means that everything is committed, and no one tracked file is changed.
If the `cwd` is not under git control, this component is `disabled`. By default, for a disabled
component an icon is shown.
This component shows a name of the git branch for the 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:

- `changed` means that at least one uncommitted change exists;
- `commited` means that everything is committed, and no one tracked file is changed;
- `disabled` means that the `cwd` is not under git control.

```lua
sections = {
Expand All @@ -41,36 +119,82 @@ sections = {

icon = '',

-- `git status` command is used to check the status of the worktree.
-- The `git status` command is used to check the status of the worktree.
-- By default, it's run in background for performance purpose, but it could lead to
-- wrong 'unknow' status at first time. `sync = true` can prevent it, but it degrades
-- startup time
-- the wrong 'unknow' status at the first time. The `sync = true` can prevent it,
-- but it degrades startup time:
sync = false,

-- colors for possible states
-- The colors for possible states:
colors = {
changed = { fg = 'orange' },
commited = { fg = 'green' },
},

-- color for disabled component
-- The color for the disabled component:
disabled_color = { fg = 'grey' }

-- `true` means that icon must be shown even in case when no git repository
-- The color for the icon of the disabled component:
disabled_icon_color = { fg = 'grey' }

-- The `true` means that the icon must be shown even in case when component is empty:
always_show_icon = true
}
}
}
```

### ex.cwd

This component shows the last `depth` directories from the working path:

```lua
sections = {
lualine_a = {
{
'ex.cwd',

-- count of directories from the current working path:
depth = 2,

-- the prefix which should be used when {depth} less than directories at all:
prefix = ''
}
}
}
```

### ex.spellcheck

The simple component shows an actual status of the `vim.wo.spell` option.

```lua
sections = {
lualine_a = {
{
'ex.spellcheck',

-- The color for the disabled component:
disabled_color = { fg = 'grey' }

-- The color for the icon of the disabled component:
disabled_icon_color = { fg = 'grey' }

-- The `true` means that the icon must be shown even in case when component is empty:
always_show_icon = true
}
}
}
```


## 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` to make it possible to
show an icon even for empty component in 'disabled' state. A state of the component depends on the result
of the `is_enabled` function.

The `is_enabled` function can be passed as component option or overridden for a class extended
`lualine.ex.component`:
of the `is_enabled` method:

```lua
-- You can specify default options for every child of your class
Expand All @@ -94,5 +218,25 @@ end
return Spell
```

The difference between cases when `cond = false` and `is_enabled = false` is that in the first case
component will not be rendered at all, but in the second case only an icon with `disabled_color` will be shown.
The difference between cases when `cond = false` and `is_enabled = false` is that the first case
component will not be rendered at all, but in the second case only an icon with `disabled_color`
will be shown.

The `disabled_color` can be specified in the same manner as the `color` for the component.

### ExComponent options

Every child of the `lualine.ex.component` inherits the following options:

```lua
{
-- The color for the disabled component:
disabled_color = { fg = 'grey' }

-- The color for the icon of the disabled component:
disabled_icon_color = { fg = 'grey' }

-- The `true` means that the icon must be shown even in case when component is empty:
always_show_icon = true
}
```
10 changes: 5 additions & 5 deletions lua/lualine/components/ex/git/branch.lua
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,17 @@ local git_providers = {}
---@field git fun(): GitProvider
local GitBranch = require('lualine.ex.component'):extend(default_options)

function GitBranch:pre_init(options)
if options.color then
function GitBranch:pre_init()
if self.options.color then
return
end
options.color = function()
self.options.color = function()
local is_worktree_changed = self.git and self.git():is_worktree_changed(self.options.sync)
-- do not change color for unknown state
if is_worktree_changed == nil then
return options.disabled_color
return self.options.disabled_color
end
return is_worktree_changed and options.colors.changed or options.colors.commited
return is_worktree_changed and self.options.colors.changed or self.options.colors.commited
end
end

Expand Down
90 changes: 90 additions & 0 deletions lua/lualine/components/ex/lsp/all.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
local log = require('plenary.log').new({ plugin = 'ex.lsp.all' })
local ex = require('lualine.ex')
local SingleLsp = require('lualine.components.ex.lsp.single')

local function str_escape(str)
str = str:gsub('-', '_')
return str
end

---@class AllLspOptions: SingleLspOptions
---@field only_attached boolean
---@field icons_only boolean

---@class AllLspComponent: ExComponent
---@field options AllLspOptions
---@field components table
local AllLsp = require('lualine.ex.component'):extend(SingleLsp.default_options)

---@protected
function AllLsp:pre_init()
self.options.component_name = 'ex_lsp_all'
self.components = {}
-- will be used to avoid duplicate highlights:
self.__hls_cache = {}
end

function AllLsp:is_enabled()
return not ex.is_empty(self:__clients())
end

---@private
function AllLsp:__clients()
if self.options.only_attached == true then
return vim.lsp.get_active_clients({ bufnr = 0 })
else
return vim.lsp.get_active_clients()
end
end

local function key(client)
return client.name .. '_' .. client.id
end

---@protected
function AllLsp:update_status(is_focused)
local status = ''
if self:is_enabled() then
self.options.icon = nil
local clients = self:__clients()
self:__actualize_components(clients)
for _, client in pairs(clients) do
local key = key(client)
local lsp = self.components[key]
if not lsp then
lsp = SingleLsp:new({
client = client,
component_name = str_escape('inner_lsp_' .. client.name),
hls_cache = self.__hls_cache,
self = self.options.self,
icons = self.options.icons,
icons_enabled = self.options.icons_enabled,
always_show_icon = self.options.always_show_icon,
icons_only = self.options.icons_only,
disabled_color = self.options.disabled_color,
disabled_icon_color = self.options.disabled_icon_color,
})
self.components[key] = lsp
end
status = status .. lsp:draw(self.default_hl, is_focused)
end
else
self.options.icon = self.options.icons.lsp_is_off
end
return status
end

---@private
function AllLsp:__actualize_components(clients)
local actual_components = {}
for _, client in ipairs(clients) do
local key = key(client)
local component = self.components[key]
if component then
actual_components[key] = component
end
end
self.components = actual_components
end

return AllLsp
Loading

0 comments on commit 515c51d

Please sign in to comment.