Refactor: Components now use OOP style (#141)

This commit is contained in:
Shadman 2021-04-11 14:20:41 +06:00 committed by GitHub
parent 2b32fb090f
commit 1b81b0021f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 666 additions and 563 deletions

135
lua/lualine/component.lua Normal file
View File

@ -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

View File

@ -1,12 +1,31 @@
-- Copyright (c) 2020-2021 shadmansaleh -- Copyright (c) 2020-2021 shadmansaleh
-- MIT license, see LICENSE for more details. -- MIT license, see LICENSE for more details.
local git_branch local Branch = require('lualine.component'):new()
-- vars
Branch.git_branch = ''
-- os specific path separator -- 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 -- 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 -- get file dir so we can search from that dir
local file_dir = vim.fn.expand('%:p:h') .. ';' local file_dir = vim.fn.expand('%:p:h') .. ';'
-- find .git/ folder genaral case -- find .git/ folder genaral case
@ -23,7 +42,7 @@ local function find_git_dir()
git_dir = git_dir:match('gitdir: (.+)$') git_dir = git_dir:match('gitdir: (.+)$')
file:close() file:close()
-- submodule / relative file path -- 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 git_dir = git_file:match('(.*).git') .. git_dir
end end
end end
@ -31,58 +50,37 @@ local function find_git_dir()
end end
-- sets git_branch veriable to branch name or commit hash if not on branch -- 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) local f_head = io.open(head_file)
if f_head then if f_head then
local HEAD = f_head:read() local HEAD = f_head:read()
f_head:close() f_head:close()
local branch = HEAD:match('ref: refs/heads/(.+)$') local branch = HEAD:match('ref: refs/heads/(.+)$')
if branch then if branch then
git_branch = branch Branch.git_branch = branch
else else
git_branch = HEAD:sub(1, 6) Branch.git_branch = HEAD:sub(1, 6)
end end
end end
return nil return nil
end end
-- event watcher to watch head file -- Update branch
local file_changed = vim.loop.new_fs_event() function Branch.update_branch()
local function watch_head() Branch.file_changed:stop()
file_changed:stop() local git_dir = Branch.find_git_dir()
local git_dir = find_git_dir()
if #git_dir > 0 then if #git_dir > 0 then
local head_file = git_dir .. sep .. 'HEAD' local head_file = git_dir .. Branch.sep .. 'HEAD'
get_git_head(head_file) Branch.get_git_head(head_file)
file_changed:start(head_file, {}, vim.schedule_wrap( Branch.file_changed:start(head_file, {}, vim.schedule_wrap(
function() function()
-- reset file-watch -- reset file-watch
watch_head() Branch.update_branch()
end)) end))
else else
-- set to nil when git dir was not found -- set to '' when git dir was not found
git_branch = nil Branch.git_branch = ''
end end
end end
local function branch(options) return Branch
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
}

View File

