Merge pull request refactor: components (#81)

* refactor: separate the child creation and initialization (#81)

- includes modified class implementation from https://github.com/rxi/classic/blob/master/classic.lua
- now base component class is created from classic.
- change to how component classes are created.
  - Don't overwrite new method to initialize a component.
    Overwrite the init method. new is responsible for
    creating class object and calling init on it.
    Unlike previous new overwrite you don't need to create
    the class (table) and return it. Instead you will recive
    the object as self and do required manipulation on that
    just like any most other oop langs. Also don't need to
    return anything from init. init's job is to initialize.
    remember to call classes init before running your operations
    unfortunately lua isn't full fledged oop lang and I don't
    how to automate this.
- changes how super classes are accesed.
  - rename Component._parent -> Component.super
  - methods on super classes now ran through super class instead
    of objects _parent self._parent as that can lead to recursive inf loop.
  See branch, diff, tabs, buffer classes call to init for example
  on pattern.
- All components updated to reflect current logic
- component loader updated to use new initialization procedure.
- updated tests
- updated BREAKING_CHANGES.md
- plus quite a bit of formatting changes in the components
  - comp.method = function(self, ...) -> function M:method(...)
BREAKING_CHANGE

* enhance: allow components to be functions too (#81)

Some components like hostname, progress are so simple that
the component class setup is just unnecessary boilerplate.
Allowing them to be function simplifies things.
With this you can put your regular component functions in
~/.config/nvim/lua/lualine/components/ folder and treat then as
regular lualine components just like 'mode' or 'branch'.
Hopefully this will help lualine plugins grow.

* refactor: allow components, extensions, themes to be multi file module
(#81)

utilize that to split tabs & buffers components into smaller files

* refactor: rename component type luae and vimf

- rename luae -> lua_expr
- rename vimf -> vim_fun
luae & vimf were too vague and hand to understand.

BREAKING_CHANGE

* chore: remove deprecation notices and deprecated options from #24

+ fix tests

* refactor: split diagnostics component (#81)

* refactor: split branch component (#81)

* refactor: split diff component (#81)
This commit is contained in:
Shadman 2021-10-11 19:17:06 +06:00 committed by GitHub
commit 7d15304449
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 1415 additions and 1482 deletions

View File

@ -61,3 +61,33 @@ See [#24](https://github.com/shadmansaleh/lualine.nvim/pull/24) for details
modified, removed in diff_color table option
- color_error, color_warning, color_info, color_hint are now available
as error, warn, info, hint in diagnostics_color table option
### Component class refactor
***Applicable only ones responsible for custom components***
- Now call extend method on super class to create new type of component instead of calling new with empty args.
- Don't overrite new method for initialization. Overrite init instead.
- rename Component._parent -> Component.super
- Call methods from super class directly on classes super not through
objects super or self.super
So basically if you had something like
```lua
local my_comp = require'lualine.component':new()
function mycomp:new(options, child)
local obj = self._parent:new(options, child or my_comp)
obj.x = 1
obj.y = 2
return obj
end
```
change it to
```lua
local my_comp = require('lualine.component'):extend()
function my_comp:init(options)
-- Notice carefully it's not self.super.init nor my_comp.super:init
my_comp.super.init(self, options)
self.x = 1
self.y = 2
end
```

View File

@ -390,8 +390,8 @@ sections = {
-- When type is omitted lualine will guess it.
-- Available types [format: type_name(example)]
-- mod(branch/filename), stl(%f/%m), var(g:coc_status/bo:modifiable),
-- luae(lua expressions), vimf(viml function name)
-- luae is short for lua-expression and vimf is short fror vim-function
-- lua_expr(lua expressions), vim_fun(viml function name)
-- lua_expr is short for lua-expression and vim_fun is short fror vim-function
type = nil,
padding = 1, -- adds padding to the left and right of components
-- padding can be specified to left or right separately like

View File

@ -371,8 +371,8 @@ for all components.
-- When type is omitted lualine will guess it.
-- Available types [format: type_name(example)]
-- mod(branch/filename), stl(%f/%m), var(g:coc_status/bo:modifiable),
-- luae(lua expressions), vimf(viml function name)
-- luae is short for lua-expression and vimf is short fror vim-function
-- lua_expr(lua expressions), vim_fun(viml function name)
-- lua_expr is short for lua-expression and vim_fun is short fror vim-function
type = nil,
padding = 1, -- adds padding to the left and right of components
-- padding can be specified to left or right separately like

View File

@ -1,216 +1,180 @@
-- Copyright (c) 2020-2021 shadmansaleh
-- MIT license, see LICENSE for more details.
local require = require('lualine_require').require
local highlight = require 'lualine.highlight'
local M = require('lualine.utils.class'):extend()
-- Used to provide a unique id for each component
local component_no = 1
local function check_deprecated_options(options)
local function rename_notice(before, now)
if options[before] then
require('lualine.utils.notices').add_notice(string.format(
[[
### option.%s
%s option has been renamed to `%s`. Please use `%s` instead in your config
for %s component.
]],
before,
before,
now,
now,
options.component_name or 'function'
))
options[now] = options[before]
options[before] = nil
function M:__tostring()
local str = 'Component: ' .. self.options.component_name
if self.debug then
str = str .. '\n---------------------\n' .. vim.inspect(self)
end
return str
end
-- Initialize new component
function M:init(options)
self.options = options or {}
component_no = component_no + 1
if not options.component_name then
self.options.component_name = tostring(component_no)
end
self.component_no = component_no
self:set_separator()
self:create_option_highlights()
end
function M:set_separator()
if self.options.separator == nil then
if self.options.component_separators then
if self.options.self.section < 'lualine_x' then
self.options.separator = self.options.component_separators.left
else
self.options.separator = self.options.component_separators.right
end
end
end
rename_notice('format', 'fmt')
rename_notice('condition', 'cond')
end
local Component = {
-- Creates a new component
new = function(self, options, child)
local new_component = {}
new_component.options = options
new_component._parent = child or self
setmetatable(new_component, { __index = new_component._parent })
-- Operation that are required for creating new components but not for inheritence
if options ~= nil then
component_no = component_no + 1
check_deprecated_options(new_component.options)
if not options.component_name then
new_component.options.component_name = tostring(component_no)
function M:create_option_highlights()
-- set custom highlights
if self.options.color then
self.options.color_highlight = highlight.create_component_highlight_group(
self.options.color,
self.options.component_name,
self.options
)
end
end
-- Adds spaces to left and right of a component
function M:apply_padding()
local padding = self.options.padding
local l_padding, r_padding
if padding == nil then
padding = 1
end
if type(padding) == 'number' then
l_padding, r_padding = padding, padding
elseif type(padding) == 'table' then
l_padding, r_padding = padding.left, padding.right
end
if l_padding then
if self.status:find '%%#.*#' == 1 then
-- When component has changed the highlight at begining
-- we will add the padding after the highlight
local pre_highlight = vim.fn.matchlist(self.status, [[\(%#.\{-\}#\)]])[2]
self.status = pre_highlight .. string.rep(' ', l_padding) .. self.status:sub(#pre_highlight + 1, #self.status)
else
self.status = string.rep(' ', l_padding) .. self.status
end
end
if r_padding then
self.status = self.status .. string.rep(' ', r_padding)
end
end
-- Applies custom highlights for component
function M:apply_highlights(default_highlight)
if self.options.color_highlight then
self.status = highlight.component_format_highlight(self.options.color_highlight) .. self.status
end
if type(self.options.separator) ~= 'table' and self.status:find '%%#' then
-- Apply default highlight only when we aren't applying trans sep and
-- the component has changed it's hl. since we won't be applying
-- regular sep in those cases so ending with default hl isn't neccessay
self.status = self.status .. default_highlight
-- Also put it in applied sep so when sep get struped so does the hl
self.applied_separator = default_highlight
end
-- Prepend default hl when the component doesn't start with hl otherwise
-- color in previous component can cause side effect
if not self.status:find '^%%#' then
self.status = default_highlight .. self.status
end
end
-- Apply icon in front of component
function M:apply_icon()
if self.options.icons_enabled and self.options.icon then
self.status = self.options.icon .. ' ' .. self.status
end
end
-- Apply separator at end of component only when
-- custom highlights haven't affected background
function M:apply_separator()
local separator = self.options.separator
if type(separator) == 'table' then
if self.options.separator[2] == '' then
if self.options.self.section < 'lualine_x' then
separator = self.options.component_separators.left
else
separator = self.options.component_separators.right
end
new_component.component_no = component_no
new_component:set_separator()
new_component:create_option_highlights()
end
return new_component
end,
set_separator = function(self)
if self.options.separator == nil then
if self.options.component_separators then
if self.options.self.section < 'lualine_x' then
self.options.separator = self.options.component_separators.left
else
self.options.separator = self.options.component_separators.right
end
end
end
end,
create_option_highlights = function(self)
-- set custom highlights
if self.options.color then
self.options.color_highlight = highlight.create_component_highlight_group(
self.options.color,
self.options.component_name,
self.options
)
end
end,
-- set upper or lower case
apply_case = function(self)
-- Donn't work on components that emit vim statusline escaped chars
if self.status:find '%%' and not self.status:find '%%%%' then
else
return
end
if self.options.upper == true then
self.status = self.status:upper()
elseif self.options.lower == true then
self.status = self.status:lower()
end
end,
end
if separator and #separator > 0 then
self.status = self.status .. separator
self.applied_separator = self.applied_separator .. separator
end
end
-- Adds spaces to left and right of a component
apply_padding = function(self)
local padding = self.options.padding
local l_padding, r_padding
if padding == nil then
padding = 1
end
if type(padding) == 'number' then
l_padding, r_padding = padding, padding
elseif type(padding) == 'table' then
l_padding, r_padding = padding.left, padding.right
end
if l_padding then
if self.status:find '%%#.*#' == 1 then
-- When component has changed the highlight at begining
-- we will add the padding after the highlight
local pre_highlight = vim.fn.matchlist(self.status, [[\(%#.\{-\}#\)]])[2]
self.status = pre_highlight .. string.rep(' ', l_padding) .. self.status:sub(#pre_highlight + 1, #self.status)
else
self.status = string.rep(' ', l_padding) .. self.status
end
end
if r_padding then
self.status = self.status .. string.rep(' ', r_padding)
end
end,
function M:apply_section_separators()
if type(self.options.separator) ~= 'table' then
return
end
if self.options.separator.left ~= nil and self.options.separator.left ~= '' then
self.status = string.format('%%s{%s}%s', self.options.separator.left, self.status)
self.strip_previous_separator = true
end
if self.options.separator.right ~= nil and self.options.separator.right ~= '' then
self.status = string.format('%s%%S{%s}', self.status, self.options.separator.right)
end
end
-- Applies custom highlights for component
apply_highlights = function(self, default_highlight)
if self.options.color_highlight then
self.status = highlight.component_format_highlight(self.options.color_highlight) .. self.status
end
if type(self.options.separator) ~= 'table' and self.status:find '%%#' then
-- Apply default highlight only when we aren't applying trans sep and
-- the component has changed it's hl. since we won't be applying
-- regular sep in those cases so ending with default hl isn't neccessay
self.status = self.status .. default_highlight
-- Also put it in applied sep so when sep get struped so does the hl
self.applied_separator = default_highlight
end
-- Prepend default hl when the component doesn't start with hl otherwise
-- color in previous component can cause side effect
if not self.status:find '^%%#' then
self.status = default_highlight .. self.status
end
end,
-- Apply icon in front of component
apply_icon = function(self)
if self.options.icons_enabled and self.options.icon then
self.status = self.options.icon .. ' ' .. self.status
end
end,
-- Apply separator at end of component only when
-- custom highlights haven't affected background
apply_separator = function(self)
local separator = self.options.separator
if type(separator) == 'table' then
if self.options.separator[2] == '' then
if self.options.self.section < 'lualine_x' then
separator = self.options.component_separators.left
else
separator = self.options.component_separators.right
end
else
return
end
end
if separator and #separator > 0 then
self.status = self.status .. separator
self.applied_separator = self.applied_separator .. separator
end
end,
apply_section_separators = function(self)
if type(self.options.separator) ~= 'table' then
return
end
if self.options.separator.left ~= nil and self.options.separator.left ~= '' then
self.status = string.format('%%s{%s}%s', self.options.separator.left, self.status)
self.strip_previous_separator = true
end
if self.options.separator.right ~= nil and self.options.separator.right ~= '' then
self.status = string.format('%s%%S{%s}', self.status, self.options.separator.right)
end
end,
strip_separator = function(self)
if not self.applied_separator then
self.applied_separator = ''
end
self.status = self.status:sub(1, (#self.status - #self.applied_separator))
self.applied_separator = nil
return self.status
end,
-- variable to store component output for manipulation
status = '',
-- Actual function that updates a component. Must be overwritten with component functionality
-- luacheck: push no unused args
update_status = function(self, is_focused) end,
-- luacheck: pop
-- Driver code of the class
draw = function(self, default_highlight, is_focused)
self.status = ''
function M:strip_separator()
if not self.applied_separator then
self.applied_separator = ''
end
self.status = self.status:sub(1, (#self.status - #self.applied_separator))
self.applied_separator = nil
return self.status
end
if self.options.cond ~= nil and self.options.cond() ~= true then
return self.status
end
local status = self:update_status(is_focused)
if self.options.fmt then
status = self.options.fmt(status or '')
end
if type(status) == 'string' and #status > 0 then
self.status = status
self:apply_icon()
self:apply_case()
self:apply_padding()
self:apply_highlights(default_highlight)
self:apply_section_separators()
self:apply_separator()
end
-- variable to store component output for manipulation
M.status = ''
-- Actual function that updates a component. Must be overwritten with component functionality
-- luacheck: push no unused args
function M:update_status(is_focused) end
-- luacheck: pop
-- Driver code of the class
function M:draw(default_highlight, is_focused)
self.status = ''
self.applied_separator = ''
if self.options.cond ~= nil and self.options.cond() ~= true then
return self.status
end,
}
end
local status = self:update_status(is_focused)
if self.options.fmt then
status = self.options.fmt(status or '')
end
if type(status) == 'string' and #status > 0 then
self.status = status
self:apply_icon()
self:apply_padding()
self:apply_highlights(default_highlight)
self:apply_section_separators()
self:apply_separator()
end
return self.status
end
return Component
return M

View File

@ -1,51 +1,64 @@
-- Copyright (c) 2020-2021 shadmansaleh
-- MIT license, see LICENSE for more details.
local Branch = require('lualine.component'):new()
local modules = require('lualine_require').lazy_require {
utils = 'lualine.utils.utils',
}
local M = {}
local require = require('lualine_require').require
local utils = require 'lualine.utils.utils'
-- vars
Branch.git_branch = ''
Branch.git_dir = ''
local current_git_branch = ''
local current_git_dir = ''
local branch_cache = {} -- stores last known branch for a buffer
local active_bufnr = '0'
-- os specific path separator
Branch.sep = package.config:sub(1, 1)
local sep = package.config:sub(1, 1)
-- event watcher to watch head file
-- Use file wstch for non windows and poll for windows.
-- windows doesn't like file watch for some reason.
Branch.file_changed = Branch.sep ~= '\\' and vim.loop.new_fs_event() or vim.loop.new_fs_poll()
Branch.active_bufnr = '0'
local branch_cache = {} -- stores last known branch for a buffer
-- Initilizer
Branch.new = function(self, options, child)
local new_branch = self._parent:new(options, child or Branch)
if not new_branch.options.icon then
new_branch.options.icon = '' -- e0a0
end
-- run watch head on load so branch is present when component is loaded
Branch.find_git_dir()
-- update branch state of BufEnter as different Buffer may be on different repos
modules.utils.define_autocmd('BufEnter', "lua require'lualine.components.branch'.find_git_dir()")
return new_branch
end
Branch.update_status = function(_, is_focused)
if Branch.active_bufnr ~= vim.g.actual_curbuf then
-- Workaround for https://github.com/hoob3rt/lualine.nvim/issues/286
-- See upstream issue https://github.com/neovim/neovim/issues/15300
-- Diff is out of sync re sync it.
Branch.find_git_dir()
end
if not is_focused then
return branch_cache[vim.fn.bufnr()] or ''
end
return Branch.git_branch
end
local file_changed = sep ~= '\\' and vim.loop.new_fs_event() or vim.loop.new_fs_poll()
local git_dir_cache = {} -- Stores git paths that we already know of
-- returns full path to git directory for current directory
function Branch.find_git_dir()
-- sets git_branch veriable to branch name or commit hash if not on branch
local function get_git_head(head_file)
local f_head = io.open(head_file)
if f_head then
local HEAD = f_head:read()
f_head:close()
local branch = HEAD:match 'ref: refs/heads/(.+)$'
if branch then
current_git_branch = branch
else
current_git_branch = HEAD:sub(1, 6)
end
end
return nil
end
-- Update branch
local function update_branch()
active_bufnr = tostring(vim.fn.bufnr())
file_changed:stop()
local git_dir = current_git_dir
if git_dir and #git_dir > 0 then
local head_file = git_dir .. sep .. 'HEAD'
get_git_head(head_file)
file_changed:start(
head_file,
sep ~= '\\' and {} or 1000,
vim.schedule_wrap(function()
-- reset file-watch
update_branch()
end)
)
else
-- set to '' when git dir was not found
current_git_branch = ''
end
branch_cache[vim.fn.bufnr()] = current_git_branch
end
-- returns full path to git directory for dir_path or current directory
function M.find_git_dir(dir_path)
-- get file dir so we can search from that dir
local file_dir = vim.fn.expand '%:p:h'
local file_dir = dir_path or vim.fn.expand '%:p:h'
local root_dir = file_dir
local git_dir
-- Search upward for .git file or folder
@ -54,7 +67,7 @@ function Branch.find_git_dir()
git_dir = git_dir_cache[root_dir]
break
end
local git_path = root_dir .. Branch.sep .. '.git'
local git_path = root_dir .. sep .. '.git'
local git_file_stat = vim.loop.fs_stat(git_path)
if git_file_stat then
if git_file_stat.type == 'directory' then
@ -66,12 +79,12 @@ function Branch.find_git_dir()
git_dir = git_dir:match 'gitdir: (.+)$'
file:close()
-- submodule / relative file path
if git_dir and git_dir:sub(1, 1) ~= Branch.sep and not git_dir:match '^%a:.*$' then
if git_dir and git_dir:sub(1, 1) ~= sep and not git_dir:match '^%a:.*$' then
git_dir = git_path:match '(.*).git' .. git_dir
end
end
if git_dir then
local head_file_stat = vim.loop.fs_stat(git_dir .. Branch.sep .. 'HEAD')
local head_file_stat = vim.loop.fs_stat(git_dir .. sep .. 'HEAD')
if head_file_stat and head_file_stat.type == 'file' then
break
else
@ -79,54 +92,34 @@ function Branch.find_git_dir()
end
end
end
root_dir = root_dir:match('(.*)' .. Branch.sep .. '.-')
root_dir = root_dir:match('(.*)' .. sep .. '.-')
end
git_dir_cache[file_dir] = git_dir
if Branch.git_dir ~= git_dir then
Branch.git_dir = git_dir
Branch.update_branch()
if dir_path == nil and current_git_dir ~= git_dir then
current_git_dir = git_dir
update_branch()
end
return git_dir
end
-- sets git_branch veriable to branch name or commit hash if not on branch
function Branch.get_git_head(head_file)
local f_head = io.open(head_file)
if f_head then
local HEAD = f_head:read()
f_head:close()
local branch = HEAD:match 'ref: refs/heads/(.+)$'
if branch then
Branch.git_branch = branch
else
Branch.git_branch = HEAD:sub(1, 6)
end
function M.init()
-- run watch head on load so branch is present when component is loaded
M.find_git_dir()
-- update branch state of BufEnter as different Buffer may be on different repos
utils.define_autocmd('BufEnter', "lua require'lualine.components.branch.git_branch'.find_git_dir()")
end
function M.get_branch(bufnr)
if vim.g.actual_curbuf ~= nil and active_bufnr ~= vim.g.actual_curbuf then
-- Workaround for https://github.com/hoob3rt/lualine.nvim/issues/286
-- See upstream issue https://github.com/neovim/neovim/issues/15300
-- Diff is out of sync re sync it.
M.find_git_dir()
end
return nil
if bufnr then
return branch_cache[bufnr] or ''
end
return current_git_branch
end
-- Update branch
function Branch.update_branch()
Branch.active_bufnr = tostring(vim.fn.bufnr())
Branch.file_changed:stop()
local git_dir = Branch.git_dir
if git_dir and #git_dir > 0 then
local head_file = git_dir .. Branch.sep .. 'HEAD'
Branch.get_git_head(head_file)
Branch.file_changed:start(
head_file,
Branch.sep ~= '\\' and {} or 1000,
vim.schedule_wrap(function()
-- reset file-watch
Branch.update_branch()
end)
)
else
-- set to '' when git dir was not found
Branch.git_branch = ''
end
branch_cache[vim.fn.bufnr()] = Branch.git_branch
end
return Branch
return M

View File

@ -0,0 +1,20 @@
-- Copyright (c) 2020-2021 shadmansaleh
-- MIT license, see LICENSE for more details.
local M = require('lualine.component'):extend()
local require = require('lualine_require').require
local git_branch = require 'lualine.components.branch.git_branch'
-- Initilizer
M.init = function(self, options)
M.super.init(self, options)
if not self.options.icon then
self.options.icon = '' -- e0a0
end
git_branch.init()
end
M.update_status = function(_, is_focused)
return git_branch.get_branch((not is_focused and vim.fn.bufnr()))
end
return M

View File

@ -1,270 +0,0 @@
-- Copyright (c) 2020-2021 shadmansaleh
-- MIT license, see LICENSE for more details.
local Buffers = require('lualine.component'):new()
local highlight = require 'lualine.highlight'
local default_options = {
show_filename_only = true,
show_modified_status = true,
max_length = 0,
filetype_names = {
TelescopePrompt = 'Telescope',
dashboard = 'Dashboard',
packer = 'Packer',
fzf = 'FZF',
alpha = 'Alpha',
},
buffers_color = {
active = nil,
inactive = nil,
},
}
local function get_hl(section, is_active)
local suffix = is_active and '_normal' or '_inactive'
local section_redirects = {
lualine_x = 'lualine_c',
lualine_y = 'lualine_b',
lualine_z = 'lualine_a',
}
if section_redirects[section] then
section = highlight.highlight_exists(section .. suffix) and section or section_redirects[section]
end
return section .. suffix
end
local Buffer = {}
function Buffer:new(buffer)
assert(buffer.bufnr, 'Cannot create Buffer without bufnr')
local newObj = { bufnr = buffer.bufnr, options = buffer.options, highlights = buffer.highlights }
self.__index = self
newObj = setmetatable(newObj, self)
newObj:get_props()
return newObj
end
function Buffer:get_props()
self.file = vim.fn.bufname(self.bufnr)
self.buftype = vim.api.nvim_buf_get_option(self.bufnr, 'buftype')
self.filetype = vim.api.nvim_buf_get_option(self.bufnr, 'filetype')
local modified = self.options.show_modified_status and vim.api.nvim_buf_get_option(self.bufnr, 'modified')
local modified_icon = self.options.icons_enabled and '' or ' +'
self.modified_icon = modified and modified_icon or ''
self.icon = ''
if self.options.icons_enabled then
local dev
local status, _ = pcall(require, 'nvim-web-devicons')
if not status then
dev, _ = '', ''
elseif self.filetype == 'TelescopePrompt' then
dev, _ = require('nvim-web-devicons').get_icon 'telescope'
elseif self.filetype == 'fugitive' then
dev, _ = require('nvim-web-devicons').get_icon 'git'
elseif self.filetype == 'vimwiki' then
dev, _ = require('nvim-web-devicons').get_icon 'markdown'
elseif self.buftype == 'terminal' then
dev, _ = require('nvim-web-devicons').get_icon 'zsh'
elseif vim.fn.isdirectory(self.file) == 1 then
dev, _ = '', nil
else
dev, _ = require('nvim-web-devicons').get_icon(self.file, vim.fn.expand('#' .. self.bufnr .. ':e'))
end
if dev then
self.icon = dev .. ' '
end
end
return self
end
function Buffer:render()
local name
if self.ellipse then
name = '...'
else
name = string.format(' %s%s%s ', self.icon, self:name(), self.modified_icon)
end
self.len = vim.fn.strchars(name)
local line = string.format('%%%s@LualineSwitchBuffer@%s%%T', self.bufnr, name)
line = highlight.component_format_highlight(self.highlights[(self.current and 'active' or 'inactive')]) .. line
if self.options.self.section < 'lualine_x' and not self.first then
local sep_before = self:separator_before()
line = sep_before .. line
self.len = self.len + vim.fn.strchars(sep_before)
elseif self.options.self.section >= 'lualine_x' and not self.last then
local sep_after = self:separator_after()
line = line .. sep_after
self.len = self.len + vim.fn.strchars(sep_after)
end
return line
end
function Buffer:separator_before()
if self.current or self.aftercurrent then
return '%S{' .. self.options.section_separators.left .. '}'
else
return self.options.component_separators.left
end
end
function Buffer:separator_after()
if self.current or self.beforecurrent then
return '%s{' .. self.options.section_separators.right .. '}'
else
return self.options.component_separators.right
end
end
function Buffer:name()
if self.options.filetype_names[self.filetype] then
return self.options.filetype_names[self.filetype]
elseif self.buftype == 'help' then
return 'help:' .. vim.fn.fnamemodify(self.file, ':t:r')
elseif self.buftype == 'terminal' then
local match = string.match(vim.split(self.file, ' ')[1], 'term:.*:(%a+)')
return match ~= nil and match or vim.fn.fnamemodify(vim.env.SHELL, ':t')
elseif vim.fn.isdirectory(self.file) == 1 then
return vim.fn.fnamemodify(self.file, ':p:.')
elseif self.file == '' then
return '[No Name]'
end
return self.options.show_filename_only and vim.fn.fnamemodify(self.file, ':t')
or vim.fn.pathshorten(vim.fn.fnamemodify(self.file, ':p:.'))
end
function Buffers:new(options, child)
local newObj = self._parent:new(options, child or Buffers)
default_options.buffers_color = {
active = get_hl(options.self.section, true),
inactive = get_hl(options.self.section, false),
}
newObj.options = vim.tbl_deep_extend('keep', newObj.options or {}, default_options)
newObj.highlights = {
active = highlight.create_component_highlight_group(
newObj.options.buffers_color.active,
'buffers_active',
newObj.options
),
inactive = highlight.create_component_highlight_group(
newObj.options.buffers_color.inactive,
'buffers_active',
newObj.options
),
}
return newObj
end
function Buffers:update_status()
local data = {}
local buffers = {}
for b = 1, vim.fn.bufnr '$' do
if vim.fn.buflisted(b) ~= 0 and vim.api.nvim_buf_get_option(b, 'buftype') ~= 'quickfix' then
buffers[#buffers + 1] = Buffer:new { bufnr = b, options = self.options, highlights = self.highlights }
end
end
local current_bufnr = vim.fn.bufnr()
local current = -2
if buffers[1] then
buffers[1].first = true
end
if buffers[#buffers] then
buffers[#buffers].last = true
end
for i, buffer in ipairs(buffers) do
if buffer.bufnr == current_bufnr then
buffer.current = true
current = i
end
end
if buffers[current - 1] then
buffers[current - 1].beforecurrent = true
end
if buffers[current + 1] then
buffers[current + 1].aftercurrent = true
end
local max_length = self.options.max_length
if max_length == 0 then
max_length = math.floor(2 * vim.o.columns / 3)
end
local total_length
for i, buffer in pairs(buffers) do
if buffer.current then
current = i
end
end
if current == -2 then
local b = Buffer:new { bufnr = vim.fn.bufnr(), options = self.options, highlights = self.highlights }
b.current = true
if self.options.self.section < 'lualine_x' then
b.last = true
if #buffers > 0 then
buffers[#buffers].last = nil
end
buffers[#buffers + 1] = b
current = #buffers
else
b.first = true
if #buffers > 0 then
buffers[1].first = nil
end
table.insert(buffers, 1, b)
current = 1
end
end
local current_buffer = buffers[current]
data[#data + 1] = current_buffer:render()
total_length = current_buffer.len
local i = 0
local before, after
while true do
i = i + 1
before = buffers[current - i]
after = buffers[current + i]
local rendered_before, rendered_after
if before == nil and after == nil then
break
end
if before then
rendered_before = before:render()
total_length = total_length + before.len
end
if after then
rendered_after = after:render()
total_length = total_length + after.len
end
if total_length > max_length then
break
end
if before then
table.insert(data, 1, rendered_before)
end
if after then
data[#data + 1] = rendered_after
end
end
if total_length > max_length then
if before ~= nil then
before.ellipse = true
before.first = true
table.insert(data, 1, before:render())
end
if after ~= nil then
after.ellipse = true
after.last = true
data[#data + 1] = after:render()
end
end
return table.concat(data)
end
vim.cmd [[
function! LualineSwitchBuffer(bufnr, mouseclicks, mousebutton, modifiers)
execute ":buffer " . a:bufnr
endfunction
]]
return Buffers

View File

@ -0,0 +1,103 @@
local highlight = require 'lualine.highlight'
local Buffer = require('lualine.utils.class'):extend()
function Buffer:init(opts)
assert(opts.bufnr, 'Cannot create Buffer without bufnr')
self.bufnr = opts.bufnr
self.options = opts.options
self.highlights = opts.highlights
self:get_props()
end
function Buffer:get_props()
self.file = vim.fn.bufname(self.bufnr)
self.buftype = vim.api.nvim_buf_get_option(self.bufnr, 'buftype')
self.filetype = vim.api.nvim_buf_get_option(self.bufnr, 'filetype')
local modified = self.options.show_modified_status and vim.api.nvim_buf_get_option(self.bufnr, 'modified')
local modified_icon = self.options.icons_enabled and '' or ' +'
self.modified_icon = modified and modified_icon or ''
self.icon = ''
if self.options.icons_enabled then
local dev
local status, _ = pcall(require, 'nvim-web-devicons')
if not status then
dev, _ = '', ''
elseif self.filetype == 'TelescopePrompt' then
dev, _ = require('nvim-web-devicons').get_icon 'telescope'
elseif self.filetype == 'fugitive' then
dev, _ = require('nvim-web-devicons').get_icon 'git'
elseif self.filetype == 'vimwiki' then
dev, _ = require('nvim-web-devicons').get_icon 'markdown'
elseif self.buftype == 'terminal' then
dev, _ = require('nvim-web-devicons').get_icon 'zsh'
elseif vim.fn.isdirectory(self.file) == 1 then
dev, _ = '', nil
else
dev, _ = require('nvim-web-devicons').get_icon(self.file, vim.fn.expand('#' .. self.bufnr .. ':e'))
end
if dev then
self.icon = dev .. ' '
end
end
return self
end
function Buffer:render()
local name
if self.ellipse then
name = '...'
else
name = string.format(' %s%s%s ', self.icon, self:name(), self.modified_icon)
end
self.len = vim.fn.strchars(name)
local line = string.format('%%%s@LualineSwitchBuffer@%s%%T', self.bufnr, name)
line = highlight.component_format_highlight(self.highlights[(self.current and 'active' or 'inactive')]) .. line
if self.options.self.section < 'lualine_x' and not self.first then
local sep_before = self:separator_before()
line = sep_before .. line
self.len = self.len + vim.fn.strchars(sep_before)
elseif self.options.self.section >= 'lualine_x' and not self.last then
local sep_after = self:separator_after()
line = line .. sep_after
self.len = self.len + vim.fn.strchars(sep_after)
end
return line
end
function Buffer:separator_before()
if self.current or self.aftercurrent then
return '%S{' .. self.options.section_separators.left .. '}'
else
return self.options.component_separators.left
end
end
function Buffer:separator_after()
if self.current or self.beforecurrent then
return '%s{' .. self.options.section_separators.right .. '}'
else
return self.options.component_separators.right
end
end
function Buffer:name()
if self.options.filetype_names[self.filetype] then
return self.options.filetype_names[self.filetype]
elseif self.buftype == 'help' then
return 'help:' .. vim.fn.fnamemodify(self.file, ':t:r')
elseif self.buftype == 'terminal' then
local match = string.match(vim.split(self.file, ' ')[1], 'term:.*:(%a+)')
return match ~= nil and match or vim.fn.fnamemodify(vim.env.SHELL, ':t')
elseif vim.fn.isdirectory(self.file) == 1 then
return vim.fn.fnamemodify(self.file, ':p:.')
elseif self.file == '' then
return '[No Name]'
end
return self.options.show_filename_only and vim.fn.fnamemodify(self.file, ':t')
or vim.fn.pathshorten(vim.fn.fnamemodify(self.file, ':p:.'))
end
return Buffer

View File

@ -0,0 +1,170 @@
-- Copyright (c) 2020-2021 shadmansaleh
-- MIT license, see LICENSE for more details.
local require = require('lualine_require').require
local Buffer = require 'lualine.components.buffers.buffer'
local M = require('lualine.component'):extend()
local highlight = require 'lualine.highlight'
local default_options = {
show_filename_only = true,
show_modified_status = true,
max_length = 0,
filetype_names = {
TelescopePrompt = 'Telescope',
dashboard = 'Dashboard',
packer = 'Packer',
fzf = 'FZF',
alpha = 'Alpha',
},
buffers_color = {
active = nil,
inactive = nil,
},
}
local function get_hl(section, is_active)
local suffix = is_active and '_normal' or '_inactive'
local section_redirects = {
lualine_x = 'lualine_c',
lualine_y = 'lualine_b',
lualine_z = 'lualine_a',
}
if section_redirects[section] then
section = highlight.highlight_exists(section .. suffix) and section or section_redirects[section]
end
return section .. suffix
end
function M:init(options)
M.super.init(self, options)
default_options.buffers_color = {
active = get_hl(options.self.section, true),
inactive = get_hl(options.self.section, false),
}
self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options)
self.highlights = {
active = highlight.create_component_highlight_group(
self.options.buffers_color.active,
'buffers_active',
self.options
),
inactive = highlight.create_component_highlight_group(
self.options.buffers_color.inactive,
'buffers_active',
self.options
),
}
end
function M:update_status()
local data = {}
local buffers = {}
for b = 1, vim.fn.bufnr '$' do
if vim.fn.buflisted(b) ~= 0 and vim.api.nvim_buf_get_option(b, 'buftype') ~= 'quickfix' then
buffers[#buffers + 1] = Buffer { bufnr = b, options = self.options, highlights = self.highlights }
end
end
local current_bufnr = vim.fn.bufnr()
local current = -2
if buffers[1] then
buffers[1].first = true
end
if buffers[#buffers] then
buffers[#buffers].last = true
end
for i, buffer in ipairs(buffers) do
if buffer.bufnr == current_bufnr then
buffer.current = true
current = i
end
end
if buffers[current - 1] then
buffers[current - 1].beforecurrent = true
end
if buffers[current + 1] then
buffers[current + 1].aftercurrent = true
end
local max_length = self.options.max_length
if max_length == 0 then
max_length = math.floor(2 * vim.o.columns / 3)
end
local total_length
for i, buffer in pairs(buffers) do
if buffer.current then
current = i
end
end
if current == -2 then
local b = Buffer { bufnr = vim.fn.bufnr(), options = self.options, highlights = self.highlights }
b.current = true
if self.options.self.section < 'lualine_x' then
b.last = true
if #buffers > 0 then
buffers[#buffers].last = nil
end
buffers[#buffers + 1] = b
current = #buffers
else
b.first = true
if #buffers > 0 then
buffers[1].first = nil
end
table.insert(buffers, 1, b)
current = 1
end
end
local current_buffer = buffers[current]
data[#data + 1] = current_buffer:render()
total_length = current_buffer.len
local i = 0
local before, after
while true do
i = i + 1
before = buffers[current - i]
after = buffers[current + i]
local rendered_before, rendered_after
if before == nil and after == nil then
break
end
if before then
rendered_before = before:render()
total_length = total_length + before.len
end
if after then
rendered_after = after:render()
total_length = total_length + after.len
end
if total_length > max_length then
break
end
if before then
table.insert(data, 1, rendered_before)
end
if after then
data[#data + 1] = rendered_after
end
end
if total_length > max_length then
if before ~= nil then
before.ellipse = true
before.first = true
table.insert(data, 1, before:render())
end
if after ~= nil then
after.ellipse = true
after.last = true
data[#data + 1] = after:render()
end
end
return table.concat(data)
end
vim.cmd [[
function! LualineSwitchBuffer(bufnr, mouseclicks, mousebutton, modifiers)
execute ":buffer " . a:bufnr
endfunction
]]
return M

View File

@ -1,248 +0,0 @@
-- Copyright (c) 2020-2021 hoob3rt
-- MIT license, see LICENSE for more details.
local lualine_require = require 'lualine_require'
local modules = lualine_require.lazy_require {
highlight = 'lualine.highlight',
utils = 'lualine.utils.utils',
utils_notices = 'lualine.utils.notices',
}
local Diagnostics = lualine_require.require('lualine.component'):new()
local function check_deprecated_options(options)
if options.color_error or options.color_warn or options.color_info or options.color_hint then
options.diagnostics_color = options.diagnostics_color or {}
require('lualine.utils.notices').add_notice(string.format [[
### diagnostics.options.colors
Previously colors in diagnostics section was set with color_error, color_warning..
separate options . They've been unified under diagnostics_color options.
Now it should be something like:
```lua
{ 'diagnostics',
sources = {'nvim_lsp'},
diagnostics_color = {
error = color_error,
warning = color_warning,
info = color_info,
hint = color_hint,
}
}
```
]])
options.diagnostics_color.error = options.color_error
options.diagnostics_color.warning = options.color_warning
options.diagnostics_color.info = options.color_info
options.diagnostics_color.hint = options.color_hint
end
end
local default_symbols = {
icons = {
error = '', -- xf659
warn = '', -- xf529
info = '', -- xf7fc
hint = '', -- xf838
},
no_icons = { error = 'E:', warn = 'W:', info = 'I:', hint = 'H:' },
}
local default_options = {
colored = true,
update_in_insert = false,
sources = { 'nvim_lsp', 'coc' },
sections = { 'error', 'warn', 'info', 'hint' },
diagnostics_color = {
error = {
fg = modules.utils.extract_color_from_hllist(
'fg',
{ 'DiagnosticError', 'LspDiagnosticsDefaultError', 'DiffDelete' },
'#e32636'
),
},
warn = {
fg = modules.utils.extract_color_from_hllist(
'fg',
{ 'DiagnosticWarn', 'LspDiagnosticsDefaultWarning', 'DiffText' },
'#ffa500'
),
},
info = {
fg = modules.utils.extract_color_from_hllist(
'fg',
{ 'DiagnosticInfo', 'LspDiagnosticsDefaultInformation', 'Normal' },
'#ffffff'
),
},
hint = {
fg = modules.utils.extract_color_from_hllist(
'fg',
{ 'DiagnosticHint', 'LspDiagnosticsDefaultHint', 'DiffChange' },
'#273faf'
),
},
},
}
-- Initializer
Diagnostics.new = function(self, options, child)
-- Run super()
local new_diagnostics = self._parent:new(options, child or Diagnostics)
-- Apply default options
new_diagnostics.options = vim.tbl_deep_extend('keep', new_diagnostics.options or {}, default_options)
check_deprecated_options(new_diagnostics.options)
-- Apply default symbols
new_diagnostics.symbols = vim.tbl_extend(
'keep',
new_diagnostics.options.symbols or {},
new_diagnostics.options.icons_enabled ~= false and default_symbols.icons or default_symbols.no_icons
)
-- Initialize highlight groups
if new_diagnostics.options.colored then
new_diagnostics.highlight_groups = {
error = modules.highlight.create_component_highlight_group(
new_diagnostics.options.diagnostics_color.error,
'diagnostics_error',
new_diagnostics.options
),
warn = modules.highlight.create_component_highlight_group(
new_diagnostics.options.diagnostics_color.warn,
'diagnostics_warn',
new_diagnostics.options
),
info = modules.highlight.create_component_highlight_group(
new_diagnostics.options.diagnostics_color.info,
'diagnostics_info',
new_diagnostics.options
),
hint = modules.highlight.create_component_highlight_group(
new_diagnostics.options.diagnostics_color.hint,
'diagnostics_hint',
new_diagnostics.options
),
}
end
-- Error out no source
if #new_diagnostics.options.sources < 1 then
print 'no sources for diagnostics configured'
return ''
end
-- Initialize variable to store last update so we can use it in insert
-- mode for no update_in_insert
new_diagnostics.last_update = ''
return new_diagnostics
end
Diagnostics.update_status = function(self)
if not self.options.update_in_insert and vim.api.nvim_get_mode().mode:sub(1, 1) == 'i' then
return self.last_update
end
local error_count, warning_count, info_count, hint_count = 0, 0, 0, 0
local diagnostic_data = self.get_diagnostics(self.options.sources)
for _, data in pairs(diagnostic_data) do
error_count = error_count + data.error
warning_count = warning_count + data.warn
info_count = info_count + data.info
hint_count = hint_count + data.hint
end
local result = {}
local data = {
error = error_count,
warn = warning_count,
info = info_count,
hint = hint_count,
}
if self.options.colored then
local colors = {}
for name, hl in pairs(self.highlight_groups) do
colors[name] = modules.highlight.component_format_highlight(hl)
end
for _, section in ipairs(self.options.sections) do
if data[section] ~= nil and data[section] > 0 then
table.insert(result, colors[section] .. self.symbols[section] .. data[section])
end
end
else
for _, section in ipairs(self.options.sections) do
if data[section] ~= nil and data[section] > 0 then
table.insert(result, self.symbols[section] .. data[section])
end
end
end
self.last_update = ''
if result[1] ~= nil then
self.last_update = table.concat(result, ' ')
end
return self.last_update
end
Diagnostics.diagnostic_sources = {
nvim_lsp = function()
local error_count = vim.lsp.diagnostic.get_count(0, 'Error')
local warning_count = vim.lsp.diagnostic.get_count(0, 'Warning')
local info_count = vim.lsp.diagnostic.get_count(0, 'Information')
local hint_count = vim.lsp.diagnostic.get_count(0, 'Hint')
return error_count, warning_count, info_count, hint_count
end,
nvim = function()
local diagnostics = vim.diagnostic.get(0)
local count = { 0, 0, 0, 0 }
for _, diagnostic in ipairs(diagnostics) do
count[diagnostic.severity] = count[diagnostic.severity] + 1
end
return count[vim.diagnostic.severity.ERROR],
count[vim.diagnostic.severity.WARN],
count[vim.diagnostic.severity.INFO],
count[vim.diagnostic.severity.HINT]
end,
coc = function()
local data = vim.b.coc_diagnostic_info
if data then
return data.error, data.warning, data.information, data.hint
else
return 0, 0, 0, 0
end
end,
ale = function()
local ok, data = pcall(vim.fn['ale#statusline#Count'], vim.fn.bufnr())
if ok then
return data.error + data.style_error, data.warning + data.style_warning, data.info, 0
else
return 0, 0, 0, 0
end
end,
vim_lsp = function()
local ok, data = pcall(vim.fn['lsp#get_buffer_diagnostics_counts'])
if ok then
return data.error, data.warning, data.information
else
return 0, 0, 0
end
end,
}
Diagnostics.get_diagnostics = function(sources)
local result = {}
for index, source in ipairs(sources) do
if type(source) == 'string' then
local error_count, warning_count, info_count, hint_count = Diagnostics.diagnostic_sources[source]()
result[index] = {
error = error_count,
warn = warning_count,
info = info_count,
hint = hint_count,
}
elseif type(source) == 'function' then
local source_result = source()
source_result = type(source_result) == 'table' and source_result or {}
result[index] = {
error = source_result.error or 0,
warn = source_result.warn or 0,
info = source_result.info or 0,
hint = source_result.hint or 0,
}
end
end
return result
end
return Diagnostics

View File

@ -0,0 +1,52 @@
local require = require('lualine_require').require
local utils = require 'lualine.utils.utils'
local M = {}
M.symbols = {
icons = {
error = '', -- xf659
warn = '', -- xf529
info = '', -- xf7fc
hint = '', -- xf838
},
no_icons = { error = 'E:', warn = 'W:', info = 'I:', hint = 'H:' },
}
M.options = {
colored = true,
update_in_insert = false,
sources = { 'nvim_lsp', 'coc' },
sections = { 'error', 'warn', 'info', 'hint' },
diagnostics_color = {
error = {
fg = utils.extract_color_from_hllist(
'fg',
{ 'DiagnosticError', 'LspDiagnosticsDefaultError', 'DiffDelete' },
'#e32636'
),
},
warn = {
fg = utils.extract_color_from_hllist(
'fg',
{ 'DiagnosticWarn', 'LspDiagnosticsDefaultWarning', 'DiffText' },
'#ffa500'
),
},
info = {
fg = utils.extract_color_from_hllist(
'fg',
{ 'DiagnosticInfo', 'LspDiagnosticsDefaultInformation', 'Normal' },
'#ffffff'
),
},
hint = {
fg = utils.extract_color_from_hllist(
'fg',
{ 'DiagnosticHint', 'LspDiagnosticsDefaultHint', 'DiffChange' },
'#273faf'
),
},
},
}
return M

View File

@ -0,0 +1,109 @@
-- Copyright (c) 2020-2021 hoob3rt
-- MIT license, see LICENSE for more details.
local lualine_require = require 'lualine_require'
local modules = lualine_require.lazy_require {
default_config = 'lualine.components.diagnostics.config',
sources = 'lualine.components.diagnostics.sources',
highlight = 'lualine.highlight',
utils = 'lualine.utils.utils',
utils_notices = 'lualine.utils.notices',
}
local M = lualine_require.require('lualine.component'):extend()
M.diagnostics_sources = modules.sources.sources
M.get_diagnostics = modules.sources.get_diagnostics
-- Initializer
function M:init(options)
-- Run super()
M.super.init(self, options)
-- Apply default options
self.options = vim.tbl_deep_extend('keep', self.options or {}, modules.default_config.options)
-- Apply default symbols
self.symbols = vim.tbl_extend(
'keep',
self.options.symbols or {},
self.options.icons_enabled ~= false and modules.default_config.symbols.icons
or modules.default_config.symbols.no_icons
)
-- Initialize highlight groups
if self.options.colored then
self.highlight_groups = {
error = modules.highlight.create_component_highlight_group(
self.options.diagnostics_color.error,
'diagnostics_error',
self.options
),
warn = modules.highlight.create_component_highlight_group(
self.options.diagnostics_color.warn,
'diagnostics_warn',
self.options
),
info = modules.highlight.create_component_highlight_group(
self.options.diagnostics_color.info,
'diagnostics_info',
self.options
),
hint = modules.highlight.create_component_highlight_group(
self.options.diagnostics_color.hint,
'diagnostics_hint',
self.options
),
}
end
-- Error out no source
if #self.options.sources < 1 then
print 'no sources for diagnostics configured'
return ''
end
-- Initialize variable to store last update so we can use it in insert
-- mode for no update_in_insert
self.last_update = ''
end
function M:update_status()
if not self.options.update_in_insert and vim.api.nvim_get_mode().mode:sub(1, 1) == 'i' then
return self.last_update
end
local error_count, warning_count, info_count, hint_count = 0, 0, 0, 0
local diagnostic_data = modules.sources.get_diagnostics(self.options.sources)
for _, data in pairs(diagnostic_data) do
error_count = error_count + data.error
warning_count = warning_count + data.warn
info_count = info_count + data.info
hint_count = hint_count + data.hint
end
local result = {}
local data = {
error = error_count,
warn = warning_count,
info = info_count,
hint = hint_count,
}
if self.options.colored then
local colors = {}
for name, hl in pairs(self.highlight_groups) do
colors[name] = modules.highlight.component_format_highlight(hl)
end
for _, section in ipairs(self.options.sections) do
if data[section] ~= nil and data[section] > 0 then
table.insert(result, colors[section] .. self.symbols[section] .. data[section])
end
end
else
for _, section in ipairs(self.options.sections) do
if data[section] ~= nil and data[section] > 0 then
table.insert(result, self.symbols[section] .. data[section])
end
end
end
self.last_update = ''
if result[1] ~= nil then
self.last_update = table.concat(result, ' ')
end
return self.last_update
end
return M

View File

@ -0,0 +1,73 @@
local M = {}
M.sources = {
nvim_lsp = function()
local error_count = vim.lsp.diagnostic.get_count(0, 'Error')
local warning_count = vim.lsp.diagnostic.get_count(0, 'Warning')
local info_count = vim.lsp.diagnostic.get_count(0, 'Information')
local hint_count = vim.lsp.diagnostic.get_count(0, 'Hint')
return error_count, warning_count, info_count, hint_count
end,
nvim = function()
local diagnostics = vim.diagnostic.get(0)
local count = { 0, 0, 0, 0 }
for _, diagnostic in ipairs(diagnostics) do
count[diagnostic.severity] = count[diagnostic.severity] + 1
end
return count[vim.diagnostic.severity.ERROR],
count[vim.diagnostic.severity.WARN],
count[vim.diagnostic.severity.INFO],
count[vim.diagnostic.severity.HINT]
end,
coc = function()
local data = vim.b.coc_diagnostic_info
if data then
return data.error, data.warning, data.information, data.hint
else
return 0, 0, 0, 0
end
end,
ale = function()
local ok, data = pcall(vim.fn['ale#statusline#Count'], vim.fn.bufnr())
if ok then
return data.error + data.style_error, data.warning + data.style_warning, data.info, 0
else
return 0, 0, 0, 0
end
end,
vim_lsp = function()
local ok, data = pcall(vim.fn['lsp#get_buffer_diagnostics_counts'])
if ok then
return data.error, data.warning, data.information
else
return 0, 0, 0
end
end,
}
M.get_diagnostics = function(sources)
local result = {}
for index, source in ipairs(sources) do
if type(source) == 'string' then
local error_count, warning_count, info_count, hint_count = M.sources[source]()
result[index] = {
error = error_count,
warn = warning_count,
info = info_count,
hint = hint_count,
}
elseif type(source) == 'function' then
local source_result = source()
source_result = type(source_result) == 'table' and source_result or {}
result[index] = {
error = source_result.error or 0,
warn = source_result.warn or 0,
info = source_result.info or 0,
hint = source_result.hint or 0,
}
end
end
return result
end
return M

View File

@ -1,244 +0,0 @@
-- Copyright (c) 2020-2021 shadmansaleh
-- MIT license, see LICENSE for more details.
local lualine_require = require 'lualine_require'
local modules = lualine_require.lazy_require {
utils = 'lualine.utils.utils',
utils_notices = 'lualine.utils.notices',
highlight = 'lualine.highlight',
Job = 'lualine.utils.job',
}
local Diff = lualine_require.require('lualine.component'):new()
local function check_deprecated_options(options)
if options.color_added or options.color_modified or options.color_removed then
options.diagnostics_color = options.diagnostics_color or {}
require('lualine.utils.notices').add_notice(string.format [[
### diff.options.colors
Previously colors in diff section was set with color_added, color_modified..
separate options . They've been unified under diff_color option.
Now it should be something like:
```lua
{ 'diff',
diff_color = {
added = color_added,
modified = color_modified,
removed = color_removed,
}
}
```
]])
options.diff_color.added = options.color_added
options.diff_color.modified = options.color_modified
options.diff_color.removed = options.color_removed
end
end
-- Vars
-- variable to store git diff stats
Diff.git_diff = nil
-- accumulates output from diff process
Diff.diff_output_cache = {}
-- variable to store git_diff job
Diff.diff_job = nil
Diff.active_bufnr = '0'
local diff_cache = {} -- Stores last known value of diff of a buffer
local default_options = {
colored = true,
symbols = { added = '+', modified = '~', removed = '-' },
diff_color = {
added = {
fg = modules.utils.extract_highlight_colors('DiffAdd', 'fg') or '#f0e130',
},
modified = {
fg = modules.utils.extract_highlight_colors('DiffChange', 'fg') or '#ff0038',
},
removed = {
fg = modules.utils.extract_highlight_colors('DiffDelete', 'fg') or '#ff0038',
},
},
}
-- Initializer
Diff.new = function(self, options, child)
local new_instance = self._parent:new(options, child or Diff)
new_instance.options = vim.tbl_deep_extend('keep', new_instance.options or {}, default_options)
check_deprecated_options(new_instance.options)
-- create highlights and save highlight_name in highlights table
if new_instance.options.colored then
new_instance.highlights = {
added = modules.highlight.create_component_highlight_group(
new_instance.options.diff_color.added,
'diff_added',
new_instance.options
),
modified = modules.highlight.create_component_highlight_group(
new_instance.options.diff_color.modified,
'diff_modified',
new_instance.options
),
removed = modules.highlight.create_component_highlight_group(
new_instance.options.diff_color.removed,
'diff_removed',
new_instance.options
),
}
end
Diff.diff_checker_enabled = type(new_instance.options.source) ~= 'function'
if Diff.diff_checker_enabled then
-- setup internal source
modules.utils.define_autocmd('BufEnter', "lua require'lualine.components.diff'.update_diff_args()")
modules.utils.define_autocmd('BufWritePost', "lua require'lualine.components.diff'.update_git_diff()")
Diff.update_diff_args()
end
return new_instance
end
-- Function that runs everytime statusline is updated
Diff.update_status = function(self, is_focused)
local git_diff
if Diff.diff_checker_enabled then
if Diff.active_bufnr ~= vim.g.actual_curbuf then
-- Workaround for https://github.com/hoob3rt/lualine.nvim/issues/286
-- See upstream issue https://github.com/neovim/neovim/issues/15300
-- Diff is out of sync re sync it.
Diff.update_diff_args()
end
git_diff = Diff.git_diff
else
git_diff = self.options.source()
end
if not is_focused then
git_diff = diff_cache[vim.fn.bufnr()] or {}
end
if git_diff == nil then
return ''
end
local colors = {}
if self.options.colored then
-- load the highlights and store them in colors table
for name, highlight_name in pairs(self.highlights) do
colors[name] = modules.highlight.component_format_highlight(highlight_name)
end
end
local result = {}
-- loop though data and load available sections in result table
for _, name in ipairs { 'added', 'modified', 'removed' } do
if git_diff[name] and git_diff[name] > 0 then
if self.options.colored then
table.insert(result, colors[name] .. self.options.symbols[name] .. git_diff[name])
else
table.insert(result, self.options.symbols[name] .. git_diff[name])
end
end
end
if #result > 0 then
return table.concat(result, ' ')
else
return ''
end
end
-- Api to get git sign count
-- scheme :
-- {
-- added = added_count,
-- modified = modified_count,
-- removed = removed_count,
-- }
-- error_code = { added = -1, modified = -1, removed = -1 }
function Diff.get_sign_count()
if Diff.diff_checker_enabled then
Diff.update_diff_args()
end
return Diff.git_diff or { added = -1, modified = -1, removed = -1 }
end
-- process diff data and update git_diff{ added, removed, modified }
function Diff.process_diff(data)
-- Adapted from https://github.com/wbthomason/nvim-vcs.lua
local added, removed, modified = 0, 0, 0
for _, line in ipairs(data) do
if string.find(line, [[^@@ ]]) then
local tokens = vim.fn.matchlist(line, [[^@@ -\v(\d+),?(\d*) \+(\d+),?(\d*)]])
local line_stats = {
mod_count = tokens[3] == nil and 0 or tokens[3] == '' and 1 or tonumber(tokens[3]),
new_count = tokens[5] == nil and 0 or tokens[5] == '' and 1 or tonumber(tokens[5]),
}
if line_stats.mod_count == 0 and line_stats.new_count > 0 then
added = added + line_stats.new_count
elseif line_stats.mod_count > 0 and line_stats.new_count == 0 then
removed = removed + line_stats.mod_count
else
local min = math.min(line_stats.mod_count, line_stats.new_count)
modified = modified + min
added = added + line_stats.new_count - min
removed = removed + line_stats.mod_count - min
end
end
end
Diff.git_diff = { added = added, modified = modified, removed = removed }
end
-- Updates the job args
function Diff.update_diff_args()
-- Donn't show git diff when current buffer doesn't have a filename
Diff.active_bufnr = tostring(vim.fn.bufnr())
if #vim.fn.expand '%' == 0 then
Diff.diff_args = nil
Diff.git_diff = nil
return
end
Diff.diff_args = {
cmd = string.format(
[[git -C %s --no-pager diff --no-color --no-ext-diff -U0 -- %s]],
vim.fn.expand '%:h',
vim.fn.expand '%:t'
),
on_stdout = function(_, data)
if next(data) then
Diff.diff_output_cache = vim.list_extend(Diff.diff_output_cache, data)
end
end,
on_stderr = function(_, data)
data = table.concat(data, '\n')
if #data > 1 or (#data == 1 and #data[1] > 0) then
Diff.git_diff = nil
Diff.diff_output_cache = {}
end
end,
on_exit = function()
if #Diff.diff_output_cache > 0 then
Diff.process_diff(Diff.diff_output_cache)
else
Diff.git_diff = { added = 0, modified = 0, removed = 0 }
end
diff_cache[vim.fn.bufnr()] = Diff.git_diff
end,
}
Diff.update_git_diff()
end
-- Update git_diff veriable
function Diff.update_git_diff()
if Diff.diff_args then
Diff.diff_output_cache = {}
if Diff.diff_job then
Diff.diff_job:stop()
end
Diff.diff_job = modules.Job(Diff.diff_args)
if Diff.diff_job then
Diff.diff_job:start()
end
end
end
return Diff

View File

@ -0,0 +1,135 @@
local lualine_require = require 'lualine_require'
local modules = lualine_require.lazy_require {
utils = 'lualine.utils.utils',
Job = 'lualine.utils.job',
}
local M = {}
-- Vars
-- variable to store git diff stats
local git_diff = nil
-- accumulates output from diff process
local diff_output_cache = {}
-- variable to store git_diff job
local diff_job = nil
local active_bufnr = '0'
local diff_cache = {} -- Stores last known value of diff of a buffer
-- initialize the module
function M.init(opts)
if type(opts.source) == 'function' then
M.src = opts.source
else
modules.utils.define_autocmd('BufEnter', "lua require'lualine.components.diff.git_diff'.update_diff_args()")
modules.utils.define_autocmd('BufWritePost', "lua require'lualine.components.diff.git_diff'.update_git_diff()")
M.update_diff_args()
end
end
-- Api to get git sign count
-- scheme :
-- {
-- added = added_count,
-- modified = modified_count,
-- removed = removed_count,
-- }
-- error_code = { added = -1, modified = -1, removed = -1 }
function M.get_sign_count(bufnr)
if bufnr then
return diff_cache[bufnr]
end
if M.src then
git_diff = M.src()
diff_cache[vim.fn.bufnr()] = git_diff
elseif vim.g.actual_curbuf ~= nil and active_bufnr ~= vim.g.actual_curbuf then
-- Workaround for https://github.com/hoob3rt/lualine.nvim/issues/286
-- See upstream issue https://github.com/neovim/neovim/issues/15300
-- Diff is out of sync re sync it.
M.update_diff_args()
end
return git_diff
end
-- process diff data and update git_diff{ added, removed, modified }
local function process_diff(data)
-- Adapted from https://github.com/wbthomason/nvim-vcs.lua
local added, removed, modified = 0, 0, 0
for _, line in ipairs(data) do
if string.find(line, [[^@@ ]]) then
local tokens = vim.fn.matchlist(line, [[^@@ -\v(\d+),?(\d*) \+(\d+),?(\d*)]])
local line_stats = {
mod_count = tokens[3] == nil and 0 or tokens[3] == '' and 1 or tonumber(tokens[3]),
new_count = tokens[5] == nil and 0 or tokens[5] == '' and 1 or tonumber(tokens[5]),
}
if line_stats.mod_count == 0 and line_stats.new_count > 0 then
added = added + line_stats.new_count
elseif line_stats.mod_count > 0 and line_stats.new_count == 0 then
removed = removed + line_stats.mod_count
else
local min = math.min(line_stats.mod_count, line_stats.new_count)
modified = modified + min
added = added + line_stats.new_count - min
removed = removed + line_stats.mod_count - min
end
end
end
git_diff = { added = added, modified = modified, removed = removed }
end
-- Updates the job args
function M.update_diff_args()
-- Donn't show git diff when current buffer doesn't have a filename
active_bufnr = tostring(vim.fn.bufnr())
if #vim.fn.expand '%' == 0 then
M.diff_args = nil
git_diff = nil
return
end
M.diff_args = {
cmd = string.format(
[[git -C %s --no-pager diff --no-color --no-ext-diff -U0 -- %s]],
vim.fn.expand '%:h',
vim.fn.expand '%:t'
),
on_stdout = function(_, data)
if next(data) then
diff_output_cache = vim.list_extend(diff_output_cache, data)
end
end,
on_stderr = function(_, data)
data = table.concat(data, '\n')
if #data > 1 or (#data == 1 and #data[1] > 0) then
git_diff = nil
diff_output_cache = {}
end
end,
on_exit = function()
if #diff_output_cache > 0 then
process_diff(diff_output_cache)
else
git_diff = { added = 0, modified = 0, removed = 0 }
end
diff_cache[vim.fn.bufnr()] = git_diff
end,
}
M.update_git_diff()
end
-- Update git_diff veriable
function M.update_git_diff()
if M.diff_args then
diff_output_cache = {}
if diff_job then
diff_job:stop()
end
diff_job = modules.Job(M.diff_args)
if diff_job then
diff_job:start()
end
end
end
return M

View File

@ -0,0 +1,88 @@
-- Copyright (c) 2020-2021 shadmansaleh
-- MIT license, see LICENSE for more details.
local lualine_require = require 'lualine_require'
local modules = lualine_require.lazy_require {
git_diff = 'lualine.components.diff.git_diff',
utils = 'lualine.utils.utils',
utils_notices = 'lualine.utils.notices',
highlight = 'lualine.highlight',
}
local M = lualine_require.require('lualine.component'):extend()
local default_options = {
colored = true,
symbols = { added = '+', modified = '~', removed = '-' },
diff_color = {
added = {
fg = modules.utils.extract_highlight_colors('DiffAdd', 'fg') or '#f0e130',
},
modified = {
fg = modules.utils.extract_highlight_colors('DiffChange', 'fg') or '#ff0038',
},
removed = {
fg = modules.utils.extract_highlight_colors('DiffDelete', 'fg') or '#ff0038',
},
},
}
-- Initializer
function M:init(options)
M.super.init(self, options)
self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options)
-- create highlights and save highlight_name in highlights table
if self.options.colored then
self.highlights = {
added = modules.highlight.create_component_highlight_group(
self.options.diff_color.added,
'diff_added',
self.options
),
modified = modules.highlight.create_component_highlight_group(
self.options.diff_color.modified,
'diff_modified',
self.options
),
removed = modules.highlight.create_component_highlight_group(
self.options.diff_color.removed,
'diff_removed',
self.options
),
}
end
modules.git_diff.init(self.options)
end
-- Function that runs everytime statusline is updated
function M:update_status(is_focused)
local git_diff = modules.git_diff.get_sign_count((not is_focused and vim.fn.bufnr()))
if git_diff == nil then
return ''
end
local colors = {}
if self.options.colored then
-- load the highlights and store them in colors table
for name, highlight_name in pairs(self.highlights) do
colors[name] = modules.highlight.component_format_highlight(highlight_name)
end
end
local result = {}
-- loop though data and load available sections in result table
for _, name in ipairs { 'added', 'modified', 'removed' } do
if git_diff[name] and git_diff[name] > 0 then
if self.options.colored then
table.insert(result, colors[name] .. self.options.symbols[name] .. git_diff[name])
else
table.insert(result, self.options.symbols[name] .. git_diff[name])
end
end
end
if #result > 0 then
return table.concat(result, ' ')
else
return ''
end
end
return M

View File

@ -1,9 +1,7 @@
-- Copyright (c) 2020-2021 hoob3rt
-- MIT license, see LICENSE for more details.
local Encoding = require('lualine.component'):new()
Encoding.update_status = function()
local function encoding()
return [[%{strlen(&fenc)?&fenc:&enc}]]
end
return Encoding
return encoding

View File

@ -1,20 +1,20 @@
-- Copyright (c) 2020-2021 shadmansaleh
-- MIT license, see LICENSE for more details.
local FileFormat = require('lualine.component'):new()
local M = require('lualine.component'):extend()
-- stylua: ignore
FileFormat.icon = {
M.icon = {
unix = '', -- e712
dos = '', -- e70f
mac = '' -- e711
}
FileFormat.update_status = function(self)
M.update_status = function(self)
if self.options.icons_enabled and not self.options.icon then
local format = vim.bo.fileformat
return FileFormat.icon[format] or format
return M.icon[format] or format
end
return vim.bo.fileformat
end
return FileFormat
return M

View File

@ -1,6 +1,6 @@
-- Copyright (c) 2020-2021 shadmansaleh
-- MIT license, see LICENSE for more details.
local FileName = require('lualine.component'):new()
local M = require('lualine.component'):extend()
local default_options = {
symbols = { modified = '[+]', readonly = '[-]' },
@ -18,13 +18,12 @@ local function shorten_path(path, sep)
return path:gsub(string.format('([^%s])[^%s]+%%%s', sep, sep, sep), '%1' .. sep, 1)
end
FileName.new = function(self, options, child)
local new_instance = self._parent:new(options, child or FileName)
new_instance.options = vim.tbl_deep_extend('keep', new_instance.options or {}, default_options)
return new_instance
M.init = function(self, options)
M.super.init(self, options)
self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options)
end
FileName.update_status = function(self)
M.update_status = function(self)
local data
if self.options.path == 1 then
-- relative path
@ -63,4 +62,4 @@ FileName.update_status = function(self)
return data
end
return FileName
return M

View File

@ -1,8 +1,6 @@
-- Copyright (c) 2020-2021 shadmansaleh
-- MIT license, see LICENSE for more details.
local M = require('lualine.component'):new()
M.update_status = function()
local function filesize()
local file = vim.fn.expand '%:p'
if file == nil or #file == 0 then
return ''
@ -24,4 +22,4 @@ M.update_status = function()
return string.format('%.1f%s', size, sufixes[i])
end
return M
return filesize

View File

@ -5,46 +5,23 @@ local modules = lualine_require.lazy_require {
highlight = 'lualine.highlight',
utils = 'lualine.utils.utils',
}
local FileType = lualine_require.require('lualine.component'):new()
local function check_deprecated_options(options)
local function rename_notice(before, now)
if options[before] then
require('lualine.utils.notices').add_notice(string.format(
[[
### option.%s
%s option has been renamed to `%s`. Please use `%s` instead in your config
for filetype component.
]],
before,
before,
now,
now
))
options[now] = options[before]
options[before] = nil
end
end
rename_notice('disable_text', 'icon_only')
end
local M = lualine_require.require('lualine.component'):extend()
local default_options = {
colored = true,
icon_only = false,
}
function FileType:new(options, child)
local new_instance = self._parent:new(options, child or FileType)
new_instance.options = vim.tbl_deep_extend('keep', new_instance.options or {}, default_options)
check_deprecated_options(new_instance.options)
return new_instance
function M:init(options)
M.super.init(self, options)
self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options)
end
function FileType.update_status()
function M.update_status()
return vim.bo.filetype or ''
end
function FileType:apply_icon()
function M:apply_icon()
if not self.options.icons_enabled then
return
end
@ -87,4 +64,4 @@ function FileType:apply_icon()
end
end
return FileType
return M

View File

@ -1,7 +1,7 @@
-- Copyright (c) 2020-2021 hoob3rt
-- MIT license, see LICENSE for more details.
local HostName = require('lualine.component'):new()
local function hostname()
return vim.loop.os_gethostname()
end
HostName.update_status = vim.loop.os_gethostname
return HostName
return hostname

View File

@ -1,9 +1,7 @@
-- Copyright (c) 2020-2021 hoob3rt
-- MIT license, see LICENSE for more details.
local Location = require('lualine.component'):new()
Location.update_status = function()
local function location()
return [[%3l:%-2c]]
end
return Location
return location

View File

@ -1,9 +1,4 @@
-- Copyright (c) 2020-2021 hoob3rt
-- MIT license, see LICENSE for more details.
local require = require('lualine_require').require
local Mode = require('lualine.component'):new()
local get_mode = require('lualine.utils.mode').get_mode
Mode.update_status = get_mode
return Mode
return get_mode

View File

@ -1,9 +1,7 @@
-- Copyright (c) 2020-2021 hoob3rt
-- MIT license, see LICENSE for more details.
local Progress = require('lualine.component'):new()
Progress.update_status = function()
local function progress()
return [[%3P]]
end
return Progress
return progress

View File

@ -1,36 +1,35 @@
-- Copyright (c) 2020-2021 shadmansaleh
-- MIT license, see LICENSE for more details.
local M = require('lualine.component'):extend()
local EvalFuncComponent = require('lualine.component'):new()
EvalFuncComponent.update_status = function(self)
function M:update_status()
local component = self.options[1]
local ok, status
if self.options.type == nil then
ok, status = pcall(EvalFuncComponent.lua_eval, component)
ok, status = pcall(M.lua_eval, component)
if not ok then
status = EvalFuncComponent.vim_function(component)
status = M.vim_function(component)
end
else
if self.options.type == 'luae' then
ok, status = pcall(EvalFuncComponent.lua_eval, component)
if self.options.type == 'lua_expr' then
ok, status = pcall(M.lua_eval, component)
if not ok then
status = nil
end
elseif self.options.type == 'vimf' then
status = EvalFuncComponent.vim_function(component)
elseif self.options.type == 'vim_fun' then
status = M.vim_function(component)
end
end
return status
end
EvalFuncComponent.lua_eval = function(code)
function M.lua_eval(code)
local result = loadstring('return ' .. code)()
assert(result, 'String expected got nil')
return tostring(result)
end
EvalFuncComponent.vim_function = function(name)
function M.vim_function(name)
-- vim function component
local ok, return_val = pcall(vim.api.nvim_call_function, name, {})
if not ok then
@ -40,4 +39,4 @@ EvalFuncComponent.vim_function = function(name)
return ok and return_val or ''
end
return EvalFuncComponent
return M

View File

@ -1,8 +1,8 @@
-- Copyright (c) 2020-2021 shadmansaleh
-- MIT license, see LICENSE for more details.
local FunctionComponent = require('lualine.component'):new()
local M = require('lualine.component'):extend()
FunctionComponent.update_status = function(self, is_focused)
M.update_status = function(self, is_focused)
-- 1st element in options table is the function provided by config
local ok, retval
ok, retval = pcall(self.options[1], self, is_focused)
@ -18,4 +18,4 @@ FunctionComponent.update_status = function(self, is_focused)
return retval
end
return FunctionComponent
return M

View File

@ -1,7 +1,8 @@
-- Copyright (c) 2020-2021 shadmansaleh
-- MIT license, see LICENSE for more details.
local VarComponent = require('lualine.component'):new()
VarComponent.update_status = function(self)
local M = require('lualine.component'):extend()
function M:update_status()
local component = self.options[1]
-- vim veriable component
-- accepts g:, v:, t:, w:, b:, o, go:, vo:, to:, wo:, bo:
@ -31,4 +32,4 @@ VarComponent.update_status = function(self)
return ok and return_val or ''
end
return VarComponent
return M

View File

@ -1,217 +0,0 @@
-- Copyright (c) 2020-2021 shadmansaleh
-- MIT license, see LICENSE for more details.
local Tabs = require('lualine.component'):new()
local highlight = require 'lualine.highlight'
local default_options = {
max_length = 0,
mode = 0,
tabs_color = {
active = nil,
inactive = nil,
},
}
local function get_hl(section, is_active)
local suffix = is_active and '_normal' or '_inactive'
local section_redirects = {
lualine_x = 'lualine_c',
lualine_y = 'lualine_b',
lualine_z = 'lualine_a',
}
if section_redirects[section] then
section = highlight.highlight_exists(section .. suffix) and section or section_redirects[section]
end
return section .. suffix
end
local Tab = {}
function Tab:new(tab)
assert(tab.tabnr, 'Cannot create Tab without tabnr')
local newObj = {
tabnr = tab.tabnr,
options = tab.options,
highlights = tab.highlights,
}
self.__index = self
newObj = setmetatable(newObj, self)
return newObj
end
function Tab:label()
local buflist = vim.fn.tabpagebuflist(self.tabnr)
local winnr = vim.fn.tabpagewinnr(self.tabnr)
local bufnr = buflist[winnr]
local file = vim.fn.bufname(bufnr)
local buftype = vim.fn.getbufvar(bufnr, '&buftype')
if buftype == 'help' then
return 'help:' .. vim.fn.fnamemodify(file, ':t:r')
elseif buftype == 'terminal' then
local match = string.match(vim.split(file, ' ')[1], 'term:.*:(%a+)')
return match ~= nil and match or vim.fn.fnamemodify(vim.env.SHELL, ':t')
elseif vim.fn.isdirectory(file) == 1 then
return vim.fn.fnamemodify(file, ':p:.')
elseif file == '' then
return '[No Name]'
end
return vim.fn.fnamemodify(file, ':t')
end
function Tab:render()
local name
if self.ellipse then
name = '...'
else
if self.options.mode == 0 then
name = string.format('%s%s ', (self.last or not self.first) and ' ' or '', tostring(self.tabnr))
elseif self.options.mode == 1 then
name = string.format('%s%s ', (self.last or not self.first) and ' ' or '', self:label())
else
name = string.format('%s%s %s ', (self.last or not self.first) and ' ' or '', tostring(self.tabnr), self:label())
end
end
self.len = #name
local line = string.format('%%%s@LualineSwitchTab@%s%%T', self.tabnr, name)
line = highlight.component_format_highlight(self.highlights[(self.current and 'active' or 'inactive')]) .. line
if self.options.self.section < 'lualine_x' and not self.first then
local sep_before = self:separator_before()
line = sep_before .. line
self.len = self.len + vim.fn.strchars(sep_before)
elseif self.options.self.section >= 'lualine_x' and not self.last then
local sep_after = self:separator_after()
line = line .. sep_after
self.len = self.len + vim.fn.strchars(sep_after)
end
return line
end
function Tab:separator_before()
if self.current or self.aftercurrent then
return '%S{' .. self.options.section_separators.left .. '}'
else
return self.options.component_separators.left
end
end
function Tab:separator_after()
if self.current or self.beforecurrent then
return '%s{' .. self.options.section_separators.right .. '}'
else
return self.options.component_separators.right
end
end
function Tabs:new(options, child)
local newObj = self._parent:new(options, child or Tabs)
default_options.tabs_color = {
active = get_hl(options.self.section, true),
inactive = get_hl(options.self.section, false),
}
newObj.options = vim.tbl_deep_extend('keep', newObj.options or {}, default_options)
-- stylua: ignore
newObj.highlights = {
active = highlight.create_component_highlight_group(
newObj.options.tabs_color.active,
'tabs_active',
newObj.options
),
inactive = highlight.create_component_highlight_group(
newObj.options.tabs_color.inactive,
'tabs_active',
newObj.options
),
}
return newObj
end
function Tabs:update_status()
local data = {}
local tabs = {}
for t = 1, vim.fn.tabpagenr '$' do
tabs[#tabs + 1] = Tab:new { tabnr = t, options = self.options, highlights = self.highlights }
end
local current = vim.fn.tabpagenr()
tabs[1].first = true
tabs[#tabs].last = true
if tabs[current] then
tabs[current].current = true
end
if tabs[current - 1] then
tabs[current - 1].beforecurrent = true
end
if tabs[current + 1] then
tabs[current + 1].aftercurrent = true
end
local max_length = self.options.max_length
if max_length == 0 then
max_length = math.floor(vim.o.columns / 3)
end
local total_length
for i, tab in pairs(tabs) do
if tab.current then
current = i
end
end
local current_tab = tabs[current]
if current_tab == nil then
local t = Tab:new { tabnr = vim.fn.tabpagenr(), options = self.options, highlights = self.highlights }
t.current = true
t.last = true
data[#data + 1] = t:render()
else
data[#data + 1] = current_tab:render()
total_length = current_tab.len
local i = 0
local before, after
while true do
i = i + 1
before = tabs[current - i]
after = tabs[current + i]
local rendered_before, rendered_after
if before == nil and after == nil then
break
end
if before then
rendered_before = before:render()
total_length = total_length + before.len
if total_length > max_length then
break
end
table.insert(data, 1, rendered_before)
end
if after then
rendered_after = after:render()
total_length = total_length + after.len
if total_length > max_length then
break
end
data[#data + 1] = rendered_after
end
end
if total_length > max_length then
if before ~= nil then
before.ellipse = true
before.first = true
table.insert(data, 1, before:render())
end
if after ~= nil then
after.ellipse = true
after.last = true
data[#data + 1] = after:render()
end
end
end
return table.concat(data)
end
vim.cmd [[
function! LualineSwitchTab(tabnr, mouseclicks, mousebutton, modifiers)
execute a:tabnr . "tabnext"
endfunction
]]
return Tabs

View File

@ -0,0 +1,140 @@
-- Copyright (c) 2020-2021 shadmansaleh
-- MIT license, see LICENSE for more details.
local require = require('lualine_require').require
local Tab = require 'lualine.components.tabs.tab'
local M = require('lualine.component'):extend()
local highlight = require 'lualine.highlight'
local default_options = {
max_length = 0,
mode = 0,
tabs_color = {
active = nil,
inactive = nil,
},
}
local function get_hl(section, is_active)
local suffix = is_active and '_normal' or '_inactive'
local section_redirects = {
lualine_x = 'lualine_c',
lualine_y = 'lualine_b',
lualine_z = 'lualine_a',
}
if section_redirects[section] then
section = highlight.highlight_exists(section .. suffix) and section or section_redirects[section]
end
return section .. suffix
end
function M:init(options)
M.super.init(self, options)
default_options.tabs_color = {
active = get_hl(options.self.section, true),
inactive = get_hl(options.self.section, false),
}
self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options)
-- stylua: ignore
self.highlights = {
active = highlight.create_component_highlight_group(
self.options.tabs_color.active,
'tabs_active',
self.options
),
inactive = highlight.create_component_highlight_group(
self.options.tabs_color.inactive,
'tabs_active',
self.options
),
}
end
function M:update_status()
local data = {}
local tabs = {}
for t = 1, vim.fn.tabpagenr '$' do
tabs[#tabs + 1] = Tab { tabnr = t, options = self.options, highlights = self.highlights }
end
local current = vim.fn.tabpagenr()
tabs[1].first = true
tabs[#tabs].last = true
if tabs[current] then
tabs[current].current = true
end
if tabs[current - 1] then
tabs[current - 1].beforecurrent = true
end
if tabs[current + 1] then
tabs[current + 1].aftercurrent = true
end
local max_length = self.options.max_length
if max_length == 0 then
max_length = math.floor(vim.o.columns / 3)
end
local total_length
for i, tab in pairs(tabs) do
if tab.current then
current = i
end
end
local current_tab = tabs[current]
if current_tab == nil then
local t = Tab { tabnr = vim.fn.tabpagenr(), options = self.options, highlights = self.highlights }
t.current = true
t.last = true
data[#data + 1] = t:render()
else
data[#data + 1] = current_tab:render()
total_length = current_tab.len
local i = 0
local before, after
while true do
i = i + 1
before = tabs[current - i]
after = tabs[current + i]
local rendered_before, rendered_after
if before == nil and after == nil then
break
end
if before then
rendered_before = before:render()
total_length = total_length + before.len
if total_length > max_length then
break
end
table.insert(data, 1, rendered_before)
end
if after then
rendered_after = after:render()
total_length = total_length + after.len
if total_length > max_length then
break
end
data[#data + 1] = rendered_after
end
end
if total_length > max_length then
if before ~= nil then
before.ellipse = true
before.first = true
table.insert(data, 1, before:render())
end
if after ~= nil then
after.ellipse = true
after.last = true
data[#data + 1] = after:render()
end
end
end
return table.concat(data)
end
vim.cmd [[
function! LualineSwitchTab(tabnr, mouseclicks, mousebutton, modifiers)
execute a:tabnr . "tabnext"
endfunction
]]
return M

View File

@ -0,0 +1,75 @@
local highlight = require 'lualine.highlight'
local Tab = require('lualine.utils.class'):extend()
function Tab:init(opts)
assert(opts.tabnr, 'Cannot create Tab without tabnr')
self.tabnr = opts.tabnr
self.options = opts.options
self.highlights = opts.highlights
end
function Tab:label()
local buflist = vim.fn.tabpagebuflist(self.tabnr)
local winnr = vim.fn.tabpagewinnr(self.tabnr)
local bufnr = buflist[winnr]
local file = vim.fn.bufname(bufnr)
local buftype = vim.fn.getbufvar(bufnr, '&buftype')
if buftype == 'help' then
return 'help:' .. vim.fn.fnamemodify(file, ':t:r')
elseif buftype == 'terminal' then
local match = string.match(vim.split(file, ' ')[1], 'term:.*:(%a+)')
return match ~= nil and match or vim.fn.fnamemodify(vim.env.SHELL, ':t')
elseif vim.fn.isdirectory(file) == 1 then
return vim.fn.fnamemodify(file, ':p:.')
elseif file == '' then
return '[No Name]'
end
return vim.fn.fnamemodify(file, ':t')
end
function Tab:render()
local name
if self.ellipse then
name = '...'
else
if self.options.mode == 0 then
name = string.format('%s%s ', (self.last or not self.first) and ' ' or '', tostring(self.tabnr))
elseif self.options.mode == 1 then
name = string.format('%s%s ', (self.last or not self.first) and ' ' or '', self:label())
else
name = string.format('%s%s %s ', (self.last or not self.first) and ' ' or '', tostring(self.tabnr), self:label())
end
end
self.len = #name
local line = string.format('%%%s@LualineSwitchTab@%s%%T', self.tabnr, name)
line = highlight.component_format_highlight(self.highlights[(self.current and 'active' or 'inactive')]) .. line
if self.options.self.section < 'lualine_x' and not self.first then
local sep_before = self:separator_before()
line = sep_before .. line
self.len = self.len + vim.fn.strchars(sep_before)
elseif self.options.self.section >= 'lualine_x' and not self.last then
local sep_after = self:separator_after()
line = line .. sep_after
self.len = self.len + vim.fn.strchars(sep_after)
end
return line
end
function Tab:separator_before()
if self.current or self.aftercurrent then
return '%S{' .. self.options.section_separators.left .. '}'
else
return self.options.component_separators.left
end
end
function Tab:separator_after()
if self.current or self.beforecurrent then
return '%s{' .. self.options.section_separators.right .. '}'
else
return self.options.component_separators.right
end
end
return Tab

View File

@ -29,25 +29,11 @@ local config = {
extensions = {},
}
local function check_sep_format_deprecation(sep)
if type(sep) == 'table' and vim.tbl_islist(sep) then
require('lualine.utils.notices').add_persistent_notice(string.format [[
### option.separator
Using list for configuring separators has been deprecated. Please configure it
with {left = left_sep, right = right_sep} like table.
]])
sep = { left = sep[1], right = sep[2] or sep[1] }
end
return sep
end
-- change separator format 'x' to {left='x', right='x'}
local function fix_separators(separators)
if separators ~= nil then
if type(separators) == 'string' then
return { left = separators, right = separators }
else
return check_sep_format_deprecation(separators)
end
end
return separators

View File

@ -0,0 +1,43 @@
-- Adapted from https://github.com/rxi/classic/blob/master/classic.lua
local Object = {}
Object.__index = Object
-- luacheck: push no unused args
-- Initializer
function Object:init(...) end
-- luacheck: pop
-- Extened base class to create a child class
function Object:extend()
local cls = {}
for k, v in pairs(self) do
if k:find '__' == 1 then
cls[k] = v
end
end
cls.__index = cls
cls.super = self
setmetatable(cls, self)
return cls
end
-- luacheck: push no unused args
function Object:__tostring()
return 'Object'
end
-- luacheck: pop
-- Creates a new object
function Object:new(...)
local obj = setmetatable({}, self)
obj:init(...)
return obj
end
-- Creates a new object
function Object:__call(...)
return self:new(...)
end
return Object

View File

@ -11,14 +11,19 @@ local is_valid_filename = lualine_require.is_valid_filename
local sep = lualine_require.sep
local component_types = {
luaf = function(component)
return require('lualine.components.special.function_component'):new(component)
lua_fun = function(component)
return require 'lualine.components.special.function_component'(component)
end,
mod = function(component)
local ok, loaded_component = pcall(require, 'lualine.components.' .. component[1])
if ok then
component.component_name = component[1]
loaded_component = loaded_component:new(component)
if type(loaded_component) == 'table' then
loaded_component = loaded_component(component)
elseif type(loaded_component) == 'function' then
component[1] = loaded_component
loaded_component = require 'lualine.components.special.function_component'(component)
end
return loaded_component
end
end,
@ -27,26 +32,26 @@ local component_types = {
component[1] = function()
return stl_expr
end
return require('lualine.components.special.function_component'):new(component)
return require 'lualine.components.special.function_component'(component)
end,
var = function(component)
return require('lualine.components.special.vim_var_component'):new(component)
return require 'lualine.components.special.vim_var_component'(component)
end,
['_'] = function(component)
return require('lualine.components.special.eval_func_component'):new(component)
return require 'lualine.components.special.eval_func_component'(component)
end,
}
local function component_loader(component)
if type(component[1]) == 'function' then
return component_types.luaf(component)
return component_types.lua_fun(component)
end
if type(component[1]) == 'string' then
-- load the component
if component.type ~= nil then
if component_types[component.type] and component.type ~= 'luaf' then
if component_types[component.type] and component.type ~= 'lua_fun' then
return component_types[component.type](component)
elseif component.type == 'vimf' or component.type == 'luae' then
elseif component.type == 'vim_fun' or component.type == 'lua_expr' then
return component_types['_'](component)
else
modules.notice.add_notice(string.format(
@ -73,85 +78,39 @@ end
local function option_deprecatation_notice(component)
local types = {
case = function()
local kind = component.upper ~= nil and 'upper' or 'lower'
type_name = function()
local changed_to = component.type == 'luae' and 'lua_expr' or 'vim_fun'
modules.notice.add_notice(string.format(
[[
### option.%s
### option.type.%s
Option `%s` has been deprecated.
Please use `fmt` option if you need to change case of a component.
type name `%s` has been deprecated.
Please use `%s`.
You have some thing like this in your config:
You have some thing like this in your config config for %s component:
```lua
%s = true,
type = %s,
```
You'll have to change it to this to retain old behavior:
```lua
fmt = string.%s
type = %s
```
]],
kind,
kind,
kind,
kind
component.type,
component.type,
changed_to,
tostring(component[1]),
component.type,
changed_to
))
end,
padding = function()
local kind = component.left_padding ~= nil and 'left_padding' or 'right_padding'
modules.notice.add_notice(string.format(
[[
### option.%s
Option `%s` has been deprecated.
Please use `padding` option to set left/right padding.
You have some thing like this in your config:
```lua
%s = %d,
```
You'll have to change it to this to retain old behavior:
```lua
padding = { %s = %d }
```
if you've set both left_padding and right_padding for a component
you'll need to have something like
```lua
padding = { left = x, right = y }
```
When you set `padding = x` it's same as `padding = {left = x, right = x}`
]],
kind,
kind,
kind,
component[kind],
kind == 'left_padding' and 'left' or 'right',
component[kind]
))
if component.left_padding and component.right_padding then
component.padding = { left = component.left_padding, right = component.right_padding }
component.left_padding = nil
component.right_padding = nil
elseif component.left_padding then
component.padding = { left = component.left_padding, right = 1 }
component.left_padding = nil
else
component.padding = { left = 1, right = component.right_padding }
component.right_padding = nil
end
component.type = changed_to
end,
}
if component.upper ~= nil or component.lower ~= nil then
types.case()
end
if component.left_padding ~= nil or component.right_padding ~= nil then
types.padding()
if component.type == 'luae' or component.type == 'vimf' then
types.type_name()
end
end
@ -226,6 +185,10 @@ local function load_theme(theme_name)
local retval
local path = table.concat { 'lua/lualine/themes/', theme_name, '.lua' }
local files = vim.api.nvim_get_runtime_file(path, true)
if #files <= 0 then
path = table.concat { 'lua/lualine/themes/', theme_name, '/init.lua' }
files = vim.api.nvim_get_runtime_file(path, true)
end
local n_files = #files
if n_files == 0 then
-- No match found

View File

@ -24,19 +24,34 @@ function M.require(module)
if package.loaded[module] then
return package.loaded[module]
end
local pattern = module:gsub('%.', M.sep) .. '.lua'
local pattern_dir = module:gsub('%.', M.sep)
local pattern_path = pattern_dir .. '.lua'
if M.plugin_dir then
local path = M.plugin_dir .. pattern
local path = M.plugin_dir .. pattern_path
assert(M.is_valid_filename(module), 'Invalid filename')
if vim.loop.fs_stat(path) then
local file_stat, dir_stat
file_stat = vim.loop.fs_stat(path)
if not file_stat then
path = M.plugin_dir .. pattern_dir
dir_stat = vim.loop.fs_stat(path)
if dir_stat and dir_stat.type == 'directory' then
path = path .. M.sep .. 'init.lua'
file_stat = vim.loop.fs_stat(path)
end
end
if file_stat and file_stat.type == 'file' then
local mod_result = dofile(path)
package.loaded[module] = mod_result
return mod_result
end
end
pattern = table.concat { 'lua/', module:gsub('%.', '/'), '.lua' }
local paths = vim.api.nvim_get_runtime_file(pattern, false)
pattern_path = table.concat { 'lua/', module:gsub('%.', '/'), '.lua' }
local paths = vim.api.nvim_get_runtime_file(pattern_path, false)
if #paths <= 0 then
pattern_path = table.concat { 'lua/', module:gsub('%.', '/'), '/init.lua' }
paths = vim.api.nvim_get_runtime_file(pattern_path, false)
end
if #paths > 0 then
local mod_result = dofile(paths[1])
package.loaded[module] = mod_result

View File

@ -16,8 +16,16 @@ M.assert_component = function(component, opts, result)
-- for testing global options
if component == nil then
component = 'special.function_component'
else
opts.component_name = component
end
local comp = require('lualine.components.' .. component)
if type(comp) == 'table' then
comp = comp(opts)
elseif type(comp) == 'function' then
opts[1] = comp
comp = require 'lualine.components.special.function_component'(opts)
end
local comp = require('lualine.components.' .. component):new(opts)
eq(result, comp:draw(opts.hl))
end

View File

@ -12,20 +12,20 @@ local stub = require 'luassert.stub'
describe('Component:', function()
it('can select separators', function()
local opts = build_component_opts()
local comp = require('lualine.components.special.function_component'):new(opts)
local comp = require 'lualine.components.special.function_component'(opts)
-- correct for lualine_c
eq('', comp.options.separator)
local opts2 = build_component_opts { self = { section = 'lualine_y' } }
local comp2 = require('lualine.components.special.function_component'):new(opts2)
local comp2 = require 'lualine.components.special.function_component'(opts2)
-- correct for lualine_u
eq('', comp2.options.separator)
end)
it('can provide unique identifier', function()
local opts1 = build_component_opts()
local comp1 = require('lualine.components.special.function_component'):new(opts1)
local comp1 = require 'lualine.components.special.function_component'(opts1)
local opts2 = build_component_opts()
local comp2 = require('lualine.components.special.function_component'):new(opts2)
local comp2 = require 'lualine.components.special.function_component'(opts2)
neq(comp1.component_no, comp2.component_no)
end)
@ -35,7 +35,7 @@ describe('Component:', function()
local hl = require 'lualine.highlight'
stub(hl, 'create_component_highlight_group')
hl.create_component_highlight_group.returns 'MyCompHl'
local comp1 = require('lualine.components.special.function_component'):new(opts1)
local comp1 = require 'lualine.components.special.function_component'(opts1)
eq('MyCompHl', comp1.options.color_highlight)
-- color highlight wan't in options when create_comp_hl was
-- called so remove it before assert
@ -46,7 +46,7 @@ describe('Component:', function()
local opts2 = build_component_opts { color = color }
stub(hl, 'create_component_highlight_group')
hl.create_component_highlight_group.returns 'MyCompLinkedHl'
local comp2 = require('lualine.components.special.function_component'):new(opts2)
local comp2 = require 'lualine.components.special.function_component'(opts2)
eq('MyCompLinkedHl', comp2.options.color_highlight)
-- color highlight wan't in options when create_comp_hl was
-- called so remove it before assert
@ -90,27 +90,6 @@ describe('Component:', function()
end)
describe('Global options:', function()
it('upper', function()
local opts = build_component_opts {
component_separators = { left = '', right = '' },
padding = 0,
upper = true,
}
assert_component(nil, opts, 'TEST')
end)
it('lower', function()
local opts = build_component_opts {
function()
return 'TeSt'
end,
component_separators = { left = '', right = '' },
padding = 0,
lower = true,
}
assert_component(nil, opts, 'test')
end)
it('left_padding', function()
local opts = build_component_opts {
component_separators = { left = '', right = '' },
@ -206,7 +185,7 @@ describe('Component:', function()
padding = 0,
color = 'MyHl',
}
local comp = require('lualine.components.special.function_component'):new(opts)
local comp = require 'lualine.components.special.function_component'(opts)
local custom_link_hl_name = 'lualine_' .. comp.options.component_name .. '_no_mode'
eq('%#' .. custom_link_hl_name .. '#test', comp:draw(opts.hl))
local opts2 = build_component_opts {
@ -217,7 +196,7 @@ describe('Component:', function()
local hl = require 'lualine.highlight'
stub(hl, 'component_format_highlight')
hl.component_format_highlight.returns '%#MyCompHl#'
local comp2 = require('lualine.components.special.function_component'):new(opts2)
local comp2 = require 'lualine.components.special.function_component'(opts2)
assert_component(nil, opts2, '%#MyCompHl#test')
assert.stub(hl.component_format_highlight).was_called_with(comp2.options.color_highlight)
hl.component_format_highlight:revert()

View File

@ -63,11 +63,14 @@ describe('config parsing', function()
end)
it('table', function()
local config = {
options = { component_separators = { 'a' }, section_separators = { 'b' } },
options = {
component_separators = { left = 'a', right = 'b' },
section_separators = { left = 'b', right = 'a' },
},
}
config = config_module.apply_configuration(config)
eq(config.options.component_separators, { left = 'a', right = 'a' })
eq(config.options.section_separators, { left = 'b', right = 'b' })
eq(config.options.component_separators, { left = 'a', right = 'b' })
eq(config.options.section_separators, { left = 'b', right = 'a' })
end)
end)
it('no seprarators', function()

View File

@ -59,8 +59,8 @@ describe('Section genarator', function()
it('can draw', function()
local opts = build_component_opts { section_separators = { left = '', right = '' } }
local section = {
require('lualine.components.special.function_component'):new(opts),
require('lualine.components.special.function_component'):new(opts),
require 'lualine.components.special.function_component'(opts),
require 'lualine.components.special.function_component'(opts),
}
eq('%#lualine_MySection_normal# test %#lualine_MySection_normal# test ', sec.draw_section(section, 'MySection'))
end)
@ -78,9 +78,9 @@ describe('Section genarator', function()
}
require('lualine.highlight').create_highlight_groups(require 'lualine.themes.gruvbox')
local section = {
require('lualine.components.special.function_component'):new(opts),
require('lualine.components.special.function_component'):new(opts_colored),
require('lualine.components.special.function_component'):new(opts),
require 'lualine.components.special.function_component'(opts),
require 'lualine.components.special.function_component'(opts_colored),
require 'lualine.components.special.function_component'(opts),
}
local highlight_name2 = 'lualine_' .. section[2].options.component_name .. '_no_mode'
-- Removes separator on string color
@ -88,14 +88,14 @@ describe('Section genarator', function()
'%#lualine_MySection_normal# test %#' .. highlight_name2 .. '#' .. ' test %#lualine_MySection_normal# test ',
sec.draw_section(section, 'MySection')
)
section[2] = require('lua.lualine.components.special.function_component'):new(opts_colored2)
section[2] = require 'lua.lualine.components.special.function_component'(opts_colored2)
local highlight_name = '%#lualine_c_' .. section[2].options.component_name .. '_normal#'
-- Removes separator on color with bg
eq(
'%#lualine_MySection_normal# test ' .. highlight_name .. ' test %#lualine_MySection_normal# test ',
sec.draw_section(section, 'MySection')
)
section[2] = require('lua.lualine.components.special.function_component'):new(opts_colored3)
section[2] = require 'lua.lualine.components.special.function_component'(opts_colored3)
highlight_name2 = '%#lualine_c_' .. section[2].options.component_name .. '_normal#'
-- Doesn't remove separator on color without bg
eq(