From 61ac66577412be28ee208f867f92f7720828c725 Mon Sep 17 00:00:00 2001 From: shadmansaleh <13149513+shadmansaleh@users.noreply.github.com> Date: Sat, 25 Sep 2021 23:15:42 +0600 Subject: [PATCH 1/8] 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 --- BREAKING_CHANGES.md | 30 ++ lua/lualine/component.lua | 348 +++++++++--------- lua/lualine/components/branch.lua | 77 ++-- lua/lualine/components/buffers.lua | 23 +- lua/lualine/components/diagnostics.lua | 51 ++- lua/lualine/components/diff.lua | 108 +++--- lua/lualine/components/encoding.lua | 6 +- lua/lualine/components/fileformat.lua | 10 +- lua/lualine/components/filename.lua | 13 +- lua/lualine/components/filesize.lua | 2 +- lua/lualine/components/filetype.lua | 17 +- lua/lualine/components/hostname.lua | 6 +- lua/lualine/components/location.lua | 6 +- lua/lualine/components/mode.lua | 7 +- lua/lualine/components/progress.lua | 6 +- .../special/eval_func_component.lua | 19 +- .../components/special/function_component.lua | 6 +- .../components/special/vim_var_component.lua | 7 +- lua/lualine/components/tabs.lua | 23 +- lua/lualine/utils/class.lua | 43 +++ lua/lualine/utils/loader.lua | 10 +- lua/tests/helpers.lua | 2 +- lua/tests/spec/component_spec.lua | 16 +- lua/tests/spec/utils_spec.lua | 14 +- 24 files changed, 459 insertions(+), 391 deletions(-) create mode 100644 lua/lualine/utils/class.lua diff --git a/BREAKING_CHANGES.md b/BREAKING_CHANGES.md index e512526..f9e552a 100644 --- a/BREAKING_CHANGES.md +++ b/BREAKING_CHANGES.md @@ -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 +``` + diff --git a/lua/lualine/component.lua b/lua/lualine/component.lua index 5fd2282..f660aa9 100644 --- a/lua/lualine/component.lua +++ b/lua/lualine/component.lua @@ -1,6 +1,8 @@ -- 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 @@ -27,190 +29,190 @@ for %s component. 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) - 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 +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 + check_deprecated_options(self.options) + 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, + 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 - ) +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 + +-- set upper or lower case +function M:apply_case() + -- Donn't work on components that emit vim statusline escaped chars + if self.status:find '%%' and not self.status:find '%%%%' then + 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 + +-- 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, + end + if r_padding then + self.status = self.status .. string.rep(' ', r_padding) + 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 +-- 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 + 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_case() + self:apply_padding() + self:apply_highlights(default_highlight) + self:apply_section_separators() + self:apply_separator() + end + return self.status +end -return Component +return M diff --git a/lua/lualine/components/branch.lua b/lua/lualine/components/branch.lua index 87832e5..637c311 100644 --- a/lua/lualine/components/branch.lua +++ b/lua/lualine/components/branch.lua @@ -1,49 +1,48 @@ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -local Branch = require('lualine.component'):new() +local M = require('lualine.component'):extend() local modules = require('lualine_require').lazy_require { utils = 'lualine.utils.utils', } -- vars -Branch.git_branch = '' -Branch.git_dir = '' +M.git_branch = '' +M.git_dir = '' -- os specific path separator -Branch.sep = package.config:sub(1, 1) +M.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' +M.file_changed = M.sep ~= '\\' and vim.loop.new_fs_event() or vim.loop.new_fs_poll() +M.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 +M.init = function(self, options) + M.super.init(self, options) + if not self.options.icon then + self.options.icon = '' -- e0a0 end -- run watch head on load so branch is present when component is loaded - Branch.find_git_dir() + M.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 +M.update_status = function(_, is_focused) + if M.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() + M.find_git_dir() end if not is_focused then return branch_cache[vim.fn.bufnr()] or '' end - return Branch.git_branch + return M.git_branch end 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() +function M.find_git_dir() -- get file dir so we can search from that dir local file_dir = vim.fn.expand '%:p:h' local root_dir = file_dir @@ -54,7 +53,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 .. M.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 +65,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) ~= M.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 .. M.sep .. 'HEAD') if head_file_stat and head_file_stat.type == 'file' then break else @@ -79,54 +78,54 @@ function Branch.find_git_dir() end end end - root_dir = root_dir:match('(.*)' .. Branch.sep .. '.-') + root_dir = root_dir:match('(.*)' .. M.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 M.git_dir ~= git_dir then + M.git_dir = git_dir + M.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) +function M.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 + M.git_branch = branch else - Branch.git_branch = HEAD:sub(1, 6) + M.git_branch = HEAD:sub(1, 6) end end return nil end -- Update branch -function Branch.update_branch() - Branch.active_bufnr = tostring(vim.fn.bufnr()) - Branch.file_changed:stop() - local git_dir = Branch.git_dir +function M.update_branch() + M.active_bufnr = tostring(vim.fn.bufnr()) + M.file_changed:stop() + local git_dir = M.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( + local head_file = git_dir .. M.sep .. 'HEAD' + M.get_git_head(head_file) + M.file_changed:start( head_file, - Branch.sep ~= '\\' and {} or 1000, + M.sep ~= '\\' and {} or 1000, vim.schedule_wrap(function() -- reset file-watch - Branch.update_branch() + M.update_branch() end) ) else -- set to '' when git dir was not found - Branch.git_branch = '' + M.git_branch = '' end - branch_cache[vim.fn.bufnr()] = Branch.git_branch + branch_cache[vim.fn.bufnr()] = M.git_branch end -return Branch +return M diff --git a/lua/lualine/components/buffers.lua b/lua/lualine/components/buffers.lua index 4bafc41..86ec797 100644 --- a/lua/lualine/components/buffers.lua +++ b/lua/lualine/components/buffers.lua @@ -1,6 +1,6 @@ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -local Buffers = require('lualine.component'):new() +local M = require('lualine.component'):extend() local highlight = require 'lualine.highlight' local default_options = { @@ -134,29 +134,28 @@ function Buffer:name() 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) +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), } - newObj.options = vim.tbl_deep_extend('keep', newObj.options or {}, default_options) - newObj.highlights = { + self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options) + self.highlights = { active = highlight.create_component_highlight_group( - newObj.options.buffers_color.active, + self.options.buffers_color.active, 'buffers_active', - newObj.options + self.options ), inactive = highlight.create_component_highlight_group( - newObj.options.buffers_color.inactive, + self.options.buffers_color.inactive, 'buffers_active', - newObj.options + self.options ), } - return newObj end -function Buffers:update_status() +function M:update_status() local data = {} local buffers = {} for b = 1, vim.fn.bufnr '$' do @@ -267,4 +266,4 @@ vim.cmd [[ endfunction ]] -return Buffers +return M diff --git a/lua/lualine/components/diagnostics.lua b/lua/lualine/components/diagnostics.lua index a8f915b..7eb024d 100644 --- a/lua/lualine/components/diagnostics.lua +++ b/lua/lualine/components/diagnostics.lua @@ -7,7 +7,7 @@ local modules = lualine_require.lazy_require { utils_notices = 'lualine.utils.notices', } -local Diagnostics = lualine_require.require('lualine.component'):new() +local M = lualine_require.require 'lualine.component':extend() local function check_deprecated_options(options) if options.color_error or options.color_warn or options.color_info or options.color_hint then @@ -83,56 +83,55 @@ local default_options = { }, } -- Initializer -Diagnostics.new = function(self, options, child) +function M:init(options) -- Run super() - local new_diagnostics = self._parent:new(options, child or Diagnostics) + M.super.init(self, options) -- Apply default options - new_diagnostics.options = vim.tbl_deep_extend('keep', new_diagnostics.options or {}, default_options) - check_deprecated_options(new_diagnostics.options) + self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options) + check_deprecated_options(self.options) -- Apply default symbols - new_diagnostics.symbols = vim.tbl_extend( + self.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 + self.options.symbols or {}, + self.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 = { + if self.options.colored then + self.highlight_groups = { error = modules.highlight.create_component_highlight_group( - new_diagnostics.options.diagnostics_color.error, + self.options.diagnostics_color.error, 'diagnostics_error', - new_diagnostics.options + self.options ), warn = modules.highlight.create_component_highlight_group( - new_diagnostics.options.diagnostics_color.warn, + self.options.diagnostics_color.warn, 'diagnostics_warn', - new_diagnostics.options + self.options ), info = modules.highlight.create_component_highlight_group( - new_diagnostics.options.diagnostics_color.info, + self.options.diagnostics_color.info, 'diagnostics_info', - new_diagnostics.options + self.options ), hint = modules.highlight.create_component_highlight_group( - new_diagnostics.options.diagnostics_color.hint, + self.options.diagnostics_color.hint, 'diagnostics_hint', - new_diagnostics.options + self.options ), } end -- Error out no source - if #new_diagnostics.options.sources < 1 then + 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 - new_diagnostics.last_update = '' - return new_diagnostics + self.last_update = '' end -Diagnostics.update_status = function(self) +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 @@ -175,7 +174,7 @@ Diagnostics.update_status = function(self) return self.last_update end -Diagnostics.diagnostic_sources = { +M.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') @@ -220,11 +219,11 @@ Diagnostics.diagnostic_sources = { end, } -Diagnostics.get_diagnostics = function(sources) +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 = Diagnostics.diagnostic_sources[source]() + local error_count, warning_count, info_count, hint_count = M.diagnostic_sources[source]() result[index] = { error = error_count, warn = warning_count, @@ -245,4 +244,4 @@ Diagnostics.get_diagnostics = function(sources) return result end -return Diagnostics +return M diff --git a/lua/lualine/components/diff.lua b/lua/lualine/components/diff.lua index f0fd098..b77c972 100644 --- a/lua/lualine/components/diff.lua +++ b/lua/lualine/components/diff.lua @@ -7,7 +7,7 @@ local modules = lualine_require.lazy_require { highlight = 'lualine.highlight', Job = 'lualine.utils.job', } -local Diff = lualine_require.require('lualine.component'):new() +local M = lualine_require.require('lualine.component'):extend() local function check_deprecated_options(options) if options.color_added or options.color_modified or options.color_removed then @@ -35,12 +35,12 @@ end -- Vars -- variable to store git diff stats -Diff.git_diff = nil +M.git_diff = nil -- accumulates output from diff process -Diff.diff_output_cache = {} +M.diff_output_cache = {} -- variable to store git_diff job -Diff.diff_job = nil -Diff.active_bufnr = '0' +M.diff_job = nil +M.active_bufnr = '0' local diff_cache = {} -- Stores last known value of diff of a buffer @@ -61,54 +61,52 @@ local default_options = { } -- 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) +function M:init(options) + M.super.init(self, options) + self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options) + check_deprecated_options(self.options) -- create highlights and save highlight_name in highlights table - if new_instance.options.colored then - new_instance.highlights = { + if self.options.colored then + self.highlights = { added = modules.highlight.create_component_highlight_group( - new_instance.options.diff_color.added, + self.options.diff_color.added, 'diff_added', - new_instance.options + self.options ), modified = modules.highlight.create_component_highlight_group( - new_instance.options.diff_color.modified, + self.options.diff_color.modified, 'diff_modified', - new_instance.options + self.options ), removed = modules.highlight.create_component_highlight_group( - new_instance.options.diff_color.removed, + self.options.diff_color.removed, 'diff_removed', - new_instance.options + self.options ), } end - Diff.diff_checker_enabled = type(new_instance.options.source) ~= 'function' + M.diff_checker_enabled = type(self.options.source) ~= 'function' - if Diff.diff_checker_enabled then + if M.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() + M.update_diff_args() end - - return new_instance end -- Function that runs everytime statusline is updated -Diff.update_status = function(self, is_focused) +function M:update_status(is_focused) local git_diff - if Diff.diff_checker_enabled then - if Diff.active_bufnr ~= vim.g.actual_curbuf then + if M.diff_checker_enabled then + if M.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() + M.update_diff_args() end - git_diff = Diff.git_diff + git_diff = M.git_diff else git_diff = self.options.source() end @@ -154,15 +152,15 @@ end -- 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() +function M.get_sign_count() + if M.diff_checker_enabled then + M.update_diff_args() end - return Diff.git_diff or { added = -1, modified = -1, removed = -1 } + return M.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) +function M.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 @@ -185,19 +183,19 @@ function Diff.process_diff(data) end end end - Diff.git_diff = { added = added, modified = modified, removed = removed } + M.git_diff = { added = added, modified = modified, removed = removed } end -- Updates the job args -function Diff.update_diff_args() +function M.update_diff_args() -- Donn't show git diff when current buffer doesn't have a filename - Diff.active_bufnr = tostring(vim.fn.bufnr()) + M.active_bufnr = tostring(vim.fn.bufnr()) if #vim.fn.expand '%' == 0 then - Diff.diff_args = nil - Diff.git_diff = nil + M.diff_args = nil + M.git_diff = nil return end - Diff.diff_args = { + M.diff_args = { cmd = string.format( [[git -C %s --no-pager diff --no-color --no-ext-diff -U0 -- %s]], vim.fn.expand '%:h', @@ -205,40 +203,40 @@ function Diff.update_diff_args() ), on_stdout = function(_, data) if next(data) then - Diff.diff_output_cache = vim.list_extend(Diff.diff_output_cache, data) + M.diff_output_cache = vim.list_extend(M.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 = {} + M.git_diff = nil + M.diff_output_cache = {} end end, on_exit = function() - if #Diff.diff_output_cache > 0 then - Diff.process_diff(Diff.diff_output_cache) + if #M.diff_output_cache > 0 then + M.process_diff(M.diff_output_cache) else - Diff.git_diff = { added = 0, modified = 0, removed = 0 } + M.git_diff = { added = 0, modified = 0, removed = 0 } end - diff_cache[vim.fn.bufnr()] = Diff.git_diff + diff_cache[vim.fn.bufnr()] = M.git_diff end, } - Diff.update_git_diff() + M.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() +function M.update_git_diff() + if M.diff_args then + M.diff_output_cache = {} + if M.diff_job then + M.diff_job:stop() end - Diff.diff_job = modules.Job(Diff.diff_args) - if Diff.diff_job then - Diff.diff_job:start() + M.diff_job = modules.Job(M.diff_args) + if M.diff_job then + M.diff_job:start() end end end -return Diff +return M diff --git a/lua/lualine/components/encoding.lua b/lua/lualine/components/encoding.lua index a2f5fdd..d191395 100644 --- a/lua/lualine/components/encoding.lua +++ b/lua/lualine/components/encoding.lua @@ -1,9 +1,9 @@ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. -local Encoding = require('lualine.component'):new() +local M = require('lualine.component'):extend() -Encoding.update_status = function() +M.update_status = function() return [[%{strlen(&fenc)?&fenc:&enc}]] end -return Encoding +return M diff --git a/lua/lualine/components/fileformat.lua b/lua/lualine/components/fileformat.lua index a36d7ef..8ca764a 100644 --- a/lua/lualine/components/fileformat.lua +++ b/lua/lualine/components/fileformat.lua @@ -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 diff --git a/lua/lualine/components/filename.lua b/lua/lualine/components/filename.lua index 2f4f6f3..27346d2 100644 --- a/lua/lualine/components/filename.lua +++ b/lua/lualine/components/filename.lua @@ -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 diff --git a/lua/lualine/components/filesize.lua b/lua/lualine/components/filesize.lua index 523dda2..4336d21 100644 --- a/lua/lualine/components/filesize.lua +++ b/lua/lualine/components/filesize.lua @@ -1,6 +1,6 @@ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -local M = require('lualine.component'):new() +local M = require('lualine.component'):extend() M.update_status = function() local file = vim.fn.expand '%:p' diff --git a/lua/lualine/components/filetype.lua b/lua/lualine/components/filetype.lua index fac7c57..2c26a03 100644 --- a/lua/lualine/components/filetype.lua +++ b/lua/lualine/components/filetype.lua @@ -5,7 +5,7 @@ local modules = lualine_require.lazy_require { highlight = 'lualine.highlight', utils = 'lualine.utils.utils', } -local FileType = lualine_require.require('lualine.component'):new() +local M = lualine_require.require('lualine.component'):extend() local function check_deprecated_options(options) local function rename_notice(before, now) @@ -33,18 +33,17 @@ local default_options = { 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) + check_deprecated_options(self.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 +86,4 @@ function FileType:apply_icon() end end -return FileType +return M diff --git a/lua/lualine/components/hostname.lua b/lua/lualine/components/hostname.lua index 5ac4112..4b04d3a 100644 --- a/lua/lualine/components/hostname.lua +++ b/lua/lualine/components/hostname.lua @@ -1,7 +1,7 @@ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. -local HostName = require('lualine.component'):new() +local M = require('lualine.component'):extend() -HostName.update_status = vim.loop.os_gethostname +M.update_status = vim.loop.os_gethostname -return HostName +return M diff --git a/lua/lualine/components/location.lua b/lua/lualine/components/location.lua index 39b2eae..91aaac5 100644 --- a/lua/lualine/components/location.lua +++ b/lua/lualine/components/location.lua @@ -1,9 +1,9 @@ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. -local Location = require('lualine.component'):new() +local M = require('lualine.component'):extend() -Location.update_status = function() +M.update_status = function() return [[%3l:%-2c]] end -return Location +return M diff --git a/lua/lualine/components/mode.lua b/lua/lualine/components/mode.lua index cec8222..092a674 100644 --- a/lua/lualine/components/mode.lua +++ b/lua/lualine/components/mode.lua @@ -1,9 +1,10 @@ -- 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 +local M = require('lualine.component'):extend() -return Mode +M.update_status = get_mode + +return M diff --git a/lua/lualine/components/progress.lua b/lua/lualine/components/progress.lua index ac7e52f..0b6aec8 100644 --- a/lua/lualine/components/progress.lua +++ b/lua/lualine/components/progress.lua @@ -1,9 +1,9 @@ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. -local Progress = require('lualine.component'):new() +local M = require('lualine.component'):extend() -Progress.update_status = function() +M.update_status = function() return [[%3P]] end -return Progress +return M diff --git a/lua/lualine/components/special/eval_func_component.lua b/lua/lualine/components/special/eval_func_component.lua index 4656b89..a209eba 100644 --- a/lua/lualine/components/special/eval_func_component.lua +++ b/lua/lualine/components/special/eval_func_component.lua @@ -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) + 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) + 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 diff --git a/lua/lualine/components/special/function_component.lua b/lua/lualine/components/special/function_component.lua index b360c31..4856dad 100644 --- a/lua/lualine/components/special/function_component.lua +++ b/lua/lualine/components/special/function_component.lua @@ -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 diff --git a/lua/lualine/components/special/vim_var_component.lua b/lua/lualine/components/special/vim_var_component.lua index eb46b28..f6853a2 100644 --- a/lua/lualine/components/special/vim_var_component.lua +++ b/lua/lualine/components/special/vim_var_component.lua @@ -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 diff --git a/lua/lualine/components/tabs.lua b/lua/lualine/components/tabs.lua index c55b72e..57e5495 100644 --- a/lua/lualine/components/tabs.lua +++ b/lua/lualine/components/tabs.lua @@ -1,6 +1,6 @@ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -local Tabs = require('lualine.component'):new() +local M = require('lualine.component'):extend() local highlight = require 'lualine.highlight' local default_options = { @@ -103,30 +103,29 @@ function Tab:separator_after() end end -function Tabs:new(options, child) - local newObj = self._parent:new(options, child or Tabs) +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), } - newObj.options = vim.tbl_deep_extend('keep', newObj.options or {}, default_options) + self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options) -- stylua: ignore - newObj.highlights = { + self.highlights = { active = highlight.create_component_highlight_group( - newObj.options.tabs_color.active, + self.options.tabs_color.active, 'tabs_active', - newObj.options + self.options ), inactive = highlight.create_component_highlight_group( - newObj.options.tabs_color.inactive, + self.options.tabs_color.inactive, 'tabs_active', - newObj.options + self.options ), } - return newObj end -function Tabs:update_status() +function M:update_status() local data = {} local tabs = {} for t = 1, vim.fn.tabpagenr '$' do @@ -214,4 +213,4 @@ vim.cmd [[ endfunction ]] -return Tabs +return M diff --git a/lua/lualine/utils/class.lua b/lua/lualine/utils/class.lua new file mode 100644 index 0000000..83700fc --- /dev/null +++ b/lua/lualine/utils/class.lua @@ -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 diff --git a/lua/lualine/utils/loader.lua b/lua/lualine/utils/loader.lua index d8e5547..0e05682 100644 --- a/lua/lualine/utils/loader.lua +++ b/lua/lualine/utils/loader.lua @@ -12,13 +12,13 @@ local sep = lualine_require.sep local component_types = { luaf = function(component) - return require('lualine.components.special.function_component'):new(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) + loaded_component = loaded_component(component) return loaded_component end end, @@ -27,13 +27,13 @@ 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, } diff --git a/lua/tests/helpers.lua b/lua/tests/helpers.lua index 1f46144..6a3516e 100644 --- a/lua/tests/helpers.lua +++ b/lua/tests/helpers.lua @@ -17,7 +17,7 @@ M.assert_component = function(component, opts, result) if component == nil then component = 'special.function_component' end - local comp = require('lualine.components.' .. component):new(opts) + local comp = require('lualine.components.' .. component)(opts) eq(result, comp:draw(opts.hl)) end diff --git a/lua/tests/spec/component_spec.lua b/lua/tests/spec/component_spec.lua index 5e02586..3e0f74a 100644 --- a/lua/tests/spec/component_spec.lua +++ b/lua/tests/spec/component_spec.lua @@ -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 @@ -206,7 +206,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 +217,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() diff --git a/lua/tests/spec/utils_spec.lua b/lua/tests/spec/utils_spec.lua index ced2a16..5f4e9ef 100644 --- a/lua/tests/spec/utils_spec.lua +++ b/lua/tests/spec/utils_spec.lua @@ -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( From c8750a35e089bab7707e2431a1ac1709f78becb1 Mon Sep 17 00:00:00 2001 From: shadmansaleh <13149513+shadmansaleh@users.noreply.github.com> Date: Sun, 10 Oct 2021 22:43:00 +0600 Subject: [PATCH 2/8] 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. --- lua/lualine/components/encoding.lua | 6 ++---- lua/lualine/components/filesize.lua | 6 ++---- lua/lualine/components/hostname.lua | 8 ++++---- lua/lualine/components/location.lua | 6 ++---- lua/lualine/components/mode.lua | 8 +------- lua/lualine/components/progress.lua | 6 ++---- lua/lualine/utils/loader.lua | 7 ++++++- lua/tests/helpers.lua | 10 +++++++++- 8 files changed, 28 insertions(+), 29 deletions(-) diff --git a/lua/lualine/components/encoding.lua b/lua/lualine/components/encoding.lua index d191395..f40aecc 100644 --- a/lua/lualine/components/encoding.lua +++ b/lua/lualine/components/encoding.lua @@ -1,9 +1,7 @@ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. -local M = require('lualine.component'):extend() - -M.update_status = function() +local function encoding() return [[%{strlen(&fenc)?&fenc:&enc}]] end -return M +return encoding diff --git a/lua/lualine/components/filesize.lua b/lua/lualine/components/filesize.lua index 4336d21..cc95da8 100644 --- a/lua/lualine/components/filesize.lua +++ b/lua/lualine/components/filesize.lua @@ -1,8 +1,6 @@ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -local M = require('lualine.component'):extend() - -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 diff --git a/lua/lualine/components/hostname.lua b/lua/lualine/components/hostname.lua index 4b04d3a..9c43b14 100644 --- a/lua/lualine/components/hostname.lua +++ b/lua/lualine/components/hostname.lua @@ -1,7 +1,7 @@ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. -local M = require('lualine.component'):extend() +local function hostname() + return vim.loop.os_gethostname() +end -M.update_status = vim.loop.os_gethostname - -return M +return hostname diff --git a/lua/lualine/components/location.lua b/lua/lualine/components/location.lua index 91aaac5..1ffff54 100644 --- a/lua/lualine/components/location.lua +++ b/lua/lualine/components/location.lua @@ -1,9 +1,7 @@ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. -local M = require('lualine.component'):extend() - -M.update_status = function() +local function location() return [[%3l:%-2c]] end -return M +return location diff --git a/lua/lualine/components/mode.lua b/lua/lualine/components/mode.lua index 092a674..721d6ca 100644 --- a/lua/lualine/components/mode.lua +++ b/lua/lualine/components/mode.lua @@ -1,10 +1,4 @@ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. -local require = require('lualine_require').require local get_mode = require('lualine.utils.mode').get_mode - -local M = require('lualine.component'):extend() - -M.update_status = get_mode - -return M +return get_mode diff --git a/lua/lualine/components/progress.lua b/lua/lualine/components/progress.lua index 0b6aec8..e4a7054 100644 --- a/lua/lualine/components/progress.lua +++ b/lua/lualine/components/progress.lua @@ -1,9 +1,7 @@ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. -local M = require('lualine.component'):extend() - -M.update_status = function() +local function progress() return [[%3P]] end -return M +return progress diff --git a/lua/lualine/utils/loader.lua b/lua/lualine/utils/loader.lua index 0e05682..ed0788a 100644 --- a/lua/lualine/utils/loader.lua +++ b/lua/lualine/utils/loader.lua @@ -18,7 +18,12 @@ local component_types = { local ok, loaded_component = pcall(require, 'lualine.components.' .. component[1]) if ok then component.component_name = component[1] - loaded_component = loaded_component(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, diff --git a/lua/tests/helpers.lua b/lua/tests/helpers.lua index 6a3516e..74e1b86 100644 --- a/lua/tests/helpers.lua +++ b/lua/tests/helpers.lua @@ -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)(opts) eq(result, comp:draw(opts.hl)) end From 6e936efe06a4d43630c3e166e083835e6c571cf0 Mon Sep 17 00:00:00 2001 From: shadmansaleh <13149513+shadmansaleh@users.noreply.github.com> Date: Mon, 11 Oct 2021 00:04:55 +0600 Subject: [PATCH 3/8] refactor: allow components, extensions, themes to be multi file module (#81) utilize that to split tabs & buffers components into smaller files --- lua/lualine/components/buffers/buffer.lua | 103 +++++++++++++++++ .../{buffers.lua => buffers/init.lua} | 107 +----------------- .../components/{tabs.lua => tabs/init.lua} | 84 +------------- lua/lualine/components/tabs/tab.lua | 75 ++++++++++++ lua/lualine/utils/loader.lua | 4 + lua/lualine_require.lua | 25 +++- 6 files changed, 210 insertions(+), 188 deletions(-) create mode 100644 lua/lualine/components/buffers/buffer.lua rename lua/lualine/components/{buffers.lua => buffers/init.lua} (51%) rename lua/lualine/components/{tabs.lua => tabs/init.lua} (55%) create mode 100644 lua/lualine/components/tabs/tab.lua diff --git a/lua/lualine/components/buffers/buffer.lua b/lua/lualine/components/buffers/buffer.lua new file mode 100644 index 0000000..2fd811d --- /dev/null +++ b/lua/lualine/components/buffers/buffer.lua @@ -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 diff --git a/lua/lualine/components/buffers.lua b/lua/lualine/components/buffers/init.lua similarity index 51% rename from lua/lualine/components/buffers.lua rename to lua/lualine/components/buffers/init.lua index 86ec797..a2f0f87 100644 --- a/lua/lualine/components/buffers.lua +++ b/lua/lualine/components/buffers/init.lua @@ -1,5 +1,7 @@ -- 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' @@ -33,107 +35,6 @@ local function get_hl(section, is_active) 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 M:init(options) M.super.init(self, options) default_options.buffers_color = { @@ -160,7 +61,7 @@ function M:update_status() 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 } + buffers[#buffers + 1] = Buffer { bufnr = b, options = self.options, highlights = self.highlights } end end local current_bufnr = vim.fn.bufnr() @@ -195,7 +96,7 @@ function M:update_status() end end if current == -2 then - local b = Buffer:new { bufnr = vim.fn.bufnr(), options = self.options, highlights = self.highlights } + 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 diff --git a/lua/lualine/components/tabs.lua b/lua/lualine/components/tabs/init.lua similarity index 55% rename from lua/lualine/components/tabs.lua rename to lua/lualine/components/tabs/init.lua index 57e5495..6a521ef 100644 --- a/lua/lualine/components/tabs.lua +++ b/lua/lualine/components/tabs/init.lua @@ -1,5 +1,7 @@ -- 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' @@ -25,84 +27,6 @@ local function get_hl(section, is_active) 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 M:init(options) M.super.init(self, options) default_options.tabs_color = { @@ -129,7 +53,7 @@ function M: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 } + tabs[#tabs + 1] = Tab { tabnr = t, options = self.options, highlights = self.highlights } end local current = vim.fn.tabpagenr() tabs[1].first = true @@ -156,7 +80,7 @@ function M:update_status() 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 } + local t = Tab { tabnr = vim.fn.tabpagenr(), options = self.options, highlights = self.highlights } t.current = true t.last = true data[#data + 1] = t:render() diff --git a/lua/lualine/components/tabs/tab.lua b/lua/lualine/components/tabs/tab.lua new file mode 100644 index 0000000..70d1c89 --- /dev/null +++ b/lua/lualine/components/tabs/tab.lua @@ -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 diff --git a/lua/lualine/utils/loader.lua b/lua/lualine/utils/loader.lua index ed0788a..175008f 100644 --- a/lua/lualine/utils/loader.lua +++ b/lua/lualine/utils/loader.lua @@ -231,6 +231,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 diff --git a/lua/lualine_require.lua b/lua/lualine_require.lua index 6aa35f7..6126104 100644 --- a/lua/lualine_require.lua +++ b/lua/lualine_require.lua @@ -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 From d25a37fea0ca357248cf882a4103a04f18d16d53 Mon Sep 17 00:00:00 2001 From: shadmansaleh <13149513+shadmansaleh@users.noreply.github.com> Date: Mon, 11 Oct 2021 13:47:37 +0600 Subject: [PATCH 4/8] 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 --- README.md | 4 +- doc/lualine.txt | 4 +- .../special/eval_func_component.lua | 4 +- lua/lualine/utils/loader.lua | 41 +++++++++++++++++-- 4 files changed, 43 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 880bd3e..56dbb15 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/doc/lualine.txt b/doc/lualine.txt index 63cd840..33e0d00 100644 --- a/doc/lualine.txt +++ b/doc/lualine.txt @@ -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 diff --git a/lua/lualine/components/special/eval_func_component.lua b/lua/lualine/components/special/eval_func_component.lua index a209eba..94253e3 100644 --- a/lua/lualine/components/special/eval_func_component.lua +++ b/lua/lualine/components/special/eval_func_component.lua @@ -11,12 +11,12 @@ function M:update_status() status = M.vim_function(component) end else - if self.options.type == 'luae' then + 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 + elseif self.options.type == 'vim_fun' then status = M.vim_function(component) end end diff --git a/lua/lualine/utils/loader.lua b/lua/lualine/utils/loader.lua index 175008f..45a7788 100644 --- a/lua/lualine/utils/loader.lua +++ b/lua/lualine/utils/loader.lua @@ -11,7 +11,7 @@ local is_valid_filename = lualine_require.is_valid_filename local sep = lualine_require.sep local component_types = { - luaf = function(component) + lua_fun = function(component) return require 'lualine.components.special.function_component'(component) end, mod = function(component) @@ -44,14 +44,14 @@ local component_types = { 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( @@ -151,6 +151,36 @@ When you set `padding = x` it's same as `padding = {left = x, right = x}` component.right_padding = nil end end, + type_name = function() + local changed_to = component.type == 'luae' and 'lua_expr' or 'vim_fun' + modules.notice.add_notice(string.format( + [[ +### option.type.%s + +type name `%s` has been deprecated. +Please use `%s`. + +You have some thing like this in your config config for %s component: + +```lua + type = %s, +``` + +You'll have to change it to this to retain old behavior: + +```lua + type = %s +``` +]], + component.type, + component.type, + changed_to, + tostring(component[1]), + component.type, + changed_to + )) + component.type = changed_to + end, } if component.upper ~= nil or component.lower ~= nil then types.case() @@ -158,6 +188,9 @@ When you set `padding = x` it's same as `padding = {left = x, right = x}` if component.left_padding ~= nil or component.right_padding ~= nil then types.padding() end + if component.type == 'luae' or component.type == 'vimf' then + types.type_name() + end end local function load_sections(sections, options) From 1e2b735aee8aaea43a4fcc9519a3d616cce72cca Mon Sep 17 00:00:00 2001 From: shadmansaleh <13149513+shadmansaleh@users.noreply.github.com> Date: Mon, 11 Oct 2021 14:51:18 +0600 Subject: [PATCH 5/8] chore: remove deprecation notices and deprecated options from #24 + fix tests --- lua/lualine/component.lua | 38 ------------- lua/lualine/components/diagnostics.lua | 28 --------- lua/lualine/components/diff.lua | 25 -------- lua/lualine/components/filetype.lua | 22 ------- lua/lualine/config.lua | 14 ----- lua/lualine/utils/loader.lua | 79 -------------------------- lua/tests/spec/component_spec.lua | 21 ------- lua/tests/spec/config_spec.lua | 9 ++- 8 files changed, 6 insertions(+), 230 deletions(-) diff --git a/lua/lualine/component.lua b/lua/lualine/component.lua index f660aa9..f461501 100644 --- a/lua/lualine/component.lua +++ b/lua/lualine/component.lua @@ -7,29 +7,6 @@ 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 - end - end - rename_notice('format', 'fmt') - rename_notice('condition', 'cond') -end - function M:__tostring() local str = 'Component: ' .. self.options.component_name if self.debug then @@ -42,7 +19,6 @@ end function M:init(options) self.options = options or {} component_no = component_no + 1 - check_deprecated_options(self.options) if not options.component_name then self.options.component_name = tostring(component_no) end @@ -74,19 +50,6 @@ function M:create_option_highlights() end end --- set upper or lower case -function M:apply_case() - -- Donn't work on components that emit vim statusline escaped chars - if self.status:find '%%' and not self.status:find '%%%%' then - 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 - -- Adds spaces to left and right of a component function M:apply_padding() local padding = self.options.padding @@ -206,7 +169,6 @@ function M:draw(default_highlight, is_focused) 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() diff --git a/lua/lualine/components/diagnostics.lua b/lua/lualine/components/diagnostics.lua index 7eb024d..b7fe114 100644 --- a/lua/lualine/components/diagnostics.lua +++ b/lua/lualine/components/diagnostics.lua @@ -9,33 +9,6 @@ local modules = lualine_require.lazy_require { local M = lualine_require.require 'lualine.component':extend() -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 @@ -88,7 +61,6 @@ function M:init(options) M.super.init(self, options) -- Apply default options self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options) - check_deprecated_options(self.options) -- Apply default symbols self.symbols = vim.tbl_extend( 'keep', diff --git a/lua/lualine/components/diff.lua b/lua/lualine/components/diff.lua index b77c972..246c983 100644 --- a/lua/lualine/components/diff.lua +++ b/lua/lualine/components/diff.lua @@ -9,30 +9,6 @@ local modules = lualine_require.lazy_require { } local M = lualine_require.require('lualine.component'):extend() -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 M.git_diff = nil @@ -64,7 +40,6 @@ local default_options = { function M:init(options) M.super.init(self, options) self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options) - check_deprecated_options(self.options) -- create highlights and save highlight_name in highlights table if self.options.colored then self.highlights = { diff --git a/lua/lualine/components/filetype.lua b/lua/lualine/components/filetype.lua index 2c26a03..89b74d2 100644 --- a/lua/lualine/components/filetype.lua +++ b/lua/lualine/components/filetype.lua @@ -7,27 +7,6 @@ local modules = lualine_require.lazy_require { } local M = lualine_require.require('lualine.component'):extend() -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 default_options = { colored = true, icon_only = false, @@ -36,7 +15,6 @@ local default_options = { function M:init(options) M.super.init(self, options) self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options) - check_deprecated_options(self.options) end function M.update_status() diff --git a/lua/lualine/config.lua b/lua/lualine/config.lua index a09cfba..ae5527e 100644 --- a/lua/lualine/config.lua +++ b/lua/lualine/config.lua @@ -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 diff --git a/lua/lualine/utils/loader.lua b/lua/lualine/utils/loader.lua index 45a7788..ef79d13 100644 --- a/lua/lualine/utils/loader.lua +++ b/lua/lualine/utils/loader.lua @@ -78,79 +78,6 @@ end local function option_deprecatation_notice(component) local types = { - case = function() - local kind = component.upper ~= nil and 'upper' or 'lower' - modules.notice.add_notice(string.format( - [[ -### option.%s - -Option `%s` has been deprecated. -Please use `fmt` option if you need to change case of a component. - -You have some thing like this in your config: - -```lua - %s = true, -``` - -You'll have to change it to this to retain old behavior: - -```lua - fmt = string.%s -``` -]], - kind, - kind, - kind, - kind - )) - 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 - end, type_name = function() local changed_to = component.type == 'luae' and 'lua_expr' or 'vim_fun' modules.notice.add_notice(string.format( @@ -182,12 +109,6 @@ You'll have to change it to this to retain old behavior: 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() - end if component.type == 'luae' or component.type == 'vimf' then types.type_name() end diff --git a/lua/tests/spec/component_spec.lua b/lua/tests/spec/component_spec.lua index 3e0f74a..5d616cc 100644 --- a/lua/tests/spec/component_spec.lua +++ b/lua/tests/spec/component_spec.lua @@ -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 = '' }, diff --git a/lua/tests/spec/config_spec.lua b/lua/tests/spec/config_spec.lua index 484e6d7..df5d48c 100644 --- a/lua/tests/spec/config_spec.lua +++ b/lua/tests/spec/config_spec.lua @@ -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() From 0b599b0ace536391f34e4c5d65a13f0ca549c5d8 Mon Sep 17 00:00:00 2001 From: shadmansaleh <13149513+shadmansaleh@users.noreply.github.com> Date: Mon, 11 Oct 2021 15:21:38 +0600 Subject: [PATCH 6/8] refactor: split diagnostics component (#81) --- lua/lualine/components/diagnostics.lua | 219 ------------------ lua/lualine/components/diagnostics/config.lua | 52 +++++ lua/lualine/components/diagnostics/init.lua | 109 +++++++++ .../components/diagnostics/sources.lua | 73 ++++++ 4 files changed, 234 insertions(+), 219 deletions(-) delete mode 100644 lua/lualine/components/diagnostics.lua create mode 100644 lua/lualine/components/diagnostics/config.lua create mode 100644 lua/lualine/components/diagnostics/init.lua create mode 100644 lua/lualine/components/diagnostics/sources.lua diff --git a/lua/lualine/components/diagnostics.lua b/lua/lualine/components/diagnostics.lua deleted file mode 100644 index b7fe114..0000000 --- a/lua/lualine/components/diagnostics.lua +++ /dev/null @@ -1,219 +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 M = lualine_require.require 'lualine.component':extend() - -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 -function M:init(options) - -- Run super() - M.super.init(self, options) - -- Apply default options - self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options) - -- Apply default symbols - self.symbols = vim.tbl_extend( - 'keep', - self.options.symbols or {}, - self.options.icons_enabled ~= false and default_symbols.icons or default_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 = 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 - -M.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, -} - -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.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 M diff --git a/lua/lualine/components/diagnostics/config.lua b/lua/lualine/components/diagnostics/config.lua new file mode 100644 index 0000000..4beb9b3 --- /dev/null +++ b/lua/lualine/components/diagnostics/config.lua @@ -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 diff --git a/lua/lualine/components/diagnostics/init.lua b/lua/lualine/components/diagnostics/init.lua new file mode 100644 index 0000000..c5a19fe --- /dev/null +++ b/lua/lualine/components/diagnostics/init.lua @@ -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 diff --git a/lua/lualine/components/diagnostics/sources.lua b/lua/lualine/components/diagnostics/sources.lua new file mode 100644 index 0000000..f221673 --- /dev/null +++ b/lua/lualine/components/diagnostics/sources.lua @@ -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 From cc64ec3f7c76feb06a9b68a7d0c89736d1ce1b0e Mon Sep 17 00:00:00 2001 From: shadmansaleh <13149513+shadmansaleh@users.noreply.github.com> Date: Mon, 11 Oct 2021 17:42:47 +0600 Subject: [PATCH 7/8] refactor: split branch component (#81) --- .../{branch.lua => branch/git_branch.lua} | 158 +++++++++--------- lua/lualine/components/branch/init.lua | 20 +++ 2 files changed, 96 insertions(+), 82 deletions(-) rename lua/lualine/components/{branch.lua => branch/git_branch.lua} (55%) create mode 100644 lua/lualine/components/branch/init.lua diff --git a/lua/lualine/components/branch.lua b/lua/lualine/components/branch/git_branch.lua similarity index 55% rename from lua/lualine/components/branch.lua rename to lua/lualine/components/branch/git_branch.lua index 637c311..75831ba 100644 --- a/lua/lualine/components/branch.lua +++ b/lua/lualine/components/branch/git_branch.lua @@ -1,50 +1,64 @@ --- Copyright (c) 2020-2021 shadmansaleh --- MIT license, see LICENSE for more details. -local M = require('lualine.component'):extend() -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 -M.git_branch = '' -M.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 -M.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. -M.file_changed = M.sep ~= '\\' and vim.loop.new_fs_event() or vim.loop.new_fs_poll() -M.active_bufnr = '0' -local branch_cache = {} -- stores last known branch for a buffer --- Initilizer -M.init = function(self, options) - M.super.init(self, options) - if not self.options.icon then - self.options.icon = '' -- e0a0 - end - -- 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 - modules.utils.define_autocmd('BufEnter', "lua require'lualine.components.branch'.find_git_dir()") -end - -M.update_status = function(_, is_focused) - if M.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 - if not is_focused then - return branch_cache[vim.fn.bufnr()] or '' - end - return M.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 M.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 @@ -53,7 +67,7 @@ function M.find_git_dir() git_dir = git_dir_cache[root_dir] break end - local git_path = root_dir .. M.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 @@ -65,12 +79,12 @@ function M.find_git_dir() git_dir = git_dir:match 'gitdir: (.+)$' file:close() -- submodule / relative file path - if git_dir and git_dir:sub(1, 1) ~= M.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 .. M.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 @@ -78,54 +92,34 @@ function M.find_git_dir() end end end - root_dir = root_dir:match('(.*)' .. M.sep .. '.-') + root_dir = root_dir:match('(.*)' .. sep .. '.-') end git_dir_cache[file_dir] = git_dir - if M.git_dir ~= git_dir then - M.git_dir = git_dir - M.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 M.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 - M.git_branch = branch - else - M.git_branch = HEAD:sub(1, 6) - end - end - return nil +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 - --- Update branch -function M.update_branch() - M.active_bufnr = tostring(vim.fn.bufnr()) - M.file_changed:stop() - local git_dir = M.git_dir - if git_dir and #git_dir > 0 then - local head_file = git_dir .. M.sep .. 'HEAD' - M.get_git_head(head_file) - M.file_changed:start( - head_file, - M.sep ~= '\\' and {} or 1000, - vim.schedule_wrap(function() - -- reset file-watch - M.update_branch() - end) - ) - else - -- set to '' when git dir was not found - M.git_branch = '' +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 - branch_cache[vim.fn.bufnr()] = M.git_branch + if bufnr then + return branch_cache[bufnr] or '' + end + return current_git_branch end return M diff --git a/lua/lualine/components/branch/init.lua b/lua/lualine/components/branch/init.lua new file mode 100644 index 0000000..1c5b6b1 --- /dev/null +++ b/lua/lualine/components/branch/init.lua @@ -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 From 6b9e9352bea4d12bdf3728dbc006a6fb5c8e461b Mon Sep 17 00:00:00 2001 From: shadmansaleh <13149513+shadmansaleh@users.noreply.github.com> Date: Mon, 11 Oct 2021 18:37:09 +0600 Subject: [PATCH 8/8] refactor: split diff component (#81) --- lua/lualine/components/diff.lua | 217 ----------------------- lua/lualine/components/diff/git_diff.lua | 135 ++++++++++++++ lua/lualine/components/diff/init.lua | 88 +++++++++ 3 files changed, 223 insertions(+), 217 deletions(-) delete mode 100644 lua/lualine/components/diff.lua create mode 100644 lua/lualine/components/diff/git_diff.lua create mode 100644 lua/lualine/components/diff/init.lua diff --git a/lua/lualine/components/diff.lua b/lua/lualine/components/diff.lua deleted file mode 100644 index 246c983..0000000 --- a/lua/lualine/components/diff.lua +++ /dev/null @@ -1,217 +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 M = lualine_require.require('lualine.component'):extend() - --- Vars --- variable to store git diff stats -M.git_diff = nil --- accumulates output from diff process -M.diff_output_cache = {} --- variable to store git_diff job -M.diff_job = nil -M.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 -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 - - M.diff_checker_enabled = type(self.options.source) ~= 'function' - - if M.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()") - M.update_diff_args() - end -end - --- Function that runs everytime statusline is updated -function M:update_status(is_focused) - local git_diff - if M.diff_checker_enabled then - if M.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 - git_diff = M.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 M.get_sign_count() - if M.diff_checker_enabled then - M.update_diff_args() - end - return M.git_diff or { added = -1, modified = -1, removed = -1 } -end - --- process diff data and update git_diff{ added, removed, modified } -function M.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 - M.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 - M.active_bufnr = tostring(vim.fn.bufnr()) - if #vim.fn.expand '%' == 0 then - M.diff_args = nil - M.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 - M.diff_output_cache = vim.list_extend(M.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 - M.git_diff = nil - M.diff_output_cache = {} - end - end, - on_exit = function() - if #M.diff_output_cache > 0 then - M.process_diff(M.diff_output_cache) - else - M.git_diff = { added = 0, modified = 0, removed = 0 } - end - diff_cache[vim.fn.bufnr()] = M.git_diff - end, - } - M.update_git_diff() -end - --- Update git_diff veriable -function M.update_git_diff() - if M.diff_args then - M.diff_output_cache = {} - if M.diff_job then - M.diff_job:stop() - end - M.diff_job = modules.Job(M.diff_args) - if M.diff_job then - M.diff_job:start() - end - end -end - -return M diff --git a/lua/lualine/components/diff/git_diff.lua b/lua/lualine/components/diff/git_diff.lua new file mode 100644 index 0000000..8507aa0 --- /dev/null +++ b/lua/lualine/components/diff/git_diff.lua @@ -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 diff --git a/lua/lualine/components/diff/init.lua b/lua/lualine/components/diff/init.lua new file mode 100644 index 0000000..0e584d1 --- /dev/null +++ b/lua/lualine/components/diff/init.lua @@ -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