mirror of
https://github.com/zoriya/virtcolumn.nvim.git
synced 2026-05-27 06:41:47 +00:00
REWRITE
This commit is contained in:
@@ -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
|
||||
@@ -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("<bang>" == "!")]]
|
||||
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
|
||||
@@ -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
|
||||
@@ -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 })
|
||||
Reference in New Issue
Block a user