From ed3c502da01a7b4dcada5cf012565d1b071734e7 Mon Sep 17 00:00:00 2001 From: wongxy Date: Thu, 14 Apr 2022 22:33:32 +0800 Subject: [PATCH] REWRITE --- lua/virt-column/commands.lua | 16 ----- lua/virt-column/init.lua | 97 ----------------------------- lua/virt-column/utils.lua | 10 --- plugin/virt-column.lua | 115 +++++++++++++++++++++++++++++++++++ 4 files changed, 115 insertions(+), 123 deletions(-) delete mode 100644 lua/virt-column/commands.lua delete mode 100644 lua/virt-column/init.lua delete mode 100644 lua/virt-column/utils.lua create mode 100644 plugin/virt-column.lua diff --git a/lua/virt-column/commands.lua b/lua/virt-column/commands.lua deleted file mode 100644 index d3b6151..0000000 --- a/lua/virt-column/commands.lua +++ /dev/null @@ -1,16 +0,0 @@ -local M = {} - -M.refresh = function(bang) - if bang then - local win = vim.api.nvim_get_current_win() - vim.cmd [[noautocmd windo lua require("virt-column").refresh()]] - if vim.api.nvim_win_is_valid(win) then - vim.api.nvim_set_current_win(win) - vim.cmd [[lua require("virt-column").refresh()]] - end - else - vim.cmd [[lua require("virt-column").refresh()]] - end -end - -return M diff --git a/lua/virt-column/init.lua b/lua/virt-column/init.lua deleted file mode 100644 index d610e2d..0000000 --- a/lua/virt-column/init.lua +++ /dev/null @@ -1,97 +0,0 @@ -local utils = require "virt-column.utils" -local ffi = require "ffi" - -ffi.cdef "int curwin_col_off(void);" - -local M = { - config = { - char = "┃", - virtcolumn = "", - }, - buffer_config = {}, -} - -M.clear_buf = function(bufnr) - if M.namespace then - vim.api.nvim_buf_clear_namespace(bufnr, M.namespace, 0, -1) - end -end - -M.setup = function(config) - M.config = vim.tbl_deep_extend("force", M.config, config or {}) - M.namespace = vim.api.nvim_create_namespace "virt-column" - - vim.cmd [[command! -bang VirtColumnRefresh lua require("virt-column.commands").refresh("" == "!")]] - vim.cmd [[highlight default link VirtColumn Whitespace]] - vim.cmd [[highlight clear ColorColumn]] - - vim.cmd [[ - augroup VirtColumnAutogroup - autocmd! - autocmd FileChangedShellPost,TextChanged,TextChangedI,CompleteChanged,BufWinEnter * VirtColumnRefresh - autocmd OptionSet colorcolumn VirtColumnRefresh - autocmd VimEnter,SessionLoadPost * VirtColumnRefresh! - augroup END - ]] -end - -M.setup_buffer = function(config) - M.buffer_config[vim.api.nvim_get_current_buf()] = config - M.refresh() -end - -M.refresh = function() - local bufnr = vim.api.nvim_get_current_buf() - - if not vim.api.nvim_buf_is_loaded(bufnr) then - return - end - - local config = vim.tbl_deep_extend("force", M.config, M.buffer_config[bufnr] or {}) - local winnr = vim.api.nvim_get_current_win() - local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) - local width = vim.api.nvim_win_get_width(winnr) - ffi.C.curwin_col_off() - local textwidth = vim.opt.textwidth:get() - local colorcolumn = utils.concat_table(vim.opt.colorcolumn:get(), vim.split(config.virtcolumn, ",")) - - for i, c in ipairs(colorcolumn) do - if vim.startswith(c, "+") then - if textwidth ~= 0 then - colorcolumn[i] = textwidth + tonumber(c:sub(2)) - else - colorcolumn[i] = nil - end - elseif vim.startswith(c, "-") then - if textwidth ~= 0 then - colorcolumn[i] = textwidth - tonumber(c:sub(2)) - else - colorcolumn[i] = nil - end - else - colorcolumn[i] = tonumber(c) - end - end - - table.sort(colorcolumn, function(a, b) - return a > b - end) - - M.clear_buf(bufnr) - - for i = 1, #lines, 1 do - for _, column in ipairs(colorcolumn) do - local line = lines[i]:gsub("\t", string.rep(" ", vim.opt.tabstop:get())) - if width > column and vim.api.nvim_strwidth(line) < column then - vim.api.nvim_buf_set_extmark(bufnr, M.namespace, i - 1, 0, { - virt_text = { { config.char, "VirtColumn" } }, - virt_text_pos = "overlay", - hl_mode = "combine", - virt_text_win_col = column - 1, - priority = 1, - }) - end - end - end -end - -return M diff --git a/lua/virt-column/utils.lua b/lua/virt-column/utils.lua deleted file mode 100644 index 257cfae..0000000 --- a/lua/virt-column/utils.lua +++ /dev/null @@ -1,10 +0,0 @@ -local M = {} - -function M.concat_table(t1, t2) - for _, v in ipairs(t2) do - t1[#t1 + 1] = v - end - return t1 -end - -return M diff --git a/plugin/virt-column.lua b/plugin/virt-column.lua new file mode 100644 index 0000000..77fcba7 --- /dev/null +++ b/plugin/virt-column.lua @@ -0,0 +1,115 @@ +--- vim.g.virtcolumn: equal to global colorcolumn +--- vim.b.virtcolumn: equal to local colorcolumn +--- vim.g.virtcolumn_char: ▕ by default + +local api = vim.api +local fn = vim.fn +local ffi = require "ffi" + +ffi.cdef "int curwin_col_off(void);" +---@diagnostic disable-next-line: undefined-field +local curwin_col_off = ffi.C.curwin_col_off + +local NS = api.nvim_create_namespace "virt-column" + +local function _refresh() + local bufnr = api.nvim_get_current_buf() + if not api.nvim_buf_is_loaded(bufnr) then + return + end + + local textwidth = vim.opt.textwidth:get() + local virtcolumns = vim.split(vim.b.virtcolumn or vim.g.virtcolumn or vim.o.cc, ",") + + ---@type number[] + local items = {} + + for _, virtcolumn in ipairs(virtcolumns) do + if virtcolumn and virtcolumn ~= "" then + if vim.startswith(virtcolumn, "+") then + if textwidth ~= 0 then + table.insert(items, textwidth + tonumber(virtcolumn:sub(2))) + end + elseif vim.startswith(virtcolumn, "-") then + if textwidth ~= 0 then + table.insert(items, textwidth - tonumber(virtcolumn:sub(2))) + end + else + table.insert(items, tonumber(virtcolumn)) + end + end + end + table.sort(items, function(a, b) + return a > b + end) + + api.nvim_buf_clear_namespace(bufnr, NS, 0, -1) + + if #items == 0 then + return + end + + local win_lines = vim.o.lines + local offset = vim.fn.line "w0" + -- Avoid flickering caused by winscrolled_timer + offset = offset <= win_lines and 1 or offset - win_lines + + -- Avoid flickering caused by winscrolled_timer + -- ↓↓↓↓↓↓↓↓↓↓↓ + local lines = api.nvim_buf_get_lines(bufnr, offset - 1, vim.fn.line "w$" + win_lines, false) + local width = api.nvim_win_get_width(0) - curwin_col_off() + local tabstop = vim.opt.tabstop:get() + local char = vim.g.virtcolumn_char or "▕" + + for i = 1, #lines do + for _, item in ipairs(items) do + local line = lines[i]:gsub("\t", string.rep(" ", tabstop)) + if width > item and api.nvim_strwidth(line) < item then + api.nvim_buf_set_extmark(bufnr, NS, i + offset - 2, 0, { + virt_text = { { char, "VirtColumn" } }, + virt_text_pos = "overlay", + hl_mode = "combine", + virt_text_win_col = item - 1, + priority = 0, + }) + end + end + end +end + +-- Avoid unnecessary refreshing as much as possible +local winscrolled_timer +local function refresh(args) + ---@type string + local event = args.event or "" + if event == "WinScrolled" then + if winscrolled_timer and winscrolled_timer:is_active() then + winscrolled_timer:stop() + winscrolled_timer:close() + end + winscrolled_timer = vim.defer_fn(_refresh, 100) + elseif event:match "TextChanged" then + local lines_count = fn.line "$" + local need_refresh = vim.b.virtcolumn_lines_count ~= lines_count + vim.b.virtcolumn_lines_count = lines_count + if need_refresh then + _refresh() + end + end +end + +local function set_hl() + vim.cmd [[ + hi clear ColorColumn + hi default link VirtColumn NonText + ]] +end +set_hl() + +local group = api.nvim_create_augroup("virtcolumn", {}) +api.nvim_create_autocmd( + { "WinScrolled", "TextChanged", "TextChangedI", "BufWinEnter", "InsertLeave" }, + { group = group, callback = refresh } +) +api.nvim_create_autocmd("OptionSet", { group = group, callback = refresh, pattern = "colorcolumn" }) +api.nvim_create_autocmd("ColorScheme", { group = group, callback = set_hl })