From 6b7c5a14ce0c5dffd9fbbc30a93bb037df4b1ced Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 9 Jun 2024 10:37:42 +0800 Subject: [PATCH] feat(statusline): add lua statusline --- autoload/SpaceVim/autocmds.vim | 1 - autoload/SpaceVim/layers/core/statusline.vim | 110 +- config/plugins/vim-signify.vim | 6 +- lua/spacevim/api/language.lua | 21 + lua/spacevim/api/messletters.lua | 122 +- lua/spacevim/api/time.lua | 11 + lua/spacevim/api/vim.lua | 4 + lua/spacevim/api/vim/statusline.lua | 85 ++ lua/spacevim/plugin/statusline.lua | 1141 +++++++++++++++++- 9 files changed, 1439 insertions(+), 62 deletions(-) create mode 100644 lua/spacevim/api/language.lua create mode 100644 lua/spacevim/api/time.lua diff --git a/autoload/SpaceVim/autocmds.vim b/autoload/SpaceVim/autocmds.vim index 3b9b1e71c..591bd69a9 100644 --- a/autoload/SpaceVim/autocmds.vim +++ b/autoload/SpaceVim/autocmds.vim @@ -316,7 +316,6 @@ function! s:apply_custom_leader_keybindings() abort endfor endfunction - function! s:disable_welcome() abort augroup SPwelcome au! diff --git a/autoload/SpaceVim/layers/core/statusline.vim b/autoload/SpaceVim/layers/core/statusline.vim index 6fbb5af50..ace158f22 100644 --- a/autoload/SpaceVim/layers/core/statusline.vim +++ b/autoload/SpaceVim/layers/core/statusline.vim @@ -21,13 +21,120 @@ " - `major_mode_cache`: Enable/disable major mode cache, enabled by default. scriptencoding utf-8 - if exists('g:_spacevim_statusline_loaded') finish endif let g:_spacevim_statusline_loaded = 1 + +if has('nvim-0.10.0') + + " 核心逻辑移至 lua,兼容 VIML 函数接口 + + function! SpaceVim#layers#core#statusline#winnr(id) abort + return v:lua.require('spacevim.plugin.statusline').winnr(a:id) + endfunction + + function! SpaceVim#layers#core#statusline#get(...) abort + if a:0 > 0 + return v:lua.require('spacevim.plugin.statusline').get(a:1) + else + return v:lua.require('spacevim.plugin.statusline').get() + endif + endfunction + + function! SpaceVim#layers#core#statusline#init() abort + return v:lua.require('spacevim.plugin.statusline').init() + endfunction + + function! SpaceVim#layers#core#statusline#def_colors() abort + return v:lua.require('spacevim.plugin.statusline').def_colors() + endfunction + + function! SpaceVim#layers#core#statusline#register_mode(mode) abort + if has_key(a:mode, 'func') && type(a:mode.func) == 2 + let mode = a:mode + let mode.func = string(a:mode.func)[10:-3] + return v:lua.require('spacevim.plugin.statusline').register_mode(mode) + else + return v:lua.require('spacevim.plugin.statusline').register_mode(a:mode) + endif + + endfunction + + function! SpaceVim#layers#core#statusline#toggle_mode(name) abort + return v:lua.require('spacevim.plugin.statusline').toggle_mode(a:name) + endfunction + + function! SpaceVim#layers#core#statusline#toggle_section(name) abort + return v:lua.require('spacevim.plugin.statusline').toggle_section(a:name) + endfunction + + function! SpaceVim#layers#core#statusline#rsep() abort + return v:lua.require('spacevim.plugin.statusline').rsep() + endfunction + + function! SpaceVim#layers#core#statusline#set_variable(var) abort + return v:lua.require('spacevim.plugin.statusline').set_variable(a:var) + endfunction + + function! SpaceVim#layers#core#statusline#config() abort + return v:lua.require('spacevim.plugin.statusline').config() + endfunction + + function! SpaceVim#layers#core#statusline#ctrlp(focus, byfname, regex, prev, item, next, marked) abort + return v:lua.require('spacevim.plugin.statusline').ctrlp(a:focus, a:byfname, a:regex, a:prev, a:item, a:next, a:marked) + endfunction + + function! SpaceVim#layers#core#statusline#ctrlp_status(str) abort + return v:lua.require('spacevim.plugin.statusline').ctrlp_status(a:str) + endfunction + + function! SpaceVim#layers#core#statusline#jump(i) abort + return v:lua.require('spacevim.plugin.statusline').jump(a:i) + endfunction + + function! SpaceVim#layers#core#statusline#mode(mode) abort + return v:lua.require('spacevim.plugin.statusline').mode(a:mode) + endfunction + + function! SpaceVim#layers#core#statusline#mode_text(mode) abort + return v:lua.require('spacevim.plugin.statusline').mode_text(a:mode) + endfunction + + function! SpaceVim#layers#core#statusline#denite_status(argv) abort + return v:lua.require('spacevim.plugin.statusline').denite_status(a:argv) + endfunction + + function! SpaceVim#layers#core#statusline#denite_mode() abort + return v:lua.require('spacevim.plugin.statusline').denite_mode() + endfunction + + function! SpaceVim#layers#core#statusline#unite_mode() abort + return v:lua.require('spacevim.plugin.statusline').unite_mode() + endfunction + + function! SpaceVim#layers#core#statusline#register_sections(name, func) abort + return v:lua.require('spacevim.plugin.statusline').register_sections(a:name, string(a:func)[10:-3]) + endfunction + + function! SpaceVim#layers#core#statusline#check_section(name) abort + return v:lua.require('spacevim.plugin.statusline').check_section(a:name) + endfunction + + function! SpaceVim#layers#core#statusline#remove_section(name) abort + return v:lua.require('spacevim.plugin.statusline').remove_section(a:name) + endfunction + + function! SpaceVim#layers#core#statusline#health() abort + return v:lua.require('spacevim.plugin.statusline').health() + endfunction + + finish +endif + + " APIs let s:MESSLETTERS = SpaceVim#api#import('messletters') let s:TIME = SpaceVim#api#import('time') @@ -414,7 +521,6 @@ function! s:filesize() abort return printf('%.1f', l:size/1024.0/1024.0/1024.0) . 'g ' endif endfunction - function! SpaceVim#layers#core#statusline#get(...) abort for nr in range(1, winnr('$')) call setwinvar(nr, 'winwidth', winwidth(nr)) diff --git a/config/plugins/vim-signify.vim b/config/plugins/vim-signify.vim index 626ad1d42..795cea5d6 100644 --- a/config/plugins/vim-signify.vim +++ b/config/plugins/vim-signify.vim @@ -1,5 +1,9 @@ augroup signify_config autocmd! - autocmd User Signify let &l:statusline = SpaceVim#layers#core#statusline#get(1) + if has('nvim-0.9.5') + autocmd User Signify let lua require('spacevim.plugin.statusline').active() + else + autocmd User Signify let &l:statusline = SpaceVim#layers#core#statusline#get(1) + endif augroup END diff --git a/lua/spacevim/api/language.lua b/lua/spacevim/api/language.lua new file mode 100644 index 000000000..097ef890d --- /dev/null +++ b/lua/spacevim/api/language.lua @@ -0,0 +1,21 @@ +local M = {} + +M.__aliases = { + typescript = 'TypeScript', + typescriptreact = 'TypeScript React', + python = 'Python', + java = 'Java', + smalltalk = 'SmallTalk', + objc = 'Objective-C', + postscript = 'PostScript', +} + +function M.get_alias(ft) + if ft and ft ~= '' and M.__aliases[ft] then + return M.__aliases[ft] + else + return ft + end +end + +return M diff --git a/lua/spacevim/api/messletters.lua b/lua/spacevim/api/messletters.lua index 267518d13..a233b4883 100644 --- a/lua/spacevim/api/messletters.lua +++ b/lua/spacevim/api/messletters.lua @@ -6,83 +6,91 @@ -- License: GPLv3 --============================================================================= - local M = {} - +function M.circled_letter(c) + local nr = vim.fn.char2nr(c) + if nr - 64 >= 1 and nr - 64 <= 26 then + return vim.fn.nr2char(9397 + nr - 64) + elseif nr - 96 >= 1 and nr - 96 <= 26 then + return vim.fn.nr2char(9423 + nr - 96) + else + return '' + end +end function M.bubble_num(num, t) - local list = {} + local list = {} - table.insert(list, {'➊', '➋', '➌', '➍', '➎', '➏', '➐', '➑', '➒', '➓'}) - table.insert(list, {'➀', '➁', '➂', '➃', '➄', '➅', '➆', '➇', '➈', '➉'}) - table.insert(list, {'⓵', '⓶', '⓷', '⓸', '⓹', '⓺', '⓻', '⓼', '⓽', '⓾'}) + table.insert(list, { '➊', '➋', '➌', '➍', '➎', '➏', '➐', '➑', '➒', '➓' }) + table.insert(list, { '➀', '➁', '➂', '➃', '➄', '➅', '➆', '➇', '➈', '➉' }) + table.insert(list, { '⓵', '⓶', '⓷', '⓸', '⓹', '⓺', '⓻', '⓼', '⓽', '⓾' }) - local n = '' + local n = '' - pcall(function () - n = list[t + 1][num] - end) + pcall(function() + n = list[t + 1][num] + end) - return n + return n end function M.circled_num(num, t) - local nr2char = vim.fn.nr2char - local range = vim.fn.range - local index = vim.fn.index - if t == 0 then - if num == 0 then - return nr2char(9471) - elseif index(range(1, 10), num) ~= -1 then - return nr2char(10102 + num - 1) - elseif index(range(11, 20), num) ~= -1 then - return nr2char(9451 + num - 11) - else - return '' - end - elseif t == 1 then - if index(range(20), num) ~= -1 then - if num == 0 then - return nr2char(9450) - else - return nr2char(9311 + num) - end - else - return '' - end - elseif t == 2 then - if index(range(1, 10), num) ~= -1 then - return nr2char(9461 + num - 1) - else - return '' - end - elseif t == 3 then - return num + local nr2char = vim.fn.nr2char + local range = vim.fn.range + local index = vim.fn.index + if t == 0 then + if num == 0 then + return nr2char(9471) + elseif index(range(1, 10), num) ~= -1 then + return nr2char(10102 + num - 1) + elseif index(range(11, 20), num) ~= -1 then + return nr2char(9451 + num - 11) + else + return '' end + elseif t == 1 then + if index(range(20), num) ~= -1 then + if num == 0 then + return nr2char(9450) + else + return nr2char(9311 + num) + end + else + return '' + end + elseif t == 2 then + if index(range(1, 10), num) ~= -1 then + return nr2char(9461 + num - 1) + else + return '' + end + elseif t == 3 then + return num + end end function M.index_num(num) - local nums = {8304, 185, 178, 179, 8308, 8309, 8310, 8311, 8312, 8313} - if vim.fn.index(vim.fn.range(1, 10), num) ~= -1 then - return vim.fn.nr2char(nums[num + 1]) - end - return '' + local nums = { 8304, 185, 178, 179, 8308, 8309, 8310, 8311, 8312, 8313 } + if vim.fn.index(vim.fn.range(1, 10), num) ~= -1 then + return vim.fn.nr2char(nums[num + 1]) + end + return '' end function M.parenthesized_num(num) - if vim.fn.index(vim.fn.range(1, 20), num) ~= -1 then - return vim.fn.nr2char(9331 + num) - else - return '' - end + if vim.fn.index(vim.fn.range(1, 20), num) ~= -1 then + return vim.fn.nr2char(9331 + num) + else + return '' + end end function M.num_period(num) - if vim.fn.index(vim.fn.range(1, 20), num) ~= -1 then - return vim.fn.nr2char(9351 + num) - else - return '' - end + if vim.fn.index(vim.fn.range(1, 20), num) ~= -1 then + return vim.fn.nr2char(9351 + num) + else + return '' + end end return M diff --git a/lua/spacevim/api/time.lua b/lua/spacevim/api/time.lua new file mode 100644 index 000000000..a3d330454 --- /dev/null +++ b/lua/spacevim/api/time.lua @@ -0,0 +1,11 @@ +local M = {} + +function M.current_time() + return vim.fn.strftime('%I:%M %p') +end + +function M.current_date() + return vim.fn.strftime('%a %b %d') +end + +return M diff --git a/lua/spacevim/api/vim.lua b/lua/spacevim/api/vim.lua index 77e4ad1a9..20597320c 100644 --- a/lua/spacevim/api/vim.lua +++ b/lua/spacevim/api/vim.lua @@ -47,4 +47,8 @@ function M.executable(bin) return vim.fn.executable(bin) == 1 end +function M.is_qf_win(winnr) + +end + return M diff --git a/lua/spacevim/api/vim/statusline.lua b/lua/spacevim/api/vim/statusline.lua index 0e99dd71c..7e0852eea 100644 --- a/lua/spacevim/api/vim/statusline.lua +++ b/lua/spacevim/api/vim/statusline.lua @@ -52,4 +52,89 @@ function M.close_float() end end +function M.check_width(len, sec, winwidth) + return len + M.len(sec) < winwidth + +end + +function M.len(sec) + if not sec then return 0 end + local str = vim.fn.matchstr(sec, '%{.*}') + if vim.fn.empty(str) == 0 then + local pos = vim.fn.match(str, '}') + return vim.fn.len(sec) - vim.fn.len(str) + vim.fn.len(vim.fn.eval(string.sub(str, 3, pos))) + 4 + else + return vim.fn.len(sec) + 4 + end +end + +function M.eval(sec) + return vim.fn.substitute(sec, '%{.*}', '', 'g') +end + +function M.build(left_sections, right_sections, lsep, rsep, fname, tag, hi_a, hi_b, hi_c, hi_z, winwidth) + local l = '%#' .. hi_a .. '#' .. left_sections[1] + l = l .. '%#' .. hi_a .. '_' .. hi_b .. '#' .. lsep + local flag = true + local len = 0 + for _, sec in ipairs(vim.tbl_filter(function(v) + return vim.fn.empty(v) == 0 + end, vim.list_slice(left_sections, 2))) do + if M.check_width(len, sec, winwidth) then + if flag then + l = l .. '%#' .. hi_b .. '#' .. sec + l = l .. '%#' .. hi_b .. '_' .. hi_c .. '#' .. lsep + else + l = l .. '%#' .. hi_c .. '#' .. sec + l = l .. '%#' .. hi_c .. '_' .. hi_b .. '#' .. lsep + end + flag = not flag + end + end + l = string.sub(l, 1, #l - #lsep) + if #right_sections == 0 then + if flag then + return l .. '%#' .. hi_c .. '#' + else + return l .. '%#' .. hi_b .. '#' + end + end + if M.check_width(len, fname, winwidth) then + len = len + M.len(fname) + if flag then + l = l .. '%#' .. hi_c .. '_' .. hi_z .. '#' .. lsep .. '%#' .. hi_z .. '#' .. fname .. '%=' + else + l = l .. '%#' .. hi_b .. '_' .. hi_z .. '#' .. lsep .. '%#' .. hi_z .. '#' .. fname .. '%=' + end + else + if flag then + l = l .. '%#' .. hi_c .. '_' .. hi_z .. '#' .. lsep .. '%=' + else + l = l .. '%#' .. hi_b .. '_' .. hi_z .. '#' .. lsep .. '%=' + end + end + if M.check_width(len, tag, winwidth) and vim.g.spacevim_enable_statusline_tag == 1 then + l = l .. '%#' .. hi_z .. '#' .. tag + end + l = l .. '%#' .. hi_b .. '_' .. hi_z .. '#' .. rsep + flag = true + for _, sec in ipairs(vim.tbl_filter(function(v) + return vim.fn.empty(v) == 0 + end, right_sections)) do + if M.check_width(len, sec, winwidth) then + len = len + M.len(sec) + if flag then + l = l .. '%#' .. hi_b .. '#' .. sec + l = l .. '%#' .. hi_c .. '_' .. hi_b .. '#' .. rsep + else + l = l .. '%#' .. hi_c .. '#' .. sec + l = l .. '%#' .. hi_b .. '_' .. hi_c .. '#' .. rsep + end + flag = not flag + end + end + l = string.sub(l, 1, #l - #rsep) + return l +end + return M diff --git a/lua/spacevim/plugin/statusline.lua b/lua/spacevim/plugin/statusline.lua index 6af427af2..5475a8c53 100644 --- a/lua/spacevim/plugin/statusline.lua +++ b/lua/spacevim/plugin/statusline.lua @@ -1,2 +1,1141 @@ ---!/usr/bin/lua +--============================================================================= +-- statusline.lua --- +-- Copyright (c) 2019-2024 Wang Shidong & Contributors +-- Author: Wang Shidong < wsdjeg@outlook.com > +-- URL: https://spacevim.org +-- License: GPLv3 +--============================================================================= +local highlight = require('spacevim.api.vim.highlight') +local system = require('spacevim.api.system') +local lang = require('spacevim.api.language') +local time = require('spacevim.api.time') +local JSON = require('spacevim.api.data.json') + +local layer = require('spacevim.layer') + +local messletters = require('spacevim.api.messletters') +local statusline = require('spacevim.api.vim.statusline') +local log = require('spacevim.logger').derive('stl') + +local M = {} + +local function index(t, v) + for n, m in ipairs(t) do + if m == v then + return n + end + end + return -1 +end + +-- https://github.com/ryanoasis/powerline-extra-symbols +local separators = { + arrow = { '', '' }, + curve = { '', '' }, + slant = { '', '' }, + brace = { '', '' }, + fire = { '', '' }, + ['nil'] = { '', '' }, +} +local i_separators = { + arrow = { '', '' }, + curve = { '', '' }, + slant = { '', '' }, + bar = { '|', '|' }, + ['nil'] = { '', '' }, +} + +local lsep = '' +local rsep = '' +local ilsep = '' +local irsep = '' + +local loaded_modes = {} +local colors_template = vim.fn['SpaceVim#mapping#guide#theme#gruvbox#palette']() +local section_old_pos = {} + +local modes = { + ['center-cursor'] = { + icon = '', + icon_asc = '-', + desc = 'centered-cursor mode', + }, + ['hi-characters-for-long-lines'] = { + icon = '⑧', + icon_asc = '8', + desc = 'toggle highlight of characters for long lines', + }, + ['fill-column-indicator'] = { + icon = messletters.circled_letter('f'), + icon_asc = 'f', + desc = 'fill-column-indicator mode', + }, + ['syntax-checking'] = { + icon = messletters.circled_letter('s'), + icon_asc = 's', + desc = 'syntax-checking mode', + }, + ['spell-checking'] = { + icon = messletters.circled_letter('S'), + icon_asc = 'S', + desc = 'spell-checking mode', + }, + ['paste-mode'] = { + icon = messletters.circled_letter('p'), + icon_asc = 'p', + desc = 'paste mode', + }, + whitespace = { + icon = messletters.circled_letter('w'), + icon_asc = 'w', + desc = 'whitespace mode', + }, + wrapline = { + icon = messletters.circled_letter('W'), + icon_asc = 'W', + desc = 'wrap line mode', + }, +} + +local major_mode_cache = true + +if layer.isLoaded('checkers') then + table.insert(loaded_modes, 'syntax-checking') +end + +if vim.o.spell then + table.insert(loaded_modes, 'spell-checking') +end + +if vim.o.cc == '80' then + table.insert(loaded_modes, 'fill-column-indicator') +end + +if index(vim.g.spacevim_statusline_right, 'whitespace') ~= -1 then + table.insert(loaded_modes, 'whitespace') +end + +local function winnr(...) + if select(1, ...) then + if vim.g.spacevim_windows_index_type == 3 then + return ' %{ get(w:, "winid", winnr()) } ' + else + return ' %{ v:lua.require("spacevim.plugin.statusline").winnr(get(w:, "winid", winnr())) } ' + end + else + if vim.g.spacevim_enable_statusline_mode == 1 then + return '%{v:lua.require("spacevim.plugin.statusline").mode(mode())} %{ v:lua.require("spacevim.plugin.statusline").winnr(get(w:, "winid", winnr())) } %{v:lua.require("spacevim.plugin.statusline").mode_text(mode())} ' + elseif vim.g.spacevim_windows_index_type == 3 then + return '%{v:lua.require("spacevim.plugin.statusline").mode(mode())} %{ get(w:, "winid", winnr()) } ' + else + return '%{v:lua.require("spacevim.plugin.statusline").mode(mode())} %{ v:lua.require("spacevim.plugin.statusline").winnr(get(w:, "winid", winnr())) } ' + end + end +end + +function M.winnr(id) + return messletters.circled_num(id, vim.g.spacevim_windows_index_type) +end + +local function whitespace() + local ln = vim.fn.search('\\s\\+$', 'nw') + if ln ~= 0 then + return ' trailing[' .. ln .. '] ' + else + return '' + end +end + +local function battery_status() + if vim.fn.executable('acpi') == 1 then + else + return '' + end +end + +---@return string # The buffer name +local function buffer_name() + if vim.b._spacevim_statusline_showbfname == 1 or vim.g.spacevim_enable_statusline_bfpath == 1 then + return ' ' .. vim.fn.bufname('%') + else + return '' + end +end + +local function input_method() end + +local function syntax_checking() end + +local function search_status() end + +local function search_count() end + +local function filesize() + local size = vim.fn.getfsize(vim.fn.bufname('%')) + if size <= 0 then + return '' + elseif size < 1024 then + return size .. ' bytes ' + elseif size < 1024 * 1024 then + return string.format('%.1f', size / 1024) .. 'k ' + elseif size < 1024 * 1024 * 1024 then + return string.format('%.1f', size / 1024 / 1024) .. 'm ' + else + return string.format('%.1f', size / 1024 / 1024 / 1024) .. 'g ' + end +end + +local function filename() + local name = vim.fn.fnamemodify(vim.fn.bufname('%'), ':t') + if name == '' then + name = 'No Name' + end + return "%{ &modified ? ' * ' : ' - '}" .. filesize() .. name .. ' ' +end + +local function fileformat() + if vim.g.spacevim_statusline_unicode == 1 then + vim.g._spacevim_statusline_fileformat = system.fileformat() + else + vim.g._spacevim_statusline_fileformat = vim.o.ff + end + return '%{ " " . g:_spacevim_statusline_fileformat . " ' + .. irsep + .. ' " . (&fenc!=""?&fenc:&enc) . " "}' +end + +local function major_mode() + local alias = lang.get_alias(vim.o.filetype) + if alias == '' then + return '' + else + return ' ' .. alias .. ' ' + end +end + +local function get_modes() -- same as s:modes() in vim statusline + local m + if vim.g.spacevim_statusline_unicode == 1 then + m = ' ❖ ' + else + m = ' # ' + end + for _, mode in ipairs(loaded_modes) do + if vim.g.spacevim_statusline_unicode == 1 then + m = m .. modes[mode].icon .. ' ' + else + m = m .. modes[mode].icon_asc .. ' ' + end + end + return m .. ' ' +end + +local function totallines() + return ' %L ' +end + +local function percentage() + return ' %P ' +end + +local function cursorpos() + return [[%{' ' . join(map(getpos('.')[1:2], "printf('%3d', v:val)"), ':') . ' '}]] +end + +local function current_time() + return ' ' .. time.current_time() .. ' ' +end + +local function current_date() + return ' ' .. time.current_date() .. ' ' +end + +local registed_sections = { + winnr = winnr, + ['syntax checking'] = syntax_checking, + filename = filename, + fileformat = fileformat, + ['major mode'] = major_mode, + ['minor mode lighters'] = get_modes, + cursorpos = cursorpos, + percentage = percentage, + totallines = totallines, + time = current_time, + date = current_date, + whitespace = whitespace, + ['battery status'] = battery_status, + ['input method'] = input_method, + ['search status'] = search_status, + ['search count'] = search_count, +} + +local function check_mode() + if vim.fn.mode() == 'n' then + return 'n' + elseif vim.fn.mode() == 'i' then + return 'i' + elseif string.match(vim.fn.mode(), 'v') then + return 'v' + elseif string.match(vim.fn.mode(), 'R') then + return 'R' + end +end + +local function current_tag() + return M._current_tag() +end + +function M._current_tag() end + +local function active() + local lsec = {} + for _, section in ipairs(vim.g.spacevim_statusline_left) do + if registed_sections[section] then + local rst = '' + local ok, _ = pcall(function() + if type(registed_sections[section]) == 'function' then + rst = registed_sections[section]() + elseif type(registed_sections[section]) == 'string' then + rst = vim.fn[registed_sections[section]]() + end + end) + if not ok then + log.debug('failed to call section func:' .. section) + end + table.insert(lsec, rst) + end + end + local rsec = {} + for _, section in ipairs(vim.g.spacevim_statusline_right) do + if registed_sections[section] then + local rst = '' + local ok, _ = pcall(function() + if type(registed_sections[section]) == 'function' then + rst = registed_sections[section]() + elseif type(registed_sections[section]) == 'string' then + rst = vim.fn[registed_sections[section]]() + end + end) + if not ok then + log.debug('failed to call section func:' .. section) + end + table.insert(rsec, rst) + end + end + local fname = buffer_name() + local tab = current_tag() + local winwidth = vim.fn.winwidth(vim.fn.winnr()) + if vim.o.laststatus == 3 then + winwidth = vim.o.columns + end + return statusline.build( + lsec, + rsec, + lsep, + rsep, + fname, + tab, + 'SpaceVim_statusline_a', + 'SpaceVim_statusline_b', + 'SpaceVim_statusline_c', + 'SpaceVim_statusline_z', + winwidth + ) +end + +local function inactive() + local l = '%#SpaceVim_statusline_ia#' + .. winnr(1) + .. '%#SpaceVim_statusline_ia_SpaceVim_statusline_b#' + .. lsep + .. '%#SpaceVim_statusline_b#' + local secs = { filename(), ' ' .. vim.o.filetype, get_modes() } + local base = 10 + for _, sec in ipairs(secs) do + local len = statusline.len(sec) + base = base + len + l = l + .. '%{ get(w:, "winwidth", 150) < ' + .. base + .. ' ? "" : (" ' + .. statusline.eval(sec) + .. ' ' + .. ilsep + .. '")}' + end + if (vim.w.winwidth or 150) > base + 10 then + l = l + .. table.concat({ + '%=', + '%{" " . get(g:, "_spacevim_statusline_fileformat", "") . " "}', + '%{" " . (&fenc!=""?&fenc:&enc) . " "}', + ' %P ', + }, irsep) + end + return l +end + +---@return string # return a simple statusline with special name +local function simple_name(name) + return '%#SpaceVim_statusline_ia#' + .. winnr(1) + .. '%#SpaceVim_statusline_ia_SpaceVim_statusline_b#' + .. lsep + .. '%#SpaceVim_statusline_b# ' + .. name + .. ' %#SpaceVim_statusline_b_SpaceVim_statusline_c#' + .. lsep +end + +local special_statusline = { + vimfiler = function() + return simple_name('VimFiler') + end, + qf = function() end, -- todo + defx = function() + return simple_name('defx') + end, + ['git-status'] = function() + return simple_name('Git status') + end, + startify = function() + pcall(vim.fn['fugitive#detect'], vim.fn.getcwd()) + local st = '%#SpaceVim_statusline_ia#' + .. winnr(1) + .. '%#SpaceVim_statusline_ia_SpaceVim_statusline_b#' + .. lsep + .. '%#SpaceVim_statusline_b# startify %#SpaceVim_statusline_b_SpaceVim_statusline_c#' + .. lsep + if index(vim.g.spacevim_statusline_left, 'vcs') ~= -1 and registed_sections.vcs then + st = st .. '%#SpaceVim_statusline_c#' + if type(registed_sections.vcs) == 'string' then + st = st .. vim.fn[registed_sections.vcs]() + elseif type(registed_sections.vcs) == 'function' then + st = st .. registed_sections.vcs() + end + st = st .. '%#SpaceVim_statusline_c_SpaceVim_statusline_z#' .. lsep + end + return st + end, + NvimTree = function() + return simple_name('NvimTree') + end, + ['neo-tree'] = function() + return simple_name('NeoTree') + end, + Fuzzy = function() end, -- todo + SpaceVimFindArgv = function() end, -- todo + ['git-commit'] = function() + return simple_name('Git commit') + end, + ['git-rebase'] = function() + return simple_name('Git rebase') + end, + ['git-blame'] = function() + return simple_name('Git blame') + end, + ['git-log'] = function() + return simple_name('Git log') + end, + ['git-diff'] = function() + return simple_name('Git diff') + end, + ['git-config'] = function() + return simple_name('Git config') + end, + SpaceVimMessageBuffer = function() + return simple_name('Message') + end, + ['gista-list'] = function() + return simple_name('Gista') + end, + terminal = function() end, -- todo + vimchat = function() end, -- todo + calender = function() + return simple_name('Calendar') + end, + ['vader-result'] = function() + return simple_name('Vader result') + end, + ['gina-status'] = function() + return simple_name('Gina status') + end, + ['gina-commit'] = function() + return simple_name('Gina commit') + end, + nerdtree = function() + return simple_name('Nerdtree') + end, + Mundo = function() + return simple_name('Mundo') + end, + MundoDiff = function() + return simple_name('MundoDiff') + end, + SpaceVimLayerManager = function() + return simple_name('LayerManager') + end, + SpaceVimGitLogPopup = function() + return simple_name('Git log popup') + end, + ['response.idris'] = function() + return simple_name('Idris Response') + end, + ['markdown.lspdoc'] = function() + return simple_name('LSP hover info') + end, + SpaceVimWinDiskManager = function() + return simple_name('WinDisk') + end, + SpaceVimTodoManager = function() + return simple_name('TODO manager') + end, + SpaceVimTasksInfo = function() + return simple_name('Tasks manager') + end, + SpaceVimGitBranchManager = function() + return simple_name('Branch manager') + end, + SpaceVimGitRemoteManager = function() + return simple_name('Remote manager') + end, + SpaceVimPlugManager = function() + return simple_name('PlugManager') + end, + SpaceVimTabsManager = function() + return simple_name('TabsManager') + end, + fzf = function() end, -- todo + denite = function() end, -- todo + ['denite-filter'] = function() + return '%#SpaceVim_statusline_a_bold#' + .. ' Filter ' + .. '%#SpaceVim_statusline_a_SpaceVim_statusline_b#' + .. lsep + end, + unite = function() end, -- todo + SpaceVimFlyGrep = function() end, -- todo + TransientState = function() + return '%#SpaceVim_statusline_ia# Transient State %#SpaceVim_statusline_a_SpaceVim_statusline_b#' + end, + SpaceVimLog = function() + return simple_name('SpaceVim Runtime Log') + end, + SpaceVimTomlViewer = function() + return simple_name('Toml Json Viewer') + end, + vimcalc = function() + return simple_name('VimCalc') + end, + HelpDescribe = function() + return simple_name('HelpDescribe') + end, + SpaceVimRunner = function() end, -- todo + SpaceVimREPL = function() end, -- todo + VimMailClient = function() end, -- todo + SpaceVimQuickFix = function() + return simple_name('SpaceVimQuickFix') + end, + VebuggerShell = function() + return simple_name('VebuggerShell') + end, + VebuggerTerminal = function() + return simple_name('VebuggerTerminal') + end, +} + +function M.get(...) + if special_statusline[vim.o.filetype] then + return special_statusline[vim.o.filetype]() + end + if select(1, ...) then + return active() + else + return inactive() + end +end + +function M.def_colors() + local name = vim.g.colors_name or 'gruvbox' + + local t + + if #vim.g.spacevim_custom_color_palette > 0 then + t = vim.g.spacevim_custom_color_palette + else + local ok = pcall(function() + t = vim.fn['SpaceVim#mapping#guide#theme#' .. name .. '#palette']() + end) + + if not ok then + t = vim.fn['Vim#mapping#guide#theme#gruvbox#palette']() + end + end + colors_template = t + vim.api.nvim_set_hl(0, 'SpaceVim_statusline_a', { + fg = t[1][1], + bg = t[1][2], + ctermfg = t[1][4], + ctermbg = t[1][3], + }) + vim.api.nvim_set_hl(0, 'SpaceVim_statusline_a_bold', { + bold = true, + fg = t[1][1], + bg = t[1][2], + ctermfg = t[1][4], + ctermbg = t[1][3], + }) + vim.api.nvim_set_hl(0, 'SpaceVim_statusline_ia', { + fg = t[1][1], + bg = t[1][2], + ctermfg = t[1][4], + ctermbg = t[1][3], + }) + vim.api.nvim_set_hl(0, 'SpaceVim_statusline_b', { + fg = t[2][1], + bg = t[2][2], + ctermfg = t[2][4], + ctermbg = t[2][3], + }) + vim.api.nvim_set_hl(0, 'SpaceVim_statusline_c', { + fg = t[3][1], + bg = t[3][2], + ctermfg = t[3][4], + ctermbg = t[3][3], + }) + vim.api.nvim_set_hl(0, 'SpaceVim_statusline_z', { + fg = t[3][1], + bg = t[4][1], + ctermfg = t[3][3], + ctermbg = t[4][2], + }) + vim.api.nvim_set_hl(0, 'SpaceVim_statusline_error', { + bold = true, + fg = '#fb4934', + bg = t[2][2], + ctermfg = 'Black', + ctermbg = t[2][3], + }) + vim.api.nvim_set_hl(0, 'SpaceVim_statusline_warn', { + bold = true, + fg = '#fabd2f', + bg = t[2][2], + ctermfg = 'Black', + ctermbg = t[2][3], + }) + highlight.hi_separator('SpaceVim_statusline_a', 'SpaceVim_statusline_b') + highlight.hi_separator('SpaceVim_statusline_a_bold', 'SpaceVim_statusline_b') + highlight.hi_separator('SpaceVim_statusline_ia', 'SpaceVim_statusline_b') + highlight.hi_separator('SpaceVim_statusline_b', 'SpaceVim_statusline_c') + highlight.hi_separator('SpaceVim_statusline_b', 'SpaceVim_statusline_z') + highlight.hi_separator('SpaceVim_statusline_c', 'SpaceVim_statusline_z') +end + +function M.register_mode(mode) + if modes[mode.key] and mode.func then + modes[mode.key].func = mode.func + log.debug('register major mode function:' .. mode.key) + else + modes[mode.key] = mode + end +end + +local function update_conf() + log.debug('write major mode to major_mode.json') + local conf = {} + for key, _ in pairs(modes) do + if index(loaded_modes, key) > -1 then + conf[key] = true + else + conf[key] = false + end + end + if + vim.fn.writefile( + { JSON.json_encode(conf) }, + vim.fn.expand(vim.g.spacevim_data_dir .. 'SpaceVim/major_mode.json') + ) == 0 + then + log.debug('update major_mode.json done') + else + log.debug('failed to update major_mode.json') + end +end + +function M.toggle_mode(name) + log.debug('toggle major mode:' .. name) + local mode = modes[name] + if not mode then + log.debug('can not find major mode:' .. name) + return + end + local done + if mode.func then + if type(mode.func) == 'string' then + done = vim.fn[mode.func]() + elseif type(mode.func) == 'function' then + done = mode.func() + end + end + local idx = index(loaded_modes, name) + if idx ~= -1 then + table.remove(loaded_modes, idx) + else + if done == 1 then + table.insert(loaded_modes, name) + end + end + vim.opt_local.statusline = M.get(1) + if major_mode_cache then + update_conf() + end +end +function M.toggle_section(name) + if + index(vim.g.spacevim_statusline_left, name) == -1 + and index(vim.g.spacevim_statusline_right, name) == -1 + and not section_old_pos[name] + then + if name == 'search status' then + local temp = vim.g.spacevim_statusline_left + table.insert(temp, 2, name) + vim.g.spacevim_statusline_left = temp + else + local temp = vim.g.spacevim_statusline_right + table.insert(temp, name) + vim.g.spacevim_statusline_right = temp + end + elseif index(vim.g.spacevim_statusline_right, name) ~= -1 then + section_old_pos[name] = { 'r', index(vim.g.spacevim_statusline_right, name) } + local temp = vim.g.spacevim_statusline_right + table.remove(temp, index(temp, name)) + vim.g.spacevim_statusline_right = temp + elseif index(vim.g.spacevim_statusline_left, name) ~= -1 then + section_old_pos[name] = { 'l', index(vim.g.spacevim_statusline_left, name) } + local temp = vim.g.spacevim_statusline_left + table.remove(temp, index(temp, name)) + vim.g.spacevim_statusline_left = temp + elseif section_old_pos[name] then + if section_old_pos[name][1] == #'r' then + local temp = vim.g.spacevim_statusline_right + table.insert(temp, section_old_pos[name][2], name) + vim.g.spacevim_statusline_right = temp + else + local temp = vim.g.spacevim_statusline_left + table.insert(temp, section_old_pos[name][2], name) + vim.g.spacevim_statusline_left = temp + end + end + vim.opt_local.statusline = M.get(1) +end +function M.ctrlp_status(str) + return statusline.build( + { ' Ctrlp ', ' ' .. str .. ' ' }, + { ' ' .. vim.fn.getcwd() .. ' ' }, + lsep, + rsep, + '', + '', + 'SpaceVim_statusline_a', + 'SpaceVim_statusline_b', + 'SpaceVim_statusline_c', + 'SpaceVim_statusline_z', + vim.fn.winwidth(vim.fn.winnr()) + ) +end +function M.config() + if separators[vim.g.spacevim_statusline_separator] then + lsep = separators[vim.g.spacevim_statusline_separator][1] + rsep = separators[vim.g.spacevim_statusline_separator][2] + end + if i_separators[vim.g.spacevim_statusline_iseparator] then + ilsep = i_separators[vim.g.spacevim_statusline_iseparator][1] + irsep = i_separators[vim.g.spacevim_statusline_iseparator][2] + end + vim.fn['SpaceVim#mapping#space#def']( + 'nnoremap', + { 't', 'm', 'm' }, + 'call SpaceVim#layers#core#statusline#toggle_section("minor mode lighters")', + 'toggle the minor mode lighters', + 1 + ) + vim.fn['SpaceVim#mapping#space#def']( + 'nnoremap', + { 't', 'm', 'M' }, + 'call SpaceVim#layers#core#statusline#toggle_section("major mode")', + 'toggle the major mode', + 1 + ) + vim.fn['SpaceVim#mapping#space#def']( + 'nnoremap', + { 't', 'm', 'b' }, + 'call SpaceVim#layers#core#statusline#toggle_section("battery status")', + 'toggle the battery status', + 1 + ) + vim.fn['SpaceVim#mapping#space#def']( + 'nnoremap', + { 't', 'm', 'd' }, + 'call SpaceVim#layers#core#statusline#toggle_section("date")', + 'toggle the date', + 1 + ) + vim.fn['SpaceVim#mapping#space#def']( + 'nnoremap', + { 't', 'm', 'i' }, + 'call SpaceVim#layers#core#statusline#toggle_section("input method")', + 'toggle the input method', + 1 + ) + vim.fn['SpaceVim#mapping#space#def']( + 'nnoremap', + { 't', 'm', 't' }, + 'call SpaceVim#layers#core#statusline#toggle_section("time")', + 'toggle the time', + 1 + ) + vim.fn['SpaceVim#mapping#space#def']( + 'nnoremap', + { 't', 'm', 'p' }, + 'call SpaceVim#layers#core#statusline#toggle_section("cursorpos")', + 'toggle the cursor position', + 1 + ) + vim.fn['SpaceVim#mapping#space#def']( + 'nnoremap', + { 't', 'm', 'T' }, + 'if &laststatus == 2 | let &laststatus = 0 | else | let &laststatus = 2 | endif', + 'toggle the statusline itself', + 1 + ) + local function TagbarStatusline(in_tagbar, sortstr, fname, flags) + local name = '' + if vim.fn.strwidth(fname) > vim.g.spacevim_sidebar_width - 15 then + name = string.sub(fname, vim.g.spacevim_sidebar_width - 20) .. '..' + else + name = fname + end + return statusline.build( + { winnr(1), ' Tagbar ', ' ' .. name .. ' ' }, + {}, + lsep, + rsep, + '', + '', + 'SpaceVim_statusline_ia', + 'SpaceVim_statusline_b', + 'SpaceVim_statusline_c', + 'SpaceVim_statusline_z', + vim.g.spacevim_sidebar_width + ) + end + vim.g.tagbar_status_func = TagbarStatusline + vim.g.unite_force_overwrite_statusline = 0 + vim.g.ctrlp_status_func = { + main = 'SpaceVim#layers#core#statusline#ctrlp', + prog = 'SpaceVim#layers#core#statusline#ctrlp_status', + } + if + vim.fn.filereadable(vim.fn.expand(vim.g.spacevim_data_dir .. 'SpaceVim/major_mode.json')) == 1 + and major_mode_cache + then + log.debug('load cache from major_mode.json') + local conf = JSON.json_decode( + vim.fn.join( + vim.fn.readfile(vim.fn.expand(vim.g.spacevim_data_dir .. 'SpaceVim/major_mode.json'), ''), + '' + ) + ) + for k, v in pairs(conf) do + if v == 1 or v == true then + log.debug('cached major mode: ' .. k) + M.toggle_mode(k) + end + end + end +end +function M.ctrlp(focus, byfname, regex, prev, item, next, marked) + return statusline.build( + { ' Ctrlp ', ' ' .. prev .. ' ', ' ' .. item .. ' ', ' ' .. next .. ' ' }, + { ' ' .. focus .. ' ', ' ' .. byfname .. ' ', ' ' .. vim.fn.getcwd() .. ' ' }, + lsep, + rsep, + '', + '', + 'SpaceVim_statusline_a_bold', + 'SpaceVim_statusline_b', + 'SpaceVim_statusline_c', + 'SpaceVim_statusline_z', + vim.fn.winwidth(vim.fn.winnr()) + ) +end +function M.jump(i) + pcall(function() + vim.cmd(i .. 'wincmd w') + end) +end + +function M.mode(mode) + local t = colors_template + local iedit_mode = vim.w.spacevim_iedit_mode or '' + if vim.w.spacevim_statusline_mode ~= mode then + if mode == 'n' then + if iedit_mode == 'n' then + vim.api.nvim_set_hl(0, 'SpaceVim_statusline_a', { + bold = true, + fg = t[9][1], + bg = t[9][2], + ctermfg = t[9][3], + ctermbg = t[9][4], + }) + elseif iedit_mode == 'i' then + vim.api.nvim_set_hl(0, 'SpaceVim_statusline_a', { + bold = true, + fg = t[8][1], + bg = t[8][2], + ctermfg = t[8][3], + ctermbg = t[8][4], + }) + else + vim.api.nvim_set_hl(0, 'SpaceVim_statusline_a', { + bold = true, + fg = t[1][1], + bg = t[1][2], + ctermfg = t[1][4], + ctermbg = t[1][3], + }) + end + elseif mode == 'i' then + vim.api.nvim_set_hl(0, 'SpaceVim_statusline_a', { + bold = true, + fg = t[5][1], + bg = t[5][2], + ctermfg = t[5][3], + ctermbg = t[5][4], + }) + elseif mode == 'R' then + vim.api.nvim_set_hl(0, 'SpaceVim_statusline_a', { + bold = true, + fg = t[7][1], + bg = t[7][2], + ctermfg = t[7][3], + ctermbg = t[7][4], + }) + elseif + mode == 'v' + or mode == 'V' + or mode == #'' + or mode == 's' + or mode == 'S' + or mode == #'' + then + vim.api.nvim_set_hl(0, 'SpaceVim_statusline_a', { + bold = true, + fg = t[6][1], + bg = t[6][2], + ctermfg = t[6][3], + ctermbg = t[6][4], + }) + end + highlight.hi_separator('SpaceVim_statusline_a', 'SpaceVim_statusline_b') + vim.w.spacevim_statusline_mode = mode + end + return '' +end + +function M.mode_text(mode) + local past_mode = '' + if vim.o.paste then + past_mode = 'Paste ' .. ilsep .. ' ' + end + local mode_text = '' + local iedit_mode = vim.w.spacevim_iedit_mode or '' + if mode == 'n' then + if iedit_mode ~= '' then + if iedit_mode == 'n' then + mode_text = 'IEDIT-NORMAL' + elseif iedit_mode == 'i' then + mode_text = 'IEDIT-INSERT' + end + else + mode_text = ' NORMAL' + end + elseif mode == 'i' then + mode_text = ' INSERT' + elseif mode == 'R' then + mode_text = 'REPLACE' + elseif mode == 'v' then + mode_text = ' VISUAL' + elseif mode == 'V' then + mode_text = ' V-LINE' + elseif mode == '' then + mode_text = 'V-BLOCK' + elseif mode == 'c' then + mode_text = 'COMMAND' + elseif mode == 't' then + mode_text = ' TERM' + elseif + mode == 'v' + or mode == 'V' + or mode == '^V' + or mode == 's' + or mode == 'S' + or mode == '^S' + then + mode_text = ' VISUAL' + end + return past_mode .. mode_text +end +function M.set_variable(var) + major_mode_cache = var.major_mode_cache or major_mode_cache +end +function M.check_section(name) + for _, v in ipairs(vim.g.spacevim_statusline_right) do + if v == name then + return true + end + end + for _, v in ipairs(vim.g.spacevim_statusline_left) do + if v == name then + return true + end + end + return false +end + +function M.denite_status(argv) + local denite_ver ---@type number + if vim.fn.exists('*denite#get_status_mode') == 1 then + denite_ver = 2 + else + denite_ver = 3 + end + if denite_ver == 3 then + return vim.fn['denite#get_status'](argv) + else + return vim.fn['denite#get_status_' .. argv]() + end +end +function M.denite_mode() + local t = colors_template + local denite_ver ---@type number + if vim.fn.exists('*denite#get_status_mode') == 1 then + denite_ver = 2 + else + denite_ver = 3 + end + if denite_ver == 3 then + return 'Denite' + else + local dmode = vim.fn.split(vim.fn['denite#get_status_mode']())[2] + if vim.w.spacevim_statusline_mode ~= dmode then + if dmode == 'NORMAL' then + vim.api.nvim_set_hl(0, 'SpaceVim_statusline_a_bold', { + bold = true, + fg = t[1][1], + bg = t[1][2], + ctermfg = t[1][4], + ctermbg = t[1][3], + }) + else + vim.api.nvim_set_hl(0, 'SpaceVim_statusline_a_bold', { + bold = true, + fg = t[5][1], + bg = t[5][2], + ctermfg = t[5][3], + ctermbg = t[5][4], + }) + end + highlight.hi_separator('SpaceVim_statusline_a_bold', 'SpaceVim_statusline_b') + vim.w.spacevim_statusline_mode = dmode + end + return dmode + end +end +function M.unite_mode() + local t = colors_template + local dmode = vim.fn.mode() + if vim.w.spacevim_statusline_mode ~= dmode then + if dmode == 'n' then + vim.api.nvim_set_hl(0, 'SpaceVim_statusline_a_bold', { + bold = true, + fg = t[1][1], + bg = t[1][2], + ctermfg = t[1][4], + ctermbg = t[1][3], + }) + elseif dmode == 'i' then + vim.api.nvim_set_hl(0, 'SpaceVim_statusline_a_bold', { + bold = true, + fg = t[5][1], + bg = t[5][2], + ctermfg = t[5][3], + ctermbg = t[5][4], + }) + end + highlight.hi_separator('SpaceVim_statusline_a_bold', 'SpaceVim_statusline_b') + vim.w.spacevim_statusline_mode = dmode + end + return dmode +end + +function M.register_sections(name, func) + if registed_sections[name] then + log.info('statusline build-in section ' .. name .. ' has been changed!') + end + if type(func) ~= 'function' and type(func) ~= 'string' then + log.warn('section' .. name .. ' need to be function or string') + return + end + registed_sections[name] = func +end + +function M.remove_section(name) + local left = {} + local right = {} + for _, v in ipairs(vim.g.spacevim_statusline_left) do + if v ~= name then + table.insert(left, v) + end + end + vim.g.spacevim_statusline_left = left + for _, v in ipairs(vim.g.spacevim_statusline_right) do + if v ~= name then + table.insert(right, v) + end + end + vim.g.spacevim_statusline_right = right + vim.opt_local.statusline = M.get(1) +end +function M.health() end +function M.init() + local group = vim.api.nvim_create_augroup('spacevim_statusline', { clear = true }) + vim.api.nvim_create_autocmd({ 'BufWinEnter', 'WinEnter', 'FileType', 'BufWritePost' }, { + group = group, + pattern = { '*' }, + callback = function(_) + vim.opt_local.statusline = M.get(1) + end, + }) + vim.api.nvim_create_autocmd({ 'WinLeave' }, { + group = group, + pattern = { '*' }, + callback = function(_) + vim.opt_local.statusline = M.get() + end, + }) + vim.api.nvim_create_autocmd({ 'ColorScheme' }, { + group = group, + pattern = { '*' }, + callback = function(_) + M.def_colors() + end, + }) +end + +function M.rsep() + return separators[vim.g.spacevim_statusline_separator] or separators.arrow +end + +return M