@ -3,13 +3,106 @@
local highlight = require('lualine.highlight') local highlight = require('lualine.highlight')
local utils = require('lualine.utils.utils') local utils = require('lualine.utils.utils')
local Diagnostics = require('lualine.component'):new()
-- LuaFormatter off -- LuaFormatter off
local default_color_error = '#e32636' Diagnostics.default_colors = {
local default_color_warn = '#ffdf00' error = '#e32636',
local default_color_info = '#ffffff' warn = '#ffdf00',
info = '#ffffff',
}
-- LuaFormatter on -- 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() nvim_lsp = function()
local error_count = vim.lsp.diagnostic.get_count(0, 'Error') local error_count = vim.lsp.diagnostic.get_count(0, 'Error')
local warning_count = vim.lsp.diagnostic.get_count(0, 'Warning') local warning_count = vim.lsp.diagnostic.get_count(0, 'Warning')
@ -32,21 +125,14 @@ local diagnostic_sources = {
else else
return 0, 0, 0 return 0, 0, 0
end 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 end
} }
local function get_diagnostics(sources) Diagnostics.get_diagnostics = function(sources)
local result = {} local result = {}
for index, source in ipairs(sources) do 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] = { result[index] = {
error = error_count, error = error_count,
warn = warning_count, warn = warning_count,
@ -56,84 +142,4 @@ local function get_diagnostics(sources)
return result return result
end end
local function diagnostics(options) return Diagnostics
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
}

View File

@ -4,13 +4,120 @@ local async = require 'lualine.utils.async'
local utils = require 'lualine.utils.utils' local utils = require 'lualine.utils.utils'
local highlight = require 'lualine.highlight' local highlight = require 'lualine.highlight'
local Diff = require('lualine.component'):new()
-- Vars
-- variable to store git diff stats -- variable to store git diff stats
local git_diff = nil Diff.git_diff = nil
-- accumulates async output to process in the end -- 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 } -- 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 -- Adapted from https://github.com/wbthomason/nvim-vcs.lua
local added, removed, modified = 0, 0, 0 local added, removed, modified = 0, 0, 0
for line in vim.gsplit(data, '\n') do for line in vim.gsplit(data, '\n') do
@ -34,149 +141,50 @@ local function process_diff(data)
end end
end end
end end
git_diff = {added = added, modified = modified, removed = removed} Diff.git_diff = {added = added, modified = modified, removed = removed}
end end
-- variable to store git_diff getter async function
local get_git_diff = nil
-- Updates the async function for current file -- 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 -- 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 -- Donn't show git diff when current buffer doesn't have a filename
if #vim.fn.expand('%') == 0 then if #vim.fn.expand('%') == 0 then
get_git_diff = nil; Diff.get_git_diff = nil;
git_diff = nil; Diff.git_diff = nil;
return return
end end
get_git_diff = async:new({ Diff.get_git_diff = async:new({
cmd = string.format( cmd = string.format(
[[git -C %s --no-pager diff --no-color --no-ext-diff -U0 -- %s]], [[git -C %s --no-pager diff --no-color --no-ext-diff -U0 -- %s]],
vim.fn.expand('%:h'), vim.fn.expand('%:t')), vim.fn.expand('%:h'), vim.fn.expand('%:t')),
on_stdout = function(_, data) 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, end,
on_stderr = function(_, data) on_stderr = function(_, data)
if data then if data then
git_diff = nil Diff.git_diff = nil
diff_data = '' Diff.diff_data = ''
end end
end, end,
on_exit = function() on_exit = function()
if diff_data ~= '' then if Diff.diff_data ~= '' then
process_diff(diff_data) Diff.process_diff(Diff.diff_data)
else else
git_diff = {added = 0, modified = 0, removed = 0} Diff.git_diff = {added = 0, modified = 0, removed = 0}
end end
end end
}) })
end end
-- Update git_diff veriable -- Update git_diff veriable
local function update_git_diff() function Diff.update_git_diff()
vim.schedule_wrap(function() vim.schedule_wrap(function()
if get_git_diff then if Diff.get_git_diff then
diff_data = '' Diff.diff_data = ''
get_git_diff:start() Diff.get_git_diff:start()
end end
end)() end)()
end end
local default_color_added = '#f0e130' return Diff
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
}

View File

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

View File

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

View File

