1
Files
flake-nixinator/config/neovim/store/lazy-plugins/vim-illuminate/lua/illuminate.lua

322 lines
9.2 KiB
Lua

local M = {}
local timers = {}
local references = {}
local paused_bufs = {}
-- returns r1 < r2 based on start of range
local function before_by_start(r1, r2)
if r1['start'].line < r2['start'].line then return true end
if r2['start'].line < r1['start'].line then return false end
if r1['start'].character < r2['start'].character then return true end
return false
end
-- returns r1 < r2 base on start and if they are disjoint
local function before_disjoint(r1, r2)
if r1['end'].line < r2['start'].line then return true end
if r2['start'].line < r1['end'].line then return false end
if r1['end'].character < r2['start'].character then return true end
return false
end
-- check for cursor row in [start,end]
-- check for cursor col in [start,end]
-- While the end is technically exclusive based on the highlighting, we treat it as inclusive to match the server.
local function point_in_range(point, range)
if point.row == range['start']['line'] and point.col < range['start']['character'] then
return false
end
if point.row == range['end']['line'] and point.col > range['end']['character'] then
return false
end
return point.row >= range['start']['line'] and point.row <= range['end']['line']
end
local function cursor_in_references(bufnr)
if not references[bufnr] then
return false
end
if vim.api.nvim_win_get_buf(0) ~= bufnr then
return false
end
local crow, ccol = unpack(vim.api.nvim_win_get_cursor(0))
crow = crow - 1 -- reference ranges are (0,0)-indexed for (row,col)
for _, reference in pairs(references[bufnr]) do
local range = reference.range
if point_in_range({ row = crow, col = ccol }, range) then
return true
end
end
return false
end
local function handle_document_highlight(result, bufnr, client_id)
if not bufnr or not vim.api.nvim_buf_is_loaded(bufnr) then return end
local btimer = timers[bufnr]
if btimer then
vim.loop.timer_stop(btimer)
-- vim.loop.close(btimer)
end
if type(result) ~= 'table' then
vim.lsp.util.buf_clear_references(bufnr)
return
end
timers[bufnr] = vim.defer_fn(function()
if not bufnr or not vim.api.nvim_buf_is_loaded(bufnr) then return end
vim.lsp.util.buf_clear_references(bufnr)
if cursor_in_references(bufnr) then
local client = vim.lsp.get_client_by_id(client_id)
if client then
vim.lsp.util.buf_highlight_references(bufnr, result, client.offset_encoding)
end
end
end, vim.g.Illuminate_delay or 17)
table.sort(result, function(a, b)
return before_by_start(a.range, b.range)
end)
references[bufnr] = result
end
local function valid(bufnr, range)
return range
and range.start.line < vim.api.nvim_buf_line_count(bufnr)
and range.start.character < #vim.fn.getline(range.start.line + 1)
end
local function augroup(bufnr, autocmds)
vim.cmd('augroup vim_illuminate_lsp' .. bufnr)
vim.cmd('autocmd!')
if autocmds then
vim.b.illuminate_lsp_enabled = true
autocmds()
else
vim.b.illuminate_lsp_enabled = false
end
vim.cmd('augroup END')
end
local function autocmd(bufnr)
vim.cmd(string.format('autocmd CursorMoved,CursorMovedI <buffer=%d> lua require"illuminate".on_cursor_moved(%d)',
bufnr, bufnr))
end
local function move_cursor(row, col)
if not paused_bufs[vim.api.nvim_get_current_buf()] then
augroup(vim.api.nvim_get_current_buf(), function()
vim.api.nvim_win_set_cursor(0, { row, col })
autocmd(vim.api.nvim_get_current_buf())
end)
else
vim.api.nvim_win_set_cursor(0, { row, col })
end
end
function M.on_attach(client)
M.stop_buf()
if client and not client.supports_method('textDocument/documentHighlight') then
return
end
pcall(vim.api.nvim_command, 'IlluminationDisable!')
augroup(vim.api.nvim_get_current_buf(), function()
autocmd(vim.api.nvim_get_current_buf())
end)
vim.lsp.handlers['textDocument/documentHighlight'] = function(...)
if vim.fn.has('nvim-0.5.1') == 1 then
handle_document_highlight(select(2, ...), select(3, ...).bufnr, select(3, ...).client_id)
else
handle_document_highlight(select(3, ...), select(5, ...), nil)
end
end
vim.lsp.buf.document_highlight()
end
function M.on_cursor_moved(bufnr)
if not cursor_in_references(bufnr) then
vim.lsp.util.buf_clear_references(bufnr)
end
-- Best-effort check if any client support textDocument/documentHighlight
local supported = nil
-- For nvim 0.10+
if vim.lsp.get_clients then
supported = false
for _, client in ipairs(vim.lsp.get_clients({bufnr = bufnr})) do
if client and client.supports_method('textDocument/documentHighlight') then
supported = true
end
end
-- For older versions
elseif vim.lsp.for_each_buffer_client then
supported = false
vim.lsp.for_each_buffer_client(bufnr, function(client)
if client.supports_method('textDocument/documentHighlight') then
supported = true
end
end)
end
if supported == nil or supported then
vim.lsp.buf.document_highlight()
else
augroup(vim.api.nvim_get_current_buf(), function()
end)
end
end
function M.get_document_highlights(bufnr)
return references[bufnr]
end
function M.next_reference(opt)
opt = vim.tbl_extend('force', { reverse = false, wrap = false, range_ordering = 'start', silent = false }, opt or {})
local before
if opt.range_ordering == 'start' then
before = before_by_start
else
before = before_disjoint
end
local bufnr = vim.api.nvim_get_current_buf()
local refs = M.get_document_highlights(bufnr)
if not refs or #refs == 0 then return nil end
local next = nil
local nexti = nil
local crow, ccol = unpack(vim.api.nvim_win_get_cursor(0))
local crange = { start = { line = crow - 1, character = ccol } }
for i, ref in ipairs(refs) do
local range = ref.range
if valid(bufnr, range) then
if opt.reverse then
if before(range, crange) and (not next or before(next, range)) then
next = range
nexti = i
end
else
if before(crange, range) and (not next or before(range, next)) then
next = range
nexti = i
end
end
end
end
if not next and opt.wrap then
nexti = opt.reverse and #refs or 1
next = refs[nexti].range
end
if next then
move_cursor(next.start.line + 1, next.start.character)
if not opt.silent then
print('[' .. nexti .. '/' .. #refs .. ']')
end
end
return next
end
function M.toggle_pause()
if paused_bufs[vim.api.nvim_get_current_buf()] then
paused_bufs[vim.api.nvim_get_current_buf()] = false
augroup(vim.api.nvim_get_current_buf(), function()
autocmd(vim.api.nvim_get_current_buf())
end)
M.on_cursor_moved(vim.api.nvim_get_current_buf())
else
paused_bufs[vim.api.nvim_get_current_buf()] = true
augroup(vim.api.nvim_get_current_buf(), nil)
end
end
function M.configure(config)
require('illuminate.config').set(config)
end
function M.pause()
require('illuminate.engine').pause()
end
function M.resume()
require('illuminate.engine').resume()
end
function M.toggle()
require('illuminate.engine').toggle()
end
function M.toggle_buf()
require('illuminate.engine').toggle_buf()
end
function M.pause_buf()
require('illuminate.engine').pause_buf()
end
function M.stop_buf()
require('illuminate.engine').stop_buf()
end
function M.resume_buf()
require('illuminate.engine').resume_buf()
end
function M.freeze_buf()
require('illuminate.engine').freeze_buf()
end
function M.unfreeze_buf()
require('illuminate.engine').unfreeze_buf()
end
function M.toggle_freeze_buf()
require('illuminate.engine').toggle_freeze_buf()
end
function M.invisible_buf()
require('illuminate.engine').invisible_buf()
end
function M.visible_buf()
require('illuminate.engine').visible_buf()
end
function M.toggle_visibility_buf()
require('illuminate.engine').toggle_visibility_buf()
end
function M.goto_next_reference(wrap)
if wrap == nil then
wrap = vim.o.wrapscan
end
require('illuminate.goto').goto_next_reference(wrap)
end
function M.goto_prev_reference(wrap)
if wrap == nil then
wrap = vim.o.wrapscan
end
require('illuminate.goto').goto_prev_reference(wrap)
end
function M.textobj_select()
require('illuminate.textobj').select()
end
function M.debug()
require('illuminate.engine').debug()
end
function M.is_paused()
return require('illuminate.engine').is_paused()
end
function M.set_highlight_defaults()
vim.cmd [[
hi def IlluminatedWordText guifg=none guibg=none gui=underline
hi def IlluminatedWordRead guifg=none guibg=none gui=underline
hi def IlluminatedWordWrite guifg=none guibg=none gui=underline
]]
end
return M