From 1b81b0021fa133ef6536faacb1789f170b9b4721 Mon Sep 17 00:00:00 2001 From: Shadman Date: Sun, 11 Apr 2021 14:20:41 +0600 Subject: [PATCH] Refactor: Components now use OOP style (#141) --- lua/lualine/component.lua | 135 ++++++++++ lua/lualine/components/branch.lua | 78 +++--- lua/lualine/components/diagnostics.lua | 196 +++++++------- lua/lualine/components/diff.lua | 246 +++++++++--------- lua/lualine/components/encoding.lua | 9 +- lua/lualine/components/fileformat.lua | 31 +-- lua/lualine/components/filename.lua | 69 ++--- lua/lualine/components/filetype.lua | 32 +-- lua/lualine/components/hostname.lua | 9 +- lua/lualine/components/location.lua | 9 +- lua/lualine/components/mode.lua | 44 +--- lua/lualine/components/progress.lua | 9 +- .../special/eval_func_component.lua | 35 +++ .../components/special/functon_component.lua | 9 + .../components/special/vim_var_component.lua | 19 ++ lua/lualine/highlight.lua | 8 +- lua/lualine/init.lua | 80 ++---- lua/lualine/utils/component.lua | 82 ------ lua/lualine/utils/mode.lua | 45 ++++ lua/lualine/utils/section.lua | 75 +++--- lua/lualine/utils/utils.lua | 9 + 21 files changed, 666 insertions(+), 563 deletions(-) create mode 100644 lua/lualine/component.lua create mode 100644 lua/lualine/components/special/eval_func_component.lua create mode 100644 lua/lualine/components/special/functon_component.lua create mode 100644 lua/lualine/components/special/vim_var_component.lua delete mode 100644 lua/lualine/utils/component.lua create mode 100644 lua/lualine/utils/mode.lua diff --git a/lua/lualine/component.lua b/lua/lualine/component.lua new file mode 100644 index 0000000..4ea5b54 --- /dev/null +++ b/lua/lualine/component.lua @@ -0,0 +1,135 @@ +local highlight = require 'lualine.highlight' + +-- Used to provide a unique id for each component +local component_no = 1 + +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 + 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 type(self.options.separator) ~= 'string' then + if self.options.component_separators then + if self.options.self.section < 'lualine_x' then + self.options.separator = self.options.component_separators[1] + else + self.options.separator = self.options.component_separators[2] + end + end + end + end, + + create_option_highlights = function(self) + -- set custom highlights + if self.options.color then + self.options.color_highlight = highlight.create_component_highlight_group( + self.options.color, + self.options.component_name, + self.options) + end + end, + + -- set upper or lower case + apply_case = function(self) + -- Donn't work on components that emit vim statusline escaped chars + if self.status:find('%%') and not self.status:find('%%%%') then 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 + apply_padding = function(self) + local l_padding = (self.options.left_padding or self.options.padding or 1) + local r_padding = (self.options.right_padding or self.options.padding or 1) + if l_padding then + if self.status:find('%%#.*#') == 1 then + -- When component has changed the highlight at begining + -- we will add the padding after the highlight + local pre_highlight = + vim.fn.matchlist(self.status, [[\(%#.\{-\}#\)]])[2] + self.status = pre_highlight .. string.rep(' ', l_padding) .. + self.status:sub(#pre_highlight + 1, #self.status) + else + self.status = string.rep(' ', l_padding) .. self.status + end + end + if r_padding then self.status = self.status .. string.rep(' ', r_padding) end + end, + + -- Applies custom highlights for component + 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 + self.status = self.status .. default_highlight + 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) + if self.options.separator and #self.options.separator > 0 then + self.status = self.status .. self.options.separator + self.applied_separator = self.options.separator + end + end, + + strip_separator = function(self, default_highlight) + if not default_highlight then default_highlight = '' end + if not self.applied_separator then self.applied_separator = '' end + self.status = self.status:sub(1, (#self.status - + (#self.applied_separator + + #default_highlight))) + self.applied_separator = nil + return self.status + end, + + -- variable to store component output for manupulation + status = '', + -- Actual function the updates a component . Must be overwritten with component functionality + update_status = function(self) end, + + -- Driver code of the class + draw = function(self, default_highlight) + self.status = '' + local status = self:update_status() + if self.options.format then status = self.options.format(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_separator() + end + return self.status + end +} + +return Component diff --git a/lua/lualine/components/branch.lua b/lua/lualine/components/branch.lua index 23fcc82..3732438 100644 --- a/lua/lualine/components/branch.lua +++ b/lua/lualine/components/branch.lua @@ -1,12 +1,31 @@ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -local git_branch +local Branch = require('lualine.component'):new() +-- vars +Branch.git_branch = '' -- os specific path separator -local sep = package.config:sub(1, 1) +Branch.sep = package.config:sub(1, 1) +-- event watcher to watch head file +Branch.file_changed = vim.loop.new_fs_event() + +-- Initilizer +Branch.new = function(self, options, child) + local new_branch = self._parent:new(options, child or Branch) + if not new_branch.options.icon then + new_branch.options.icon = '' -- e0a0 + end + -- run watch head on load so branch is present when component is loaded + Branch.update_branch() + -- update branch state of BufEnter as different Buffer may be on different repos + vim.cmd [[autocmd BufEnter * lua require'lualine.components.branch'.update_branch()]] + return new_branch +end + +Branch.update_status = function() return Branch.git_branch end -- returns full path to git directory for current directory -local function find_git_dir() +function Branch.find_git_dir() -- get file dir so we can search from that dir local file_dir = vim.fn.expand('%:p:h') .. ';' -- find .git/ folder genaral case @@ -23,7 +42,7 @@ local function find_git_dir() git_dir = git_dir:match('gitdir: (.+)$') file:close() -- submodule / relative file path - if git_dir:sub(1, 1) ~= sep and not git_dir:match('^%a:.*$') then + if git_dir:sub(1, 1) ~= Branch.sep and not git_dir:match('^%a:.*$') then git_dir = git_file:match('(.*).git') .. git_dir end end @@ -31,58 +50,37 @@ local function find_git_dir() end -- sets git_branch veriable to branch name or commit hash if not on branch -local function get_git_head(head_file) +function Branch.get_git_head(head_file) local f_head = io.open(head_file) if f_head then local HEAD = f_head:read() f_head:close() local branch = HEAD:match('ref: refs/heads/(.+)$') if branch then - git_branch = branch + Branch.git_branch = branch else - git_branch = HEAD:sub(1, 6) + Branch.git_branch = HEAD:sub(1, 6) end end return nil end --- event watcher to watch head file -local file_changed = vim.loop.new_fs_event() -local function watch_head() - file_changed:stop() - local git_dir = find_git_dir() +-- Update branch +function Branch.update_branch() + Branch.file_changed:stop() + local git_dir = Branch.find_git_dir() if #git_dir > 0 then - local head_file = git_dir .. sep .. 'HEAD' - get_git_head(head_file) - file_changed:start(head_file, {}, vim.schedule_wrap( - function() + local head_file = git_dir .. Branch.sep .. 'HEAD' + Branch.get_git_head(head_file) + Branch.file_changed:start(head_file, {}, vim.schedule_wrap( + function() -- reset file-watch - watch_head() + Branch.update_branch() end)) else - -- set to nil when git dir was not found - git_branch = nil + -- set to '' when git dir was not found + Branch.git_branch = '' end end -local function branch(options) - if not options.icon then - options.icon = '' -- e0a0 - end - - return function() - if not git_branch or #git_branch == 0 then return '' end - return git_branch - end -end - --- run watch head on load so branch is present when component is loaded -watch_head() - --- update branch state of BufEnter as different Buffer may be on different repos -vim.cmd [[autocmd BufEnter * lua require'lualine.components.branch'.lualine_branch_update()]] - -return { - init = function(options) return branch(options) end, - lualine_branch_update = watch_head -} +return Branch diff --git a/lua/lualine/components/diagnostics.lua b/lua/lualine/components/diagnostics.lua index 9dc5c11..9b6e467 100644 --- a/lua/lualine/components/diagnostics.lua +++ b/lua/lualine/components/diagnostics.lua @@ -3,13 +3,106 @@ local highlight = require('lualine.highlight') local utils = require('lualine.utils.utils') +local Diagnostics = require('lualine.component'):new() + -- LuaFormatter off -local default_color_error = '#e32636' -local default_color_warn = '#ffdf00' -local default_color_info = '#ffffff' +Diagnostics.default_colors = { + error = '#e32636', + warn = '#ffdf00', + info = '#ffffff', +} -- LuaFormatter on -local diagnostic_sources = { +-- Initializer +Diagnostics.new = function(self, options, child) + local new_diagnostics = self._parent:new(options, child or Diagnostics) + local default_symbols = new_diagnostics.options.icons_enabled and { + error = ' ', -- xf659 + warn = ' ', -- xf529 + info = ' ' -- xf7fc + } or {error = 'E:', warn = 'W:', info = 'I:'} + new_diagnostics.symbols = vim.tbl_extend('force', default_symbols, + new_diagnostics.options.symbols or {}) + if new_diagnostics.options.sources == nil then + print('no sources for diagnostics configured') + return '' + end + if new_diagnostics.options.sections == nil then + new_diagnostics.options.sections = {'error', 'warn', 'info'} + end + if new_diagnostics.options.colored == nil then + new_diagnostics.options.colored = true + end + -- apply colors + if not new_diagnostics.options.color_error then + new_diagnostics.options.color_error = + utils.extract_highlight_colors('DiffDelete', 'guifg') or + Diagnostics.default_colors.error + end + if not new_diagnostics.options.color_warn then + new_diagnostics.options.color_warn = + utils.extract_highlight_colors('DiffText', 'guifg') or + Diagnostics.default_colors.warn + end + if not new_diagnostics.options.color_info then + new_diagnostics.options.color_info = + utils.extract_highlight_colors('Normal', 'guifg') or + Diagnostics.default_colors.info + end + + if new_diagnostics.options.colored then + new_diagnostics.highlight_groups = { + error = highlight.create_component_highlight_group( + {fg = new_diagnostics.options.color_error}, 'diagnostics_error', + new_diagnostics.options), + warn = highlight.create_component_highlight_group( + {fg = new_diagnostics.options.color_warn}, 'diagnostics_warn', + new_diagnostics.options), + info = highlight.create_component_highlight_group( + {fg = new_diagnostics.options.color_info}, 'diagnostics_info', + new_diagnostics.options) + } + end + + return new_diagnostics +end + +Diagnostics.update_status = function(self) + local error_count, warning_count, info_count = 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 + end + local result = {} + local data = {error = error_count, warn = warning_count, info = info_count} + if self.options.colored then + local colors = {} + for name, hl in pairs(self.highlight_groups) do + colors[name] = 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 + if result[1] ~= nil then + return table.concat(result, ' ') + else + return '' + end +end + +Diagnostics.diagnostic_sources = { nvim_lsp = function() local error_count = vim.lsp.diagnostic.get_count(0, 'Error') local warning_count = vim.lsp.diagnostic.get_count(0, 'Warning') @@ -32,21 +125,14 @@ local diagnostic_sources = { else return 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 } -local function get_diagnostics(sources) +Diagnostics.get_diagnostics = function(sources) local result = {} for index, source in ipairs(sources) do - local error_count, warning_count, info_count = diagnostic_sources[source]() + local error_count, warning_count, info_count = + Diagnostics.diagnostic_sources[source]() result[index] = { error = error_count, warn = warning_count, @@ -56,84 +142,4 @@ local function get_diagnostics(sources) return result end -local function diagnostics(options) - local default_symbols = options.icons_enabled and { - error = ' ', -- xf659 - warn = ' ', -- xf529 - info = ' ' -- xf7fc - } or {error = 'E:', warn = 'W:', info = 'I:'} - options.symbols = vim.tbl_extend('force', default_symbols, - options.symbols or {}) - if options.sources == nil then - print('no sources for diagnostics configured') - return '' - end - if options.sections == nil then options.sections = {'error', 'warn', 'info'} end - if options.colored == nil then options.colored = true end - -- apply colors - if not options.color_error then - options.color_error = - utils.extract_highlight_colors('DiffDelete', 'guifg') or - default_color_error - end - if not options.color_warn then - options.color_warn = utils.extract_highlight_colors('DiffText', 'guifg') or - default_color_warn - end - if not options.color_info then - options.color_info = utils.extract_highlight_colors('Normal', 'guifg') or - default_color_info - end - - local highlight_groups = {} - if options.colored then - highlight_groups = { - error = highlight.create_component_highlight_group( - {fg = options.color_error}, 'diagnostics_error', options), - warn = highlight.create_component_highlight_group( - {fg = options.color_warn}, 'diagnostics_warn', options), - info = highlight.create_component_highlight_group( - {fg = options.color_info}, 'diagnostics_info', options) - } - end - - return function() - local error_count, warning_count, info_count = 0, 0, 0 - local diagnostic_data = get_diagnostics(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 - end - local result = {} - local data = {error = error_count, warn = warning_count, info = info_count} - if options.colored then - local colors = {} - for name, hl in pairs(highlight_groups) do - colors[name] = highlight.component_format_highlight(hl) - end - for _, section in ipairs(options.sections) do - if data[section] ~= nil and data[section] > 0 then - table.insert(result, colors[section] .. options.symbols[section] .. - data[section]) - end - end - else - for _, section in ipairs(options.sections) do - if data[section] ~= nil and data[section] > 0 then - table.insert(result, options.symbols[section] .. data[section]) - end - end - end - if result[1] ~= nil then - return table.concat(result, ' ') - else - return '' - end - end -end - -return { - init = function(options) return diagnostics(options) end, - get_diagnostics = get_diagnostics -} +return Diagnostics diff --git a/lua/lualine/components/diff.lua b/lua/lualine/components/diff.lua index c039e68..1bbf21c 100644 --- a/lua/lualine/components/diff.lua +++ b/lua/lualine/components/diff.lua @@ -4,13 +4,120 @@ local async = require 'lualine.utils.async' local utils = require 'lualine.utils.utils' local highlight = require 'lualine.highlight' +local Diff = require('lualine.component'):new() + +-- Vars -- variable to store git diff stats -local git_diff = nil +Diff.git_diff = nil -- accumulates async output to process in the end -local diff_data = '' +Diff.diff_data = '' +-- variable to store git_diff getter async function +Diff.get_git_diff = nil +-- default colors +Diff.default_colors = { + added = '#f0e130', + removed = '#90ee90', + modified = '#ff0038' +} + +-- Initializer +Diff.new = function(self, options, child) + local new_instence = self._parent:new(options, child or Diff) + local default_symbols = {added = '+', modified = '~', removed = '-'} + new_instence.options.symbols = vim.tbl_extend('force', default_symbols, + new_instence.options.symbols or + {}) + if new_instence.options.colored == nil then + new_instence.options.colored = true + end + -- apply colors + if not new_instence.options.color_added then + new_instence.options.color_added = utils.extract_highlight_colors('DiffAdd', + 'guifg') or + Diff.default_colors.added + end + if not new_instence.options.color_modified then + new_instence.options.color_modified = + utils.extract_highlight_colors('DiffChange', 'guifg') or + Diff.default_colors.modified + end + if not new_instence.options.color_removed then + new_instence.options.color_removed = + utils.extract_highlight_colors('DiffDelete', 'guifg') or + Diff.default_colors.removed + end + + -- create highlights and save highlight_name in highlights table + if new_instence.options.colored then + new_instence.highlights = { + added = highlight.create_component_highlight_group( + {fg = new_instence.options.color_added}, 'diff_added', + new_instence.options), + modified = highlight.create_component_highlight_group( + {fg = new_instence.options.color_modified}, 'diff_modified', + new_instence.options), + removed = highlight.create_component_highlight_group( + {fg = new_instence.options.color_removed}, 'diff_removed', + new_instence.options) + } + end + + vim.api.nvim_exec([[ + autocmd lualine BufEnter * lua require'lualine.components.diff'.update_git_diff_getter() + autocmd lualine BufEnter * lua require'lualine.components.diff'.update_git_diff() + autocmd lualine BufWritePost * lua require'lualine.components.diff'.update_git_diff() + ]], false) + + return new_instence +end + +-- Function that runs everytime statusline is updated +Diff.update_status = function(self) + if Diff.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] = 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 Diff.git_diff[name] and Diff.git_diff[name] > 0 then + if self.options.colored then + table.insert(result, colors[name] .. self.options.symbols[name] .. + Diff.git_diff[name]) + else + table.insert(result, self.options.symbols[name] .. Diff.git_diff[name]) + end + end + end + if #result > 0 then + return table.concat(result, ' ') + else + return '' + end +end + +-- Api to get git sign count +-- scheme : +-- { +-- added = added_count, +-- modified = modified_count, +-- removed = removed_count, +-- } +-- error_code = { added = -1, modified = -1, removed = -1 } +function Diff.get_sign_count() + Diff.update_git_diff_getter() + Diff.update_git_diff() + return Diff.git_diff or {added = -1, modified = -1, removed = -1} +end -- process diff data and update git_diff{ added, removed, modified } -local function process_diff(data) +function Diff.process_diff(data) -- Adapted from https://github.com/wbthomason/nvim-vcs.lua local added, removed, modified = 0, 0, 0 for line in vim.gsplit(data, '\n') do @@ -34,149 +141,50 @@ local function process_diff(data) end end end - git_diff = {added = added, modified = modified, removed = removed} + Diff.git_diff = {added = added, modified = modified, removed = removed} end --- variable to store git_diff getter async function -local get_git_diff = nil - -- Updates the async function for current file -local function update_git_diff_getter() +function Diff.update_git_diff_getter() -- stop older function properly before overwritting it - if get_git_diff then get_git_diff:stop() end + if Diff.get_git_diff then Diff.get_git_diff:stop() end -- Donn't show git diff when current buffer doesn't have a filename if #vim.fn.expand('%') == 0 then - get_git_diff = nil; - git_diff = nil; + Diff.get_git_diff = nil; + Diff.git_diff = nil; return end - get_git_diff = async:new({ + Diff.get_git_diff = async:new({ 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 data then diff_data = diff_data .. data end + if data then Diff.diff_data = Diff.diff_data .. data end end, on_stderr = function(_, data) if data then - git_diff = nil - diff_data = '' + Diff.git_diff = nil + Diff.diff_data = '' end end, on_exit = function() - if diff_data ~= '' then - process_diff(diff_data) + if Diff.diff_data ~= '' then + Diff.process_diff(Diff.diff_data) else - git_diff = {added = 0, modified = 0, removed = 0} + Diff.git_diff = {added = 0, modified = 0, removed = 0} end end }) end -- Update git_diff veriable -local function update_git_diff() +function Diff.update_git_diff() vim.schedule_wrap(function() - if get_git_diff then - diff_data = '' - get_git_diff:start() + if Diff.get_git_diff then + Diff.diff_data = '' + Diff.get_git_diff:start() end end)() end -local default_color_added = '#f0e130' -local default_color_removed = '#90ee90' -local default_color_modified = '#ff0038' - -local function diff(options) - if options.colored == nil then options.colored = true end - -- apply colors - if not options.color_added then - options.color_added = utils.extract_highlight_colors('DiffAdd', 'guifg') or - default_color_added - end - if not options.color_modified then - options.color_modified = utils.extract_highlight_colors('DiffChange', - 'guifg') or - default_color_modified - end - if not options.color_removed then - options.color_removed = - utils.extract_highlight_colors('DiffDelete', 'guifg') or - default_color_removed - end - - local highlights = {} - - -- create highlights and save highlight_name in highlights table - if options.colored then - highlights = { - added = highlight.create_component_highlight_group( - {fg = options.color_added}, 'diff_added', options), - modified = highlight.create_component_highlight_group( - {fg = options.color_modified}, 'diff_modified', options), - removed = highlight.create_component_highlight_group( - {fg = options.color_removed}, 'diff_removed', options) - } - end - - vim.api.nvim_exec([[ - autocmd lualine BufEnter * lua require'lualine.components.diff'.update_git_diff_getter() - autocmd lualine BufEnter * lua require'lualine.components.diff'.update_git_diff() - autocmd lualine BufWritePost * lua require'lualine.components.diff'.update_git_diff() - ]], false) - - -- Function that runs everytime statusline is updated - return function() - if git_diff == nil then return '' end - - local default_symbols = {added = '+', modified = '~', removed = '-'} - options.symbols = vim.tbl_extend('force', default_symbols, - options.symbols or {}) - local colors = {} - if options.colored then - -- load the highlights and store them in colors table - for name, highlight_name in pairs(highlights) do - colors[name] = 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 options.colored then - table.insert(result, - colors[name] .. options.symbols[name] .. git_diff[name]) - else - table.insert(result, options.symbols[name] .. git_diff[name]) - end - end - end - if #result > 0 then - return table.concat(result, ' ') - else - return '' - end - 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 } -local function get_sign_count() - update_git_diff_getter() - update_git_diff() - return git_diff or {added = -1, modified = -1, removed = -1} -end - -return { - init = function(options) return diff(options) end, - update_git_diff = update_git_diff, - update_git_diff_getter = update_git_diff_getter, - get_sign_count = get_sign_count -} +return Diff diff --git a/lua/lualine/components/encoding.lua b/lua/lualine/components/encoding.lua index 9c4f45f..b792c96 100644 --- a/lua/lualine/components/encoding.lua +++ b/lua/lualine/components/encoding.lua @@ -1,8 +1,7 @@ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. -local function encoding() - local data = [[%{strlen(&fenc)?&fenc:&enc}]] - return data -end +local Encoding = require('lualine.component'):new() -return encoding +Encoding.update_status = function() return [[%{strlen(&fenc)?&fenc:&enc}]] end + +return Encoding diff --git a/lua/lualine/components/fileformat.lua b/lua/lualine/components/fileformat.lua index 98d7e41..6a11c11 100644 --- a/lua/lualine/components/fileformat.lua +++ b/lua/lualine/components/fileformat.lua @@ -1,22 +1,19 @@ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -local function fileformat(options) - local icon_linux = '' -- e712 - local icon_windos = '' -- e70f - local icon_mac = '' -- e711 - return function() - if options.icons_enabled and not options.icon then - local format = vim.bo.fileformat - if format == 'unix' then - return icon_linux - elseif format == 'dos' then - return icon_windos - elseif format == 'mac' then - return icon_mac - end - end - return vim.bo.fileformat +local FileFormat = require('lualine.component'):new() + +FileFormat.icon = { + unix = '', -- e712 + dos = '', -- e70f + mac = '' -- e711 +} + +FileFormat.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 end + return vim.bo.fileformat end -return {init = function(options) return fileformat(options) end} +return FileFormat diff --git a/lua/lualine/components/filename.lua b/lua/lualine/components/filename.lua index 35d6bb3..deb452d 100644 --- a/lua/lualine/components/filename.lua +++ b/lua/lualine/components/filename.lua @@ -1,35 +1,46 @@ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -local function filename(options) +local FileName = require('lualine.component'):new() + +FileName.new = function(self, options, child) + local new_instence = self._parent:new(options, child or FileName) + -- setting defaults - if options.file_status == nil then options.file_status = true end - if options.shorten == nil then options.shorten = true end - if options.full_path == nil then options.full_path = false end - - return function() - local data - if not options.full_path then - data = vim.fn.expand('%:t') - elseif options.shorten then - data = vim.fn.expand('%:~:.') - else - data = vim.fn.expand('%:p') - end - if data == '' then - data = '[No Name]' - elseif vim.fn.winwidth(0) <= 84 or #data > 40 then - data = vim.fn.pathshorten(data) - end - - if options.file_status then - if vim.bo.modified then - data = data .. '[+]' - elseif vim.bo.modifiable == false or vim.bo.readonly == true then - data = data .. '[-]' - end - end - return data + if new_instence.options.file_status == nil then + new_instence.options.file_status = true end + if new_instence.options.shorten == nil then + new_instence.options.shorten = true + end + if new_instence.options.full_path == nil then + new_instence.options.full_path = false + end + + return new_instence end -return {init = function(options) return filename(options) end} +FileName.update_status = function(self) + local data = vim.fn.expand('%:p') + if not self.options.full_path then + data = vim.fn.expand('%:t') + elseif self.options.shorten then + data = vim.fn.expand('%:~:.') + end + + if data == '' then + data = '[No Name]' + elseif vim.fn.winwidth(0) <= 84 or #data > 40 then + data = vim.fn.pathshorten(data) + end + + if self.options.file_status then + if vim.bo.modified then + data = data .. '[+]' + elseif vim.bo.modifiable == false or vim.bo.readonly == true then + data = data .. '[-]' + end + end + return data +end + +return FileName diff --git a/lua/lualine/components/filetype.lua b/lua/lualine/components/filetype.lua index a4d6455..e63411a 100644 --- a/lua/lualine/components/filetype.lua +++ b/lua/lualine/components/filetype.lua @@ -1,23 +1,23 @@ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. -local function filetype(options) - return function() - local data = vim.bo.filetype - if #data > 0 then - local ok, devicons = pcall(require, 'nvim-web-devicons') - if ok then - local f_name, f_extension = vim.fn.expand('%:t'), vim.fn.expand('%:e') - options.icon = devicons.get_icon(f_name, f_extension) - else - ok = vim.fn.exists('*WebDevIconsGetFileTypeSymbol') - if ok ~= 0 then - options.icon = vim.fn.WebDevIconsGetFileTypeSymbol() - end +local FileType = require('lualine.component'):new() + +FileType.update_status = function(self) + local data = vim.bo.filetype + if #data > 0 then + local ok, devicons = pcall(require, 'nvim-web-devicons') + if ok then + local f_name, f_extension = vim.fn.expand('%:t'), vim.fn.expand('%:e') + self.options.icon = devicons.get_icon(f_name, f_extension) + else + ok = vim.fn.exists('*WebDevIconsGetFileTypeSymbol') + if ok ~= 0 then + self.options.icon = vim.fn.WebDevIconsGetFileTypeSymbol() end - return data end - return '' + return data end + return '' end -return {init = function(options) return filetype(options) end} +return FileType diff --git a/lua/lualine/components/hostname.lua b/lua/lualine/components/hostname.lua index baf48bd..897ce12 100644 --- a/lua/lualine/components/hostname.lua +++ b/lua/lualine/components/hostname.lua @@ -1,8 +1,7 @@ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. -local function hostname() - local data = vim.loop.os_gethostname() - return data -end +local HostName = require('lualine.component'):new() -return hostname +HostName.update_status = vim.loop.os_gethostname() + +return HostName diff --git a/lua/lualine/components/location.lua b/lua/lualine/components/location.lua index 12d8978..9a8d0a5 100644 --- a/lua/lualine/components/location.lua +++ b/lua/lualine/components/location.lua @@ -1,8 +1,7 @@ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. -local function location() - local data = [[%3l:%-2c]] - return data -end +local Location = require('lualine.component'):new() -return location +Location.update_status = function() return [[%3l:%-2c]] end + +return Location diff --git a/lua/lualine/components/mode.lua b/lua/lualine/components/mode.lua index 9baf8ad..06ffce4 100644 --- a/lua/lualine/components/mode.lua +++ b/lua/lualine/components/mode.lua @@ -1,42 +1,8 @@ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. --- LuaFormatter off -local mode_map = { - ['n'] = 'NORMAL', - ['no'] = 'O-PENDING', - ['nov'] = 'O-PENDING', - ['noV'] = 'O-PENDING', - ['no'] = 'O-PENDING', - ['niI'] = 'NORMAL', - ['niR'] = 'NORMAL', - ['niV'] = 'NORMAL', - ['v'] = 'VISUAL', - ['V'] = 'V-LINE', - [''] = 'V-BLOCK', - ['s'] = 'SELECT', - ['S'] = 'S-LINE', - [''] = 'S-BLOCK', - ['i'] = 'INSERT', - ['ic'] = 'INSERT', - ['ix'] = 'INSERT', - ['R'] = 'REPLACE', - ['Rc'] = 'REPLACE', - ['Rv'] = 'V-REPLACE', - ['Rx'] = 'REPLACE', - ['c'] = 'COMMAND', - ['cv'] = 'EX', - ['ce'] = 'EX', - ['r'] = 'REPLACE', - ['rm'] = 'MORE', - ['r?'] = 'CONFIRM', - ['!'] = 'SHELL', - ['t'] = 'TERMINAL', -} --- LuaFormatter on -local function mode() - local mode_code = vim.api.nvim_get_mode().mode - if mode_map[mode_code] == nil then return mode_code end - return mode_map[mode_code] -end +local Mode = require('lualine.component'):new() +local get_mode = require('lualine.utils.mode').get_mode -return mode +Mode.update_status = get_mode + +return Mode diff --git a/lua/lualine/components/progress.lua b/lua/lualine/components/progress.lua index c504d7e..2a046e7 100644 --- a/lua/lualine/components/progress.lua +++ b/lua/lualine/components/progress.lua @@ -1,8 +1,7 @@ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. -local function progress() - local data = [[%3P]] - return data -end +local Progress = require('lualine.component'):new() -return progress +Progress.update_status = function() return [[%3P]] end + +return Progress diff --git a/lua/lualine/components/special/eval_func_component.lua b/lua/lualine/components/special/eval_func_component.lua new file mode 100644 index 0000000..93e1234 --- /dev/null +++ b/lua/lualine/components/special/eval_func_component.lua @@ -0,0 +1,35 @@ +local EvalFuncComponent = require('lualine.component'):new() + +EvalFuncComponent.update_status = function(self) + local component = self.options[1] + local ok, status = EvalFuncComponent.evallua(component) + if not ok then status = EvalFuncComponent.vim_function(component) end + return status +end + +EvalFuncComponent.evallua = function(code) + if loadstring(string.format('return %s ~= nil', code)) and + loadstring(string.format([[return %s ~= nil]], code))() then + -- lua veriable component + return true, loadstring(string.format( + [[ + local ok, return_val = pcall(tostring, %s) + if ok then return return_val end + return '']], code))() + end + return false, '' +end + +EvalFuncComponent.vim_function = function(name) + -- vim function component + local ok, return_val = pcall(vim.fn[name]) + if not ok then return '' end -- function call failed + ok, return_val = pcall(tostring, return_val) + if ok then + return return_val + else + return '' + end +end + +return EvalFuncComponent diff --git a/lua/lualine/components/special/functon_component.lua b/lua/lualine/components/special/functon_component.lua new file mode 100644 index 0000000..820d566 --- /dev/null +++ b/lua/lualine/components/special/functon_component.lua @@ -0,0 +1,9 @@ +local FunctionComponent = require('lualine.component'):new() + +FunctionComponent.new = function(self, options, child) + local new_instence = self._parent:new(options, child or FunctionComponent) + new_instence.update_status = options[1] + return new_instence +end + +return FunctionComponent diff --git a/lua/lualine/components/special/vim_var_component.lua b/lua/lualine/components/special/vim_var_component.lua new file mode 100644 index 0000000..245d130 --- /dev/null +++ b/lua/lualine/components/special/vim_var_component.lua @@ -0,0 +1,19 @@ +local VarComponent = require('lualine.component'):new() +VarComponent.update_status = function(self) + local component = self.options[1] + -- vim veriable component + -- accepts g:, v:, t:, w:, b:, o, go:, vo:, to:, wo:, bo: + -- filters g portion from g:var + local scope = component:match('[gvtwb]?o?') + -- filters var portion from g:var + local var_name = component:sub(#scope + 2, #component) + -- Displays nothing when veriable aren't present + local return_val = vim[scope][var_name] + if return_val == nil then return '' end + local ok + ok, return_val = pcall(tostring, return_val) + if ok then return return_val end + return '' +end + +return VarComponent diff --git a/lua/lualine/highlight.lua b/lua/lualine/highlight.lua index f2e9be5..80f5290 100644 --- a/lua/lualine/highlight.lua +++ b/lua/lualine/highlight.lua @@ -42,7 +42,7 @@ end -- @param highlight_group:(string) name of highlight group -- @return: (string) highlight group name with mode local function append_mode(highlight_group) - local mode = require('lualine.components.mode')() + local mode = require('lualine.utils.mode').get_mode() if mode == 'VISUAL' or mode == 'V-BLOCK' or mode == 'V-LINE' or mode == 'SELECT' or mode == 'S-LINE' or mode == 'S-BLOCK' then highlight_group = highlight_group .. '_visual' @@ -159,7 +159,7 @@ function M.get_transitional_highlights(left_section_data, right_section_data, -- Grab the last highlighter of left section if left_section_data then -- extract highlight_name from .....%#highlight_name# - left_highlight_name = left_section_data:match('.*%%#(.*)#') + left_highlight_name = left_section_data:match('.*%%#(.-)#') else -- When right section us unavailable default to lualine_c left_highlight_name = append_mode('lualine_c') @@ -168,10 +168,8 @@ function M.get_transitional_highlights(left_section_data, right_section_data, end end if right_section_data then - -- using vim-regex cause lua-paterns don't have non-greedy matching -- extract highlight_name from %#highlight_name#.... - right_highlight_name = vim.fn.matchlist(right_section_data, - [[%#\(.\{-\}\)#]])[2] + right_highlight_name = right_section_data:match('%%#(.-)#.*') else -- When right section us unavailable default to lualine_c right_highlight_name = append_mode('lualine_c') diff --git a/lua/lualine/init.lua b/lua/lualine/init.lua index cedf3ab..13d30b1 100644 --- a/lua/lualine/init.lua +++ b/lua/lualine/init.lua @@ -57,70 +57,25 @@ local function check_single_separator() end end -local function load_special_components(component) - return function() - -- precedence lualine_component > vim_var > lua_var > vim_function - if component:find('[gvtwb]?o?:') == 1 then - -- vim veriable component - -- accepts g:, v:, t:, w:, b:, o, go:, vo:, to:, wo:, bo: - -- filters g portion from g:var - local scope = component:match('[gvtwb]?o?') - -- filters var portion from g:var - local var_name = component:sub(#scope + 2, #component) - -- Displays nothing when veriable aren't present - local return_val = vim[scope][var_name] - if return_val == nil then return '' end - local ok - ok, return_val = pcall(tostring, return_val) - if ok then return return_val end - return '' - elseif loadstring(string.format('return %s ~= nil', component)) and - loadstring(string.format([[return %s ~= nil]], component))() then - -- lua veriable component - return loadstring(string.format([[ - local ok, return_val = pcall(tostring, %s) - if ok then return return_val end - return '']], component))() - else - -- vim function component - local ok, return_val = pcall(vim.fn[component]) - if not ok then return '' end -- function call failed - ok, return_val = pcall(tostring, return_val) - if ok then - return return_val - else - return '' - end - end - end -end - local function component_loader(component) - if type(component[1]) == 'function' then return component end + if type(component[1]) == 'function' then + return require 'lualine.components.special.functon_component':new(component) + end if type(component[1]) == 'string' then - -- Keep component name for later use as component[1] will be overwritten - -- With component function - component.component_name = component[1] - -- apply default args - for opt_name, opt_val in pairs(config.options) do - if component[opt_name] == nil then component[opt_name] = opt_val end - end -- load the component - local ok, loaded_component = pcall(require, 'lualine.components.' .. - component.component_name) - if not ok then - loaded_component = load_special_components(component.component_name) - end - component[1] = loaded_component - if type(component[1]) == 'table' then - component[1] = component[1].init(component) - end - -- set custom highlights - if component.color then - component.color_highlight = highlight.create_component_highlight_group( - component.color, component.component_name, - 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) + elseif component[1]:find('[gvtwb]?o?:') == 1 then + loaded_component = + require 'lualine.components.special.vim_var_component':new(component) + else + loaded_component = + require 'lualine.components.special.eval_func_component':new(component) end + return loaded_component end end @@ -132,8 +87,9 @@ local function load_sections(sections) end component.self = {} component.self.section = section_name - component_loader(component) - section[index] = component + -- apply default args + component = vim.tbl_extend('keep', component, config.options) + section[index] = component_loader(component) end end end diff --git a/lua/lualine/utils/component.lua b/lua/lualine/utils/component.lua deleted file mode 100644 index 5b79287..0000000 --- a/lua/lualine/utils/component.lua +++ /dev/null @@ -1,82 +0,0 @@ --- Copyright (c) 2020-2021 shadmansaleh --- MIT license, see LICENSE for more details. -local M = {} - -local highlight = require 'lualine.highlight' - --- set upper or lower case -function M.apply_case(status, options) - -- Donn't work on components that emit vim statusline escaped chars - if status:find('%%') and not status:find('%%%%') then return status end - if options.upper == true then - return status:upper() - elseif options.lower == true then - return status:lower() - end - return status -end - --- Adds spaces to left and right of a component -function M.apply_padding(status, options) - local l_padding = (options.left_padding or options.padding or 1) - local r_padding = (options.right_padding or options.padding or 1) - if l_padding then - if 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(status, [[\(%#.\{-\}#\)]])[2] - status = pre_highlight .. string.rep(' ', l_padding) .. - status:sub(#pre_highlight + 1, #status) - else - status = string.rep(' ', l_padding) .. status - end - end - if r_padding then status = status .. string.rep(' ', r_padding) end - return status -end - --- Applies custom highlights for component -function M.apply_highlights(status, options, default_hl) - if options.color_highlight then - status = highlight.component_format_highlight(options.color_highlight) .. - status - end - return status .. default_hl -end - --- Apply icon in front of component -function M.apply_icon(status, options) - if options.icons_enabled and options.icon then - status = options.icon .. ' ' .. status - end - return status -end - --- Apply separator at end of component only when --- custom highlights haven't affected background -function M.apply_spearator(status, options) - local separator - if options.separator and #options.separator > 0 then - separator = options.separator - elseif options.component_separators then - if options.self.section < 'lualine_x' then - separator = options.component_separators[1] - else - separator = options.component_separators[2] - end - options.separator = separator - end - if separator then status = status .. separator end - options.separator_applied = separator - return status -end - -function M.strip_separator(status, options) - if options.separator_applied then - status = status:sub(1, #status - #options.separator_applied) - options.separator_applied = nil - end - return status -end - -return M diff --git a/lua/lualine/utils/mode.lua b/lua/lualine/utils/mode.lua new file mode 100644 index 0000000..e7f6c62 --- /dev/null +++ b/lua/lualine/utils/mode.lua @@ -0,0 +1,45 @@ +-- Copyright (c) 2020-2021 hoob3rt +-- MIT license, see LICENSE for more details. +-- LuaFormatter off + +local Mode = {} + +Mode.map = { + ['n'] = 'NORMAL', + ['no'] = 'O-PENDING', + ['nov'] = 'O-PENDING', + ['noV'] = 'O-PENDING', + ['no'] = 'O-PENDING', + ['niI'] = 'NORMAL', + ['niR'] = 'NORMAL', + ['niV'] = 'NORMAL', + ['v'] = 'VISUAL', + ['V'] = 'V-LINE', + [''] = 'V-BLOCK', + ['s'] = 'SELECT', + ['S'] = 'S-LINE', + [''] = 'S-BLOCK', + ['i'] = 'INSERT', + ['ic'] = 'INSERT', + ['ix'] = 'INSERT', + ['R'] = 'REPLACE', + ['Rc'] = 'REPLACE', + ['Rv'] = 'V-REPLACE', + ['Rx'] = 'REPLACE', + ['c'] = 'COMMAND', + ['cv'] = 'EX', + ['ce'] = 'EX', + ['r'] = 'REPLACE', + ['rm'] = 'MORE', + ['r?'] = 'CONFIRM', + ['!'] = 'SHELL', + ['t'] = 'TERMINAL', +} +-- LuaFormatter on +function Mode.get_mode() + local mode_code = vim.api.nvim_get_mode().mode + if Mode.map[mode_code] == nil then return mode_code end + return Mode.map[mode_code] +end + +return Mode diff --git a/lua/lualine/utils/section.lua b/lua/lualine/utils/section.lua index ab87ced..0ba3cc4 100644 --- a/lua/lualine/utils/section.lua +++ b/lua/lualine/utils/section.lua @@ -1,52 +1,49 @@ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. -local utils_component = require('lualine.utils.component') local M = {} +local utils = require('lualine.utils.utils') -- Returns formated string for a section function M.draw_section(section, highlight_name) local status = {} - local drawn_components = {} for _, component in pairs(section) do - local localstatus = component[1]() - if #localstatus > 0 then - local custom_highlight_at_begining = - localstatus:find('%%#.*#') == 1 or component.color ~= nil - -- Apply modifier functions for options - if component.format then localstatus = component.format(localstatus) end - localstatus = utils_component.apply_icon(localstatus, component) - localstatus = utils_component.apply_case(localstatus, component) - localstatus = utils_component.apply_padding(localstatus, component) - localstatus = utils_component.apply_highlights(localstatus, component, - highlight_name) - localstatus = utils_component.apply_spearator(localstatus, component) - if custom_highlight_at_begining or (#drawn_components > 0 and - not drawn_components[#drawn_components].separator_applied) then - -- Don't prepend with old highlight when the component changes it imidiately - -- Or when it was already applied with separator - table.insert(status, localstatus) - else - table.insert(status, highlight_name .. localstatus) - end - table.insert(drawn_components, component) + -- load components into status table + table.insert(status, component:draw(highlight_name)) + end + + -- Flags required for knowing when to remove component separator + local next_component_colored = false + local last_component_found = false + + -- Check through components to see when component separator need to be removed + for component_no = #section, 1, -1 do + -- Remove component separator with highlight for last component + if not last_component_found and #status[component_no] > 0 then + last_component_found = true + status[component_no] = section[component_no]:strip_separator( + highlight_name) + end + -- Remove component separator when color option is used in next component + if next_component_colored then + next_component_colored = false + status[component_no] = section[component_no]:strip_separator() + end + -- Remove component separator when color option is used to color background + if section[component_no].options.color and + section[component_no].options.color.bg then + next_component_colored = true + status[component_no] = section[component_no]:strip_separator() end end - -- Draw nothing when all the components were empty - if #status == 0 then return '' end - -- Remove separators sorounding custom highlighted component - for i = 1, #status do - if (drawn_components[i].color and drawn_components[i].color.bg) or - drawn_components[i].custom_highlight then - status[i] = - utils_component.strip_separator(status[i], drawn_components[i]) - if i > 1 then - status[i - 1] = utils_component.strip_separator(status[i - 1], - drawn_components[i - 1]) - end - end + + -- Remove empty strings from status + status = utils.list_shrink(status) + local status_str = table.concat(status) + if status_str:find('%%#.*#') == 1 then + -- Don't prepend with old highlight when the component changes it imidiately + return status_str + else + return highlight_name .. status_str end - status[#status] = utils_component.strip_separator(status[#status], - drawn_components[#status]) - return table.concat(status) end return M diff --git a/lua/lualine/utils/utils.lua b/lua/lualine/utils/utils.lua index f5466ed..d07de8b 100644 --- a/lua/lualine/utils/utils.lua +++ b/lua/lualine/utils/utils.lua @@ -54,4 +54,13 @@ function M.clear_highlights() end end +-- remove empty strings from list +function M.list_shrink(list) + local new_list = {} + for i = 1, #list do + if list[i] and #list[i] > 0 then table.insert(new_list, list[i]) end + end + return new_list +end + return M