@ -1,27 +1,39 @@
-- Copyright (c) 2020-2021 shadmansaleh -- Copyright (c) 2020-2021 shadmansaleh
-- MIT license, see LICENSE for more details. -- MIT license, see LICENSE for more details.
local function filename(options) local FileName = require('lualine.component'):new()
-- 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() FileName.new = function(self, options, child)
local data local new_instence = self._parent:new(options, child or FileName)
if not options.full_path then
data = vim.fn.expand('%:t') -- setting defaults
elseif options.shorten then if new_instence.options.file_status == nil then
data = vim.fn.expand('%:~:.') new_instence.options.file_status = true
else
data = vim.fn.expand('%:p')
end 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
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 if data == '' then
data = '[No Name]' data = '[No Name]'
elseif vim.fn.winwidth(0) <= 84 or #data > 40 then elseif vim.fn.winwidth(0) <= 84 or #data > 40 then
data = vim.fn.pathshorten(data) data = vim.fn.pathshorten(data)
end end
if options.file_status then if self.options.file_status then
if vim.bo.modified then if vim.bo.modified then
data = data .. '[+]' data = data .. '[+]'
elseif vim.bo.modifiable == false or vim.bo.readonly == true then elseif vim.bo.modifiable == false or vim.bo.readonly == true then
@ -29,7 +41,6 @@ local function filename(options)
end end
end end
return data return data
end
end end
return {init = function(options) return filename(options) end} return FileName

View File

@ -1,23 +1,23 @@
-- Copyright (c) 2020-2021 hoob3rt -- Copyright (c) 2020-2021 hoob3rt
-- MIT license, see LICENSE for more details. -- MIT license, see LICENSE for more details.
local function filetype(options) local FileType = require('lualine.component'):new()
return function()
FileType.update_status = function(self)
local data = vim.bo.filetype local data = vim.bo.filetype
if #data > 0 then if #data > 0 then
local ok, devicons = pcall(require, 'nvim-web-devicons') local ok, devicons = pcall(require, 'nvim-web-devicons')
if ok then if ok then
local f_name, f_extension = vim.fn.expand('%:t'), vim.fn.expand('%:e') local f_name, f_extension = vim.fn.expand('%:t'), vim.fn.expand('%:e')
options.icon = devicons.get_icon(f_name, f_extension) self.options.icon = devicons.get_icon(f_name, f_extension)
else else
ok = vim.fn.exists('*WebDevIconsGetFileTypeSymbol') ok = vim.fn.exists('*WebDevIconsGetFileTypeSymbol')
if ok ~= 0 then if ok ~= 0 then
options.icon = vim.fn.WebDevIconsGetFileTypeSymbol() self.options.icon = vim.fn.WebDevIconsGetFileTypeSymbol()
end end
end end
return data return data
end end
return '' return ''
end
end end
return {init = function(options) return filetype(options) end} return FileType

View File

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

View File

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

View File

