aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/lua/vim')
-rw-r--r--runtime/lua/vim/_editor.lua2
-rw-r--r--runtime/lua/vim/_inspector.lua2
-rw-r--r--runtime/lua/vim/filetype.lua7
-rw-r--r--runtime/lua/vim/filetype/detect.lua22
-rw-r--r--runtime/lua/vim/fs.lua18
-rw-r--r--runtime/lua/vim/lsp.lua22
-rw-r--r--runtime/lua/vim/lsp/_watchfiles.lua17
-rw-r--r--runtime/lua/vim/lsp/buf.lua2
-rw-r--r--runtime/lua/vim/lsp/handlers.lua2
-rw-r--r--runtime/lua/vim/lsp/util.lua30
-rw-r--r--runtime/lua/vim/shared.lua8
-rw-r--r--runtime/lua/vim/treesitter.lua10
-rw-r--r--runtime/lua/vim/treesitter/_fold.lua4
-rw-r--r--runtime/lua/vim/treesitter/_meta.lua18
-rw-r--r--runtime/lua/vim/treesitter/_range.lua38
-rw-r--r--runtime/lua/vim/treesitter/language.lua5
-rw-r--r--runtime/lua/vim/treesitter/languagetree.lua73
-rw-r--r--runtime/lua/vim/treesitter/query.lua45
-rw-r--r--runtime/lua/vim/version.lua2
19 files changed, 234 insertions, 93 deletions
diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua
index 20e813d77c..b26def5958 100644
--- a/runtime/lua/vim/_editor.lua
+++ b/runtime/lua/vim/_editor.lua
@@ -778,7 +778,7 @@ do
-- some bugs, so fake the two-step dance for now.
local matches
- --- Omnifunc for completing lua values from from the runtime lua interpreter,
+ --- Omnifunc for completing lua values from the runtime lua interpreter,
--- similar to the builtin completion for the `:lua` command.
---
--- Activate using `set omnifunc=v:lua.vim.lua_omnifunc` in a lua buffer.
diff --git a/runtime/lua/vim/_inspector.lua b/runtime/lua/vim/_inspector.lua
index 2ebb7a7efd..ecd39c35bc 100644
--- a/runtime/lua/vim/_inspector.lua
+++ b/runtime/lua/vim/_inspector.lua
@@ -90,7 +90,7 @@ function vim.inspect_pos(bufnr, row, col, filter)
nsmap[id] = name
end
- --- Convert an extmark tuple into a map-like table
+ --- Convert an extmark tuple into a table
--- @private
local function to_map(extmark)
extmark = {
diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index 5c799b23f2..1b04666161 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -579,6 +579,7 @@ local extension = {
['json-patch'] = 'json',
json5 = 'json5',
jsonc = 'jsonc',
+ jsonl = 'jsonl',
jsonnet = 'jsonnet',
libsonnet = 'jsonnet',
jsp = 'jsp',
@@ -997,7 +998,9 @@ local extension = {
spi = 'spyce',
spy = 'spyce',
tyc = 'sql',
- typ = 'sql',
+ typ = function(path, bufnr)
+ return require('vim.filetype.detect').typ(bufnr)
+ end,
pkb = 'sql',
tyb = 'sql',
pks = 'sql',
@@ -1078,6 +1081,8 @@ local extension = {
uit = 'uil',
uil = 'uil',
ungram = 'ungrammar',
+ usd = 'usd',
+ usda = 'usd',
sba = 'vb',
vb = 'vb',
dsm = 'vb',
diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua
index 74b01d569c..94106a3547 100644
--- a/runtime/lua/vim/filetype/detect.lua
+++ b/runtime/lua/vim/filetype/detect.lua
@@ -1322,6 +1322,28 @@ function M.txt(bufnr)
end
end
+function M.typ(bufnr)
+ if vim.g.filetype_typ then
+ return vim.g.filetype_typ
+ end
+
+ for _, line in ipairs(getlines(bufnr, 1, 200)) do
+ if
+ findany(line, {
+ '^CASE[%s]?=[%s]?SAME$',
+ '^CASE[%s]?=[%s]?LOWER$',
+ '^CASE[%s]?=[%s]?UPPER$',
+ '^CASE[%s]?=[%s]?OPPOSITE$',
+ '^TYPE%s',
+ })
+ then
+ return 'sql'
+ end
+ end
+
+ return 'typst'
+end
+
-- Determine if a .v file is Verilog, V, or Coq
function M.v(bufnr)
if vim.fn.did_filetype() ~= 0 then
diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua
index 2a51bde263..864ba495f1 100644
--- a/runtime/lua/vim/fs.lua
+++ b/runtime/lua/vim/fs.lua
@@ -72,8 +72,12 @@ function M.basename(file)
return file:match('[/\\]$') and '' or (file:match('[^\\/]*$'):gsub('\\', '/'))
end
----@private
-local function join_paths(...)
+--- Concatenate directories and/or file into a single path with normalization
+--- (e.g., `"foo/"` and `"bar"` get joined to `"foo/bar"`)
+---
+---@param ... string
+---@return string
+function M.joinpath(...)
return (table.concat({ ... }, '/'):gsub('//+', '/'))
end
@@ -116,14 +120,14 @@ function M.dir(path, opts)
local dirs = { { path, 1 } }
while #dirs > 0 do
local dir0, level = unpack(table.remove(dirs, 1))
- local dir = level == 1 and dir0 or join_paths(path, dir0)
+ local dir = level == 1 and dir0 or M.joinpath(path, dir0)
local fs = vim.loop.fs_scandir(M.normalize(dir))
while fs do
local name, t = vim.loop.fs_scandir_next(fs)
if not name then
break
end
- local f = level == 1 and name or join_paths(dir0, name)
+ local f = level == 1 and name or M.joinpath(dir0, name)
coroutine.yield(f, t)
if
opts.depth
@@ -230,7 +234,7 @@ function M.find(names, opts)
local t = {}
for name, type in M.dir(p) do
if (not opts.type or opts.type == type) and names(name, p) then
- table.insert(t, join_paths(p, name))
+ table.insert(t, M.joinpath(p, name))
end
end
return t
@@ -239,7 +243,7 @@ function M.find(names, opts)
test = function(p)
local t = {}
for _, name in ipairs(names) do
- local f = join_paths(p, name)
+ local f = M.joinpath(p, name)
local stat = vim.loop.fs_stat(f)
if stat and (not opts.type or opts.type == stat.type) then
t[#t + 1] = f
@@ -276,7 +280,7 @@ function M.find(names, opts)
end
for other, type_ in M.dir(dir) do
- local f = join_paths(dir, other)
+ local f = M.joinpath(dir, other)
if type(names) == 'function' then
if (not opts.type or opts.type == type_) and names(other, dir) then
if add(f) then
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 5c78bd7580..2e6ca7a0ac 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -8,12 +8,8 @@ local sync = require('vim.lsp.sync')
local semantic_tokens = require('vim.lsp.semantic_tokens')
local api = vim.api
-local nvim_err_writeln, nvim_buf_get_lines, nvim_command, nvim_buf_get_option, nvim_exec_autocmds =
- api.nvim_err_writeln,
- api.nvim_buf_get_lines,
- api.nvim_command,
- api.nvim_buf_get_option,
- api.nvim_exec_autocmds
+local nvim_err_writeln, nvim_buf_get_lines, nvim_command, nvim_exec_autocmds =
+ api.nvim_err_writeln, api.nvim_buf_get_lines, api.nvim_command, api.nvim_exec_autocmds
local uv = vim.loop
local tbl_isempty, tbl_extend = vim.tbl_isempty, vim.tbl_extend
local validate = vim.validate
@@ -137,7 +133,7 @@ local format_line_ending = {
---@param bufnr (number)
---@return string
local function buf_get_line_ending(bufnr)
- return format_line_ending[nvim_buf_get_option(bufnr, 'fileformat')] or '\n'
+ return format_line_ending[vim.bo[bufnr].fileformat] or '\n'
end
local client_index = 0
@@ -319,7 +315,7 @@ end
local function buf_get_full_text(bufnr)
local line_ending = buf_get_line_ending(bufnr)
local text = table.concat(nvim_buf_get_lines(bufnr, 0, -1, true), line_ending)
- if nvim_buf_get_option(bufnr, 'eol') then
+ if vim.bo[bufnr].eol then
text = text .. line_ending
end
return text
@@ -709,7 +705,7 @@ local function text_document_did_open_handler(bufnr, client)
if not api.nvim_buf_is_loaded(bufnr) then
return
end
- local filetype = nvim_buf_get_option(bufnr, 'filetype')
+ local filetype = vim.bo[bufnr].filetype
local params = {
textDocument = {
@@ -901,8 +897,8 @@ end
--- Field `cmd` in {config} is required.
---
---@param config (table) Configuration for the server:
---- - cmd: (table|string|fun(dispatchers: table):table) command string or
---- list treated like |jobstart()|. The command must launch the language server
+--- - cmd: (string[]|fun(dispatchers: table):table) command a list of
+--- strings treated like |jobstart()|. The command must launch the language server
--- process. `cmd` can also be a function that creates an RPC client.
--- The function receives a dispatchers table and must return a table with the
--- functions `request`, `notify`, `is_closing` and `terminate`
@@ -913,7 +909,7 @@ end
--- the `cmd` process. Not related to `root_dir`.
---
--- - cmd_env: (table) Environment flags to pass to the LSP on
---- spawn. Must be specified using a map-like table.
+--- spawn. Must be specified using a table.
--- Non-string values are coerced to string.
--- Example:
--- <pre>
@@ -2177,7 +2173,7 @@ end
---
--- Currently only supports a single client. This can be set via
--- `setlocal formatexpr=v:lua.vim.lsp.formatexpr()` but will typically or in `on_attach`
---- via ``vim.api.nvim_buf_set_option(bufnr, 'formatexpr', 'v:lua.vim.lsp.formatexpr(#{timeout_ms:250})')``.
+--- via ``vim.bo[bufnr].formatexpr = 'v:lua.vim.lsp.formatexpr(#{timeout_ms:250})'``.
---
---@param opts table options for customizing the formatting expression which takes the
--- following optional keys:
diff --git a/runtime/lua/vim/lsp/_watchfiles.lua b/runtime/lua/vim/lsp/_watchfiles.lua
index 533a955925..cf2c57db1f 100644
--- a/runtime/lua/vim/lsp/_watchfiles.lua
+++ b/runtime/lua/vim/lsp/_watchfiles.lua
@@ -193,21 +193,27 @@ local to_lsp_change_type = {
function M.register(reg, ctx)
local client_id = ctx.client_id
local client = vim.lsp.get_client_by_id(client_id)
- if not client.workspace_folders then
+ if
+ -- Ill-behaved servers may not honor the client capability and try to register
+ -- anyway, so ignore requests when the user has opted out of the feature.
+ not client.config.capabilities.workspace.didChangeWatchedFiles.dynamicRegistration
+ or not client.workspace_folders
+ then
return
end
local watch_regs = {}
for _, w in ipairs(reg.registerOptions.watchers) do
+ local relative_pattern = false
local glob_patterns = {}
if type(w.globPattern) == 'string' then
for _, folder in ipairs(client.workspace_folders) do
table.insert(glob_patterns, { baseUri = folder.uri, pattern = w.globPattern })
end
else
+ relative_pattern = true
table.insert(glob_patterns, w.globPattern)
end
for _, glob_pattern in ipairs(glob_patterns) do
- local pattern = parse(glob_pattern.pattern)
local base_dir = nil
if type(glob_pattern.baseUri) == 'string' then
base_dir = glob_pattern.baseUri
@@ -216,9 +222,16 @@ function M.register(reg, ctx)
end
assert(base_dir, "couldn't identify root of watch")
base_dir = vim.uri_to_fname(base_dir)
+
local kind = w.kind
or protocol.WatchKind.Create + protocol.WatchKind.Change + protocol.WatchKind.Delete
+ local pattern = glob_pattern.pattern
+ if relative_pattern then
+ pattern = base_dir .. '/' .. pattern
+ end
+ pattern = parse(pattern)
+
table.insert(watch_regs, {
base_dir = base_dir,
pattern = pattern,
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index 3d9011656f..a307dea673 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -31,7 +31,9 @@ end
--- ready.
---
---@returns `true` if server responds.
+---@deprecated
function M.server_ready()
+ vim.deprecate('vim.lsp.buf.server_ready', nil, '0.10.0')
return not not vim.lsp.buf_notify(0, 'window/progress', {})
end
diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index 71bef43bc1..8e926c4644 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -454,7 +454,7 @@ function M.signature_help(_, result, ctx, config)
local client = vim.lsp.get_client_by_id(ctx.client_id)
local triggers =
vim.tbl_get(client.server_capabilities, 'signatureHelpProvider', 'triggerCharacters')
- local ft = api.nvim_buf_get_option(ctx.bufnr, 'filetype')
+ local ft = vim.bo[ctx.bufnr].filetype
local lines, hl = util.convert_signature_help_to_markdown_lines(result, ft, triggers)
lines = util.trim_empty_lines(lines)
if vim.tbl_isempty(lines) then
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 8274361f6d..9fffc845b1 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -401,7 +401,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding)
if not api.nvim_buf_is_loaded(bufnr) then
vim.fn.bufload(bufnr)
end
- api.nvim_buf_set_option(bufnr, 'buflisted', true)
+ vim.bo[bufnr].buflisted = true
-- Fix reversed range and indexing each text_edits
local index = 0
@@ -530,11 +530,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding)
-- Remove final line if needed
local fix_eol = has_eol_text_edit
- fix_eol = fix_eol
- and (
- api.nvim_buf_get_option(bufnr, 'eol')
- or (api.nvim_buf_get_option(bufnr, 'fixeol') and not api.nvim_buf_get_option(bufnr, 'binary'))
- )
+ fix_eol = fix_eol and (vim.bo[bufnr].eol or (vim.bo[bufnr].fixeol and not vim.bo[bufnr].binary))
fix_eol = fix_eol and get_line(bufnr, max - 1) == ''
if fix_eol then
api.nvim_buf_set_lines(bufnr, -2, -1, false, {})
@@ -1076,7 +1072,7 @@ function M.make_floating_popup_options(width, height, opts)
local wincol = opts.relative == 'mouse' and vim.fn.getmousepos().column or vim.fn.wincol()
- if wincol + width + (opts.offset_x or 0) <= api.nvim_get_option('columns') then
+ if wincol + width + (opts.offset_x or 0) <= vim.o.columns then
anchor = anchor .. 'W'
col = 0
else
@@ -1142,7 +1138,7 @@ function M.show_document(location, offset_encoding, opts)
or focus and api.nvim_get_current_win()
or create_window_without_focus()
- api.nvim_buf_set_option(bufnr, 'buflisted', true)
+ vim.bo[bufnr].buflisted = true
api.nvim_win_set_buf(win, bufnr)
if focus then
api.nvim_set_current_win(win)
@@ -1201,12 +1197,12 @@ function M.preview_location(location, opts)
end
local range = location.targetRange or location.range
local contents = api.nvim_buf_get_lines(bufnr, range.start.line, range['end'].line + 1, false)
- local syntax = api.nvim_buf_get_option(bufnr, 'syntax')
+ local syntax = vim.bo[bufnr].syntax
if syntax == '' then
-- When no syntax is set, we use filetype as fallback. This might not result
-- in a valid syntax definition. See also ft detection in stylize_markdown.
-- An empty syntax is more common now with TreeSitter, since TS disables syntax.
- syntax = api.nvim_buf_get_option(bufnr, 'filetype')
+ syntax = vim.bo[bufnr].filetype
end
opts = opts or {}
opts.focus_id = 'location'
@@ -1665,7 +1661,7 @@ function M.open_floating_preview(contents, syntax, opts)
contents = M.stylize_markdown(floating_bufnr, contents, opts)
else
if syntax then
- api.nvim_buf_set_option(floating_bufnr, 'syntax', syntax)
+ vim.bo[floating_bufnr].syntax = syntax
end
api.nvim_buf_set_lines(floating_bufnr, 0, -1, true, contents)
end
@@ -1681,16 +1677,16 @@ function M.open_floating_preview(contents, syntax, opts)
local float_option = M.make_floating_popup_options(width, height, opts)
local floating_winnr = api.nvim_open_win(floating_bufnr, false, float_option)
if do_stylize then
- api.nvim_win_set_option(floating_winnr, 'conceallevel', 2)
- api.nvim_win_set_option(floating_winnr, 'concealcursor', 'n')
+ vim.wo[floating_winnr].conceallevel = 2
+ vim.wo[floating_winnr].concealcursor = 'n'
end
-- disable folding
- api.nvim_win_set_option(floating_winnr, 'foldenable', false)
+ vim.wo[floating_winnr].foldenable = false
-- soft wrapping
- api.nvim_win_set_option(floating_winnr, 'wrap', opts.wrap)
+ vim.wo[floating_winnr].wrap = opts.wrap
- api.nvim_buf_set_option(floating_bufnr, 'modifiable', false)
- api.nvim_buf_set_option(floating_bufnr, 'bufhidden', 'wipe')
+ vim.bo[floating_bufnr].modifiable = false
+ vim.bo[floating_bufnr].bufhidden = 'wipe'
api.nvim_buf_set_keymap(
floating_bufnr,
'n',
diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua
index a55deb1415..4f230c4412 100644
--- a/runtime/lua/vim/shared.lua
+++ b/runtime/lua/vim/shared.lua
@@ -361,7 +361,7 @@ local function tbl_extend(behavior, deep_extend, ...)
return ret
end
---- Merges two or more map-like tables.
+--- Merges two or more tables.
---
---@see |extend()|
---
@@ -369,13 +369,13 @@ end
--- - "error": raise an error
--- - "keep": use value from the leftmost map
--- - "force": use value from the rightmost map
----@param ... table Two or more map-like tables
+---@param ... table Two or more tables
---@return table Merged table
function vim.tbl_extend(behavior, ...)
return tbl_extend(behavior, false, ...)
end
---- Merges recursively two or more map-like tables.
+--- Merges recursively two or more tables.
---
---@see |vim.tbl_extend()|
---
@@ -385,7 +385,7 @@ end
--- - "error": raise an error
--- - "keep": use value from the leftmost map
--- - "force": use value from the rightmost map
----@param ... T2 Two or more map-like tables
+---@param ... T2 Two or more tables
---@return T1|T2 (table) Merged table
function vim.tbl_deep_extend(behavior, ...)
return tbl_extend(behavior, true, ...)
diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua
index d1f5996768..12fbe1654f 100644
--- a/runtime/lua/vim/treesitter.lua
+++ b/runtime/lua/vim/treesitter.lua
@@ -136,16 +136,6 @@ function M.get_parser(bufnr, lang, opts)
return parsers[bufnr]
end
----@package
----@param bufnr (integer|nil) Buffer number
----@return boolean
-function M._has_parser(bufnr)
- if bufnr == nil or bufnr == 0 then
- bufnr = api.nvim_get_current_buf()
- end
- return parsers[bufnr] ~= nil
-end
-
--- Returns a string parser
---
---@param str string Text to parse
diff --git a/runtime/lua/vim/treesitter/_fold.lua b/runtime/lua/vim/treesitter/_fold.lua
index f6425d7cb9..a8f8c7967e 100644
--- a/runtime/lua/vim/treesitter/_fold.lua
+++ b/runtime/lua/vim/treesitter/_fold.lua
@@ -301,7 +301,8 @@ function M.foldexpr(lnum)
lnum = lnum or vim.v.lnum
local bufnr = api.nvim_get_current_buf()
- if not ts._has_parser(bufnr) or not lnum then
+ local parser = vim.F.npcall(ts.get_parser, bufnr)
+ if not parser then
return '0'
end
@@ -309,7 +310,6 @@ function M.foldexpr(lnum)
foldinfos[bufnr] = FoldInfo.new()
get_folds_levels(bufnr, foldinfos[bufnr])
- local parser = ts.get_parser(bufnr)
parser:register_cbs({
on_changedtree = function(tree_changes)
on_changedtree(bufnr, foldinfos[bufnr], tree_changes)
diff --git a/runtime/lua/vim/treesitter/_meta.lua b/runtime/lua/vim/treesitter/_meta.lua
index 4d0f43d030..9ca4b560c6 100644
--- a/runtime/lua/vim/treesitter/_meta.lua
+++ b/runtime/lua/vim/treesitter/_meta.lua
@@ -31,17 +31,21 @@ local TSNode = {}
---@param query userdata
---@param captures true
----@param start integer
----@param end_ integer
+---@param start? integer
+---@param end_? integer
+---@param opts? table
---@return fun(): integer, TSNode, any
-function TSNode:_rawquery(query, captures, start, end_) end
+function TSNode:_rawquery(query, captures, start, end_, opts) end
---@param query userdata
---@param captures false
----@param start integer
----@param end_ integer
+---@param start? integer
+---@param end_? integer
+---@param opts? table
---@return fun(): string, any
-function TSNode:_rawquery(query, captures, start, end_) end
+function TSNode:_rawquery(query, captures, start, end_, opts) end
+
+---@alias TSLoggerCallback fun(logtype: 'parse'|'lex', msg: string)
---@class TSParser
---@field parse fun(self: TSParser, tree: TSTree?, source: integer|string, include_bytes: boolean?): TSTree, integer[]
@@ -50,6 +54,8 @@ function TSNode:_rawquery(query, captures, start, end_) end
---@field set_included_ranges fun(self: TSParser, ranges: Range6[])
---@field set_timeout fun(self: TSParser, timeout: integer)
---@field timeout fun(self: TSParser): integer
+---@field _set_logger fun(self: TSParser, lex: boolean, parse: boolean, cb: TSLoggerCallback)
+---@field _logger fun(self: TSParser): TSLoggerCallback
---@class TSTree
---@field root fun(self: TSTree): TSNode
diff --git a/runtime/lua/vim/treesitter/_range.lua b/runtime/lua/vim/treesitter/_range.lua
index f4db5016ac..35081c6400 100644
--- a/runtime/lua/vim/treesitter/_range.lua
+++ b/runtime/lua/vim/treesitter/_range.lua
@@ -143,6 +143,29 @@ function M.contains(r1, r2)
return true
end
+--- @param source integer|string
+--- @param index integer
+--- @return integer
+local function get_offset(source, index)
+ if index == 0 then
+ return 0
+ end
+
+ if type(source) == 'number' then
+ return api.nvim_buf_get_offset(source, index)
+ end
+
+ local byte = 0
+ local next_offset = source:gmatch('()\n')
+ local line = 1
+ while line <= index do
+ byte = next_offset() --[[@as integer]]
+ line = line + 1
+ end
+
+ return byte
+end
+
---@private
---@param source integer|string
---@param range Range
@@ -152,19 +175,10 @@ function M.add_bytes(source, range)
return range --[[@as Range6]]
end
- local start_row, start_col, end_row, end_col = range[1], range[2], range[3], range[4]
- local start_byte = 0
- local end_byte = 0
+ local start_row, start_col, end_row, end_col = M.unpack4(range)
-- TODO(vigoux): proper byte computation here, and account for EOL ?
- if type(source) == 'number' then
- -- Easy case, this is a buffer parser
- start_byte = api.nvim_buf_get_offset(source, start_row) + start_col
- end_byte = api.nvim_buf_get_offset(source, end_row) + end_col
- elseif type(source) == 'string' then
- -- string parser, single `\n` delimited string
- start_byte = vim.fn.byteidx(source, start_col)
- end_byte = vim.fn.byteidx(source, end_col)
- end
+ local start_byte = get_offset(source, start_row) + start_col
+ local end_byte = get_offset(source, end_row) + end_col
return { start_row, start_col, start_byte, end_row, end_col, end_byte }
end
diff --git a/runtime/lua/vim/treesitter/language.lua b/runtime/lua/vim/treesitter/language.lua
index b616d4d70b..08c297c9ad 100644
--- a/runtime/lua/vim/treesitter/language.lua
+++ b/runtime/lua/vim/treesitter/language.lua
@@ -27,6 +27,11 @@ function M.get_lang(filetype)
if filetype == '' then
return
end
+ if ft_to_lang[filetype] then
+ return ft_to_lang[filetype]
+ end
+ -- support subfiletypes like html.glimmer
+ filetype = vim.split(filetype, '.', { plain = true })[1]
return ft_to_lang[filetype]
end
diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua
index 1adf6759fa..6c780f33c4 100644
--- a/runtime/lua/vim/treesitter/languagetree.lua
+++ b/runtime/lua/vim/treesitter/languagetree.lua
@@ -31,8 +31,17 @@
--- shouldn't be done directly in the change callback anyway as they will be very frequent. Rather
--- a plugin that does any kind of analysis on a tree should use a timer to throttle too frequent
--- updates.
+---
+
+-- Debugging:
+--
+-- vim.g.__ts_debug levels:
+-- - 1. Messages from languagetree.lua
+-- - 2. Parse messages from treesitter
+-- - 2. Lex messages from treesitter
+--
+-- Log file can be found in stdpath('log')/treesitter.log
-local api = vim.api
local query = require('vim.treesitter.query')
local language = require('vim.treesitter.language')
local Range = require('vim.treesitter._range')
@@ -75,6 +84,8 @@ local TSCallbackNames = {
---@field private _source (integer|string) Buffer or string to parse
---@field private _trees TSTree[] Reference to parsed tree (one for each language)
---@field private _valid boolean|table<integer,boolean> If the parsed tree is valid
+---@field private _logger? fun(logtype: string, msg: string)
+---@field private _logfile? file*
local LanguageTree = {}
---@class LanguageTreeOpts
@@ -114,6 +125,10 @@ function LanguageTree.new(source, lang, opts)
_callbacks_rec = {},
}, LanguageTree)
+ if vim.g.__ts_debug and type(vim.g.__ts_debug) == 'number' then
+ self:_set_logger()
+ end
+
for _, name in pairs(TSCallbackNames) do
self._callbacks[name] = {}
self._callbacks_rec[name] = {}
@@ -122,6 +137,33 @@ function LanguageTree.new(source, lang, opts)
return self
end
+function LanguageTree:_set_logger()
+ local source = self:source()
+ source = type(source) == 'string' and 'text' or tostring(source)
+
+ local lang = self:lang()
+
+ local logfilename = vim.fs._join_paths(vim.fn.stdpath('log'), 'treesitter.log')
+
+ local logfile, openerr = io.open(logfilename, 'a+')
+
+ if not logfile or openerr then
+ error(string.format('Could not open file (%s) for logging: %s', logfilename, openerr))
+ return
+ end
+
+ self._logfile = logfile
+
+ self._logger = function(logtype, msg)
+ self._logfile:write(string.format('%s:%s:(%s) %s\n', source, lang, logtype, msg))
+ self._logfile:flush()
+ end
+
+ local log_lex = vim.g.__ts_debug >= 3
+ local log_parse = vim.g.__ts_debug >= 2
+ self._parser:_set_logger(log_lex, log_parse, self._logger)
+end
+
---@private
---Measure execution time of a function
---@generic R1, R2, R3
@@ -139,7 +181,11 @@ end
---@private
---@vararg any
function LanguageTree:_log(...)
- if vim.g.__ts_debug == nil then
+ if not self._logger then
+ return
+ end
+
+ if not vim.g.__ts_debug or vim.g.__ts_debug < 1 then
return
end
@@ -150,19 +196,17 @@ function LanguageTree:_log(...)
local info = debug.getinfo(2, 'nl')
local nregions = #self:included_regions()
- local prefix =
- string.format('%s:%d: [%s:%d] ', info.name, info.currentline, self:lang(), nregions)
+ local prefix = string.format('%s:%d: (#regions=%d) ', info.name, info.currentline, nregions)
- api.nvim_out_write(prefix)
+ local msg = { prefix }
for _, x in ipairs(args) do
if type(x) == 'string' then
- api.nvim_out_write(x)
+ msg[#msg + 1] = x
else
- api.nvim_out_write(vim.inspect(x, { newline = ' ', indent = '' }))
+ msg[#msg + 1] = vim.inspect(x, { newline = ' ', indent = '' })
end
- api.nvim_out_write(' ')
end
- api.nvim_out_write('\n')
+ self._logger('nvim', table.concat(msg, ' '))
end
--- Invalidates this parser and all its children
@@ -876,16 +920,23 @@ end
function LanguageTree:_on_detach(...)
self:invalidate(true)
self:_do_callback('detach', ...)
+ if self._logfile then
+ self._logger('nvim', 'detaching')
+ self._logger = nil
+ self._logfile:close()
+ end
end
--- Registers callbacks for the |LanguageTree|.
---@param cbs table An |nvim_buf_attach()|-like table argument with the following handlers:
--- - `on_bytes` : see |nvim_buf_attach()|, but this will be called _after_ the parsers callback.
--- - `on_changedtree` : a callback that will be called every time the tree has syntactical changes.
---- It will only be passed one argument, which is a table of the ranges (as node ranges) that
---- changed.
+--- It will be passed two arguments: a table of the ranges (as node ranges) that
+--- changed and the changed tree.
--- - `on_child_added` : emitted when a child is added to the tree.
--- - `on_child_removed` : emitted when a child is removed from the tree.
+--- - `on_detach` : emitted when the buffer is detached, see |nvim_buf_detach_event|.
+--- Takes one argument, the number of the buffer.
--- @param recursive? boolean Apply callbacks recursively for all children. Any new children will
--- also inherit the callbacks.
function LanguageTree:register_cbs(cbs, recursive)
diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua
index 93841bb31e..73b561c777 100644
--- a/runtime/lua/vim/treesitter/query.lua
+++ b/runtime/lua/vim/treesitter/query.lua
@@ -382,6 +382,39 @@ local predicate_handlers = {
return string_set[node_text]
end,
+
+ ['has-ancestor?'] = function(match, _, _, predicate)
+ local node = match[predicate[2]]
+ if not node then
+ return true
+ end
+
+ local ancestor_types = {}
+ for _, type in ipairs({ unpack(predicate, 3) }) do
+ ancestor_types[type] = true
+ end
+
+ node = node:parent()
+ while node do
+ if ancestor_types[node:type()] then
+ return true
+ end
+ node = node:parent()
+ end
+ return false
+ end,
+
+ ['has-parent?'] = function(match, _, _, predicate)
+ local node = match[predicate[2]]
+ if not node then
+ return true
+ end
+
+ if vim.list_contains({ unpack(predicate, 3) }, node:parent():type()) then
+ return true
+ end
+ return false
+ end,
}
-- As we provide lua-match? also expose vim-match?
@@ -609,10 +642,10 @@ end
---
--- {source} is needed if the query contains predicates; then the caller
--- must ensure to use a freshly parsed tree consistent with the current
---- text of the buffer (if relevant). {start_row} and {end_row} can be used to limit
+--- text of the buffer (if relevant). {start} and {stop} can be used to limit
--- matches inside a row range (this is typically used with root node
--- as the {node}, i.e., to get syntax highlight matches in the current
---- viewport). When omitted, the {start} and {end} row values are used from the given node.
+--- viewport). When omitted, the {start} and {stop} row values are used from the given node.
---
--- The iterator returns three values: a numeric id identifying the capture,
--- the captured node, and metadata from any directives processing the match.
@@ -686,16 +719,20 @@ end
---@param source (integer|string) Source buffer or string to search
---@param start integer Starting line for the search
---@param stop integer Stopping line for the search (end-exclusive)
+---@param opts table|nil Options:
+--- - max_start_depth (integer) if non-zero, sets the maximum start depth
+--- for each match. This is used to prevent traversing too deep into a tree.
+--- Requires treesitter >= 0.20.9.
---
---@return (fun(): integer, table<integer,TSNode>, table): pattern id, match, metadata
-function Query:iter_matches(node, source, start, stop)
+function Query:iter_matches(node, source, start, stop, opts)
if type(source) == 'number' and source == 0 then
source = api.nvim_get_current_buf()
end
start, stop = value_or_node_range(start, stop, node)
- local raw_iter = node:_rawquery(self.query, false, start, stop)
+ local raw_iter = node:_rawquery(self.query, false, start, stop, opts)
---@cast raw_iter fun(): string, any
local function iter()
local pattern, match = raw_iter()
diff --git a/runtime/lua/vim/version.lua b/runtime/lua/vim/version.lua
index 3aacf3d4e0..ebe8f4e053 100644
--- a/runtime/lua/vim/version.lua
+++ b/runtime/lua/vim/version.lua
@@ -333,7 +333,7 @@ local function create_err_msg(v)
return string.format('invalid version: %s (%s)', tostring(v), type(v))
end
---- Parses and compares two version version objects (the result of |vim.version.parse()|, or
+--- Parses and compares two version objects (the result of |vim.version.parse()|, or
--- specified literally as a `{major, minor, patch}` tuple, e.g. `{1, 0, 3}`).
---
--- Example: