mirror of
https://github.com/nvim-treesitter/nvim-treesitter-context
synced 2024-09-16 14:14:03 +02:00
d1767935a6
Fixes #442
236 lines
5 KiB
Lua
236 lines
5 KiB
Lua
local api = vim.api
|
|
|
|
local config = require('treesitter-context.config')
|
|
|
|
local augroup = api.nvim_create_augroup
|
|
local command = api.nvim_create_user_command
|
|
|
|
local enabled = false
|
|
|
|
--- @type table<integer, Range4[]>
|
|
local all_contexts = {}
|
|
|
|
--- @generic F: function
|
|
--- @param f F
|
|
--- @param ms? number
|
|
--- @return F
|
|
local function throttle(f, ms)
|
|
ms = ms or 200
|
|
local timer = assert(vim.loop.new_timer())
|
|
local waiting = 0
|
|
return function()
|
|
if timer:is_active() then
|
|
waiting = waiting + 1
|
|
return
|
|
end
|
|
waiting = 0
|
|
f() -- first call, execute immediately
|
|
timer:start(ms, 0, function()
|
|
if waiting > 1 then
|
|
vim.schedule(f) -- only execute if there are calls waiting
|
|
end
|
|
end)
|
|
end
|
|
end
|
|
|
|
local had_open = false
|
|
|
|
local function close()
|
|
if had_open then
|
|
require('treesitter-context.render').close()
|
|
end
|
|
end
|
|
|
|
--- @param bufnr integer
|
|
--- @param winid integer
|
|
--- @param ctx_ranges Range4[]
|
|
--- @param ctx_lines string[]
|
|
local function open(bufnr, winid, ctx_ranges, ctx_lines)
|
|
had_open = true
|
|
require('treesitter-context.render').open(bufnr, winid, ctx_ranges, ctx_lines)
|
|
end
|
|
|
|
local attached = {} --- @type table<integer,true>
|
|
|
|
---@param bufnr integer
|
|
---@param winid integer
|
|
local function can_open(bufnr, winid)
|
|
if not attached[bufnr] then
|
|
return false
|
|
end
|
|
|
|
if vim.bo[bufnr].filetype == '' then
|
|
return false
|
|
end
|
|
|
|
if vim.bo[bufnr].buftype ~= '' then
|
|
return false
|
|
end
|
|
|
|
if vim.wo[winid].previewwindow then
|
|
return false
|
|
end
|
|
|
|
if vim.fn.getcmdtype() ~= '' then
|
|
return false
|
|
end
|
|
|
|
if api.nvim_win_get_height(winid) < config.min_window_height then
|
|
return false
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
local update = throttle(function()
|
|
local bufnr = api.nvim_get_current_buf()
|
|
local winid = api.nvim_get_current_win()
|
|
|
|
if not can_open(bufnr, winid) then
|
|
close()
|
|
return
|
|
end
|
|
|
|
local context, context_lines = require('treesitter-context.context').get(bufnr, winid)
|
|
all_contexts[bufnr] = context
|
|
|
|
if not context or #context == 0 then
|
|
close()
|
|
return
|
|
end
|
|
|
|
assert(context_lines)
|
|
|
|
open(bufnr, winid, context, context_lines)
|
|
end)
|
|
|
|
local M = {
|
|
config = config,
|
|
}
|
|
|
|
local group = augroup('treesitter_context_update', {})
|
|
|
|
---@param event string|string[]
|
|
---@param callback fun(args: table)
|
|
---@param opts? vim.api.keyset.create_autocmd
|
|
local function autocmd(event, callback, opts)
|
|
opts = opts or {}
|
|
opts.callback = callback
|
|
opts.group = group
|
|
api.nvim_create_autocmd(event, opts)
|
|
end
|
|
|
|
function M.enable()
|
|
local cbuf = api.nvim_get_current_buf()
|
|
|
|
attached[cbuf] = true
|
|
|
|
autocmd({ 'WinScrolled', 'BufEnter', 'WinEnter', 'VimResized' }, update)
|
|
|
|
autocmd('BufReadPost', function(args)
|
|
attached[args.buf] = nil
|
|
if not config.on_attach or config.on_attach(args.buf) ~= false then
|
|
attached[args.buf] = true
|
|
end
|
|
end)
|
|
|
|
autocmd('BufDelete', function(args)
|
|
attached[args.buf] = nil
|
|
end)
|
|
|
|
autocmd('CursorMoved', update)
|
|
|
|
autocmd('OptionSet', function(args)
|
|
if args.match == 'number' or args.match == 'relativenumber' then
|
|
update()
|
|
end
|
|
end)
|
|
|
|
autocmd({ 'BufLeave', 'WinLeave' }, close)
|
|
|
|
autocmd('User', close, { pattern = 'SessionSavePre' })
|
|
autocmd('User', update, { pattern = 'SessionSavePost' })
|
|
|
|
update()
|
|
enabled = true
|
|
end
|
|
|
|
function M.disable()
|
|
augroup('treesitter_context_update', {})
|
|
attached = {}
|
|
close()
|
|
enabled = false
|
|
end
|
|
|
|
function M.toggle()
|
|
if enabled then
|
|
M.disable()
|
|
else
|
|
M.enable()
|
|
end
|
|
end
|
|
|
|
function M.enabled()
|
|
return enabled
|
|
end
|
|
|
|
local function init()
|
|
command('TSContextEnable', M.enable, {})
|
|
command('TSContextDisable', M.disable, {})
|
|
command('TSContextToggle', M.toggle, {})
|
|
|
|
api.nvim_set_hl(0, 'TreesitterContext', { link = 'NormalFloat', default = true })
|
|
api.nvim_set_hl(0, 'TreesitterContextLineNumber', { link = 'LineNr', default = true })
|
|
api.nvim_set_hl(0, 'TreesitterContextBottom', { link = 'NONE', default = true })
|
|
api.nvim_set_hl(0, 'TreesitterContextLineNumberBottom', { link = 'TreesitterContextBottom', default = true })
|
|
api.nvim_set_hl(0, 'TreesitterContextSeparator', { link = 'FloatBorder', default = true })
|
|
end
|
|
|
|
local did_init = false
|
|
|
|
---@param options? TSContext.UserConfig
|
|
function M.setup(options)
|
|
if options then
|
|
config.update(options)
|
|
end
|
|
|
|
if config.enable then
|
|
M.enable()
|
|
else
|
|
M.disable()
|
|
end
|
|
|
|
if not did_init then
|
|
init()
|
|
did_init = true
|
|
end
|
|
end
|
|
|
|
---@param depth integer? default 1
|
|
function M.go_to_context(depth)
|
|
depth = depth or 1
|
|
local line = api.nvim_win_get_cursor(0)[1]
|
|
local context = nil
|
|
local bufnr = api.nvim_get_current_buf()
|
|
local contexts = all_contexts[bufnr] or {}
|
|
|
|
for idx = #contexts, 1, -1 do
|
|
local c = contexts[idx]
|
|
if depth == 0 then
|
|
break
|
|
end
|
|
if c[1] + 1 < line then
|
|
context = c
|
|
depth = depth - 1
|
|
end
|
|
end
|
|
|
|
if context == nil then
|
|
return
|
|
end
|
|
|
|
vim.cmd([[ normal! m' ]]) -- add current cursor position to the jump list
|
|
api.nvim_win_set_cursor(0, { context[1] + 1, context[2] })
|
|
end
|
|
|
|
return M
|