@ -1,42 +1,8 @@
-- Copyright (c) 2020-2021 hoob3rt -- Copyright (c) 2020-2021 hoob3rt
-- MIT license, see LICENSE for more details. -- MIT license, see LICENSE for more details.
-- LuaFormatter off local Mode = require('lualine.component'):new()
local mode_map = { local get_mode = require('lualine.utils.mode').get_mode
['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
return mode Mode.update_status = get_mode
return Mode

View File

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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -42,7 +42,7 @@ end
-- @param highlight_group:(string) name of highlight group -- @param highlight_group:(string) name of highlight group
-- @return: (string) highlight group name with mode -- @return: (string) highlight group name with mode
local function append_mode(highlight_group) 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 == if mode == 'VISUAL' or mode == 'V-BLOCK' or mode == 'V-LINE' or mode ==
'SELECT' or mode == 'S-LINE' or mode == 'S-BLOCK' then 'SELECT' or mode == 'S-LINE' or mode == 'S-BLOCK' then
highlight_group = highlight_group .. '_visual' 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 -- Grab the last highlighter of left section
if left_section_data then if left_section_data then
-- extract highlight_name from .....%#highlight_name# -- extract highlight_name from .....%#highlight_name#
left_highlight_name = left_section_data:match('.*%%#(.*)#') left_highlight_name = left_section_data:match('.*%%#(.-)#')
else else
-- When right section us unavailable default to lualine_c -- When right section us unavailable default to lualine_c
left_highlight_name = append_mode('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
end end
if right_section_data then if right_section_data then
-- using vim-regex cause lua-paterns don't have non-greedy matching
-- extract highlight_name from %#highlight_name#.... -- extract highlight_name from %#highlight_name#....
right_highlight_name = vim.fn.matchlist(right_section_data, right_highlight_name = right_section_data:match('%%#(.-)#.*')
[[%#\(.\{-\}\)#]])[2]
else else
-- When right section us unavailable default to lualine_c -- When right section us unavailable default to lualine_c
right_highlight_name = append_mode('lualine_c') right_highlight_name = append_mode('lualine_c')

View File

@ -57,70 +57,25 @@ local function check_single_separator()
end end
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) 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 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 -- load the component
local ok, loaded_component = pcall(require, 'lualine.components.' .. local ok, loaded_component = pcall(require,
component.component_name) 'lualine.components.' .. component[1])
if not ok then if ok then
loaded_component = load_special_components(component.component_name) component.component_name = component[1]
end loaded_component = loaded_component:new(component)
component[1] = loaded_component elseif component[1]:find('[gvtwb]?o?:') == 1 then
if type(component[1]) == 'table' then loaded_component =
component[1] = component[1].init(component) require 'lualine.components.special.vim_var_component':new(component)
end else
-- set custom highlights loaded_component =
if component.color then require 'lualine.components.special.eval_func_component':new(component)
component.color_highlight = highlight.create_component_highlight_group(
component.color, component.component_name,
component)
end end
return loaded_component
end end
end end
@ -132,8 +87,9 @@ local function load_sections(sections)
end end
component.self = {} component.self = {}
component.self.section = section_name component.self.section = section_name
component_loader(component) -- apply default args
section[index] = component component = vim.tbl_extend('keep', component, config.options)
section[index] = component_loader(component)
end end
end end
end end

View File

@ -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

View File

@ -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

View File

@ -1,52 +1,49 @@
-- Copyright (c) 2020-2021 hoob3rt -- Copyright (c) 2020-2021 hoob3rt
-- MIT license, see LICENSE for more details. -- MIT license, see LICENSE for more details.
local utils_component = require('lualine.utils.component')
local M = {} local M = {}
local utils = require('lualine.utils.utils')
-- Returns formated string for a section -- Returns formated string for a section
function M.draw_section(section, highlight_name) function M.draw_section(section, highlight_name)
local status = {} local status = {}
local drawn_components = {}
for _, component in pairs(section) do for _, component in pairs(section) do
local localstatus = component[1]() -- load components into status table
if #localstatus > 0 then table.insert(status, component:draw(highlight_name))
local custom_highlight_at_begining = end
localstatus:find('%%#.*#') == 1 or component.color ~= nil
-- Apply modifier functions for options -- Flags required for knowing when to remove component separator
if component.format then localstatus = component.format(localstatus) end local next_component_colored = false
localstatus = utils_component.apply_icon(localstatus, component) local last_component_found = false
localstatus = utils_component.apply_case(localstatus, component)
localstatus = utils_component.apply_padding(localstatus, component) -- Check through components to see when component separator need to be removed
localstatus = utils_component.apply_highlights(localstatus, component, 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) highlight_name)
localstatus = utils_component.apply_spearator(localstatus, component) end
if custom_highlight_at_begining or (#drawn_components > 0 and -- Remove component separator when color option is used in next component
not drawn_components[#drawn_components].separator_applied) then 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
-- 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 -- Don't prepend with old highlight when the component changes it imidiately
-- Or when it was already applied with separator return status_str
table.insert(status, localstatus)
else else
table.insert(status, highlight_name .. localstatus) return highlight_name .. status_str
end end
table.insert(drawn_components, component)
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
end
status[#status] = utils_component.strip_separator(status[#status],
drawn_components[#status])
return table.concat(status)
end end
return M return M

View File

@ -54,4 +54,13 @@ function M.clear_highlights()
end end
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 return M