mirror of
https://github.com/neovim/neovim.git
synced 2026-05-28 15:55:34 +00:00
Merge #39802 from ofseed/lsp-remove-private
This commit is contained in:
@@ -10,7 +10,8 @@ local util = lsp.util
|
||||
local function mk_tag_item(name, range, uri, position_encoding)
|
||||
local bufnr = vim.uri_to_bufnr(uri)
|
||||
-- This is get_line_byte_from_position is 0-indexed, call cursor expects a 1-indexed position
|
||||
local byte = util._get_line_byte_from_position(bufnr, range.start, position_encoding) + 1
|
||||
local pos = vim.pos.lsp(bufnr, range.start, position_encoding)
|
||||
local byte = pos.col + 1
|
||||
return {
|
||||
name = name,
|
||||
filename = vim.uri_to_fname(uri),
|
||||
|
||||
@@ -38,17 +38,21 @@ local function ctx_is_valid(ctx)
|
||||
then
|
||||
return false
|
||||
end
|
||||
---@type lsp.Position?
|
||||
local p = ctx.params and ctx.params.position
|
||||
if not p then
|
||||
return true
|
||||
end
|
||||
|
||||
local cur = api.nvim_win_get_cursor(0)
|
||||
local c = lsp.get_client_by_id(ctx.client_id)
|
||||
local enc = c and c.offset_encoding
|
||||
if not enc then
|
||||
return false
|
||||
end
|
||||
|
||||
return cur[1] - 1 == p.line and enc and cur[2] == util._get_line_byte_from_position(bufnr, p, enc)
|
||||
or false
|
||||
local cur_pos = vim.pos.cursor(bufnr, api.nvim_win_get_cursor(0))
|
||||
local pos = vim.pos.lsp(bufnr, p, enc)
|
||||
return cur_pos == pos
|
||||
end
|
||||
|
||||
--- @class vim.lsp.buf.hover.Opts : vim.lsp.util.open_floating_preview.Opts
|
||||
@@ -162,19 +166,15 @@ function M.hover(config)
|
||||
else
|
||||
vim.list_extend(contents, util.convert_input_to_markdown_lines(result.contents))
|
||||
end
|
||||
local range = result.range
|
||||
if range then
|
||||
local start = range.start
|
||||
local end_ = range['end']
|
||||
local start_idx = util._get_line_byte_from_position(bufnr, start, client.offset_encoding)
|
||||
local end_idx = util._get_line_byte_from_position(bufnr, end_, client.offset_encoding)
|
||||
if result.range then
|
||||
local range = vim.range.lsp(bufnr, result.range, client.offset_encoding)
|
||||
|
||||
vim.hl.range(
|
||||
bufnr,
|
||||
hover_ns,
|
||||
'LspReferenceTarget',
|
||||
{ start.line, start_idx },
|
||||
{ end_.line, end_idx },
|
||||
{ range.start_row, range.start_col },
|
||||
{ range.end_row, range.end_col },
|
||||
{ priority = vim.hl.priorities.user }
|
||||
)
|
||||
end
|
||||
@@ -739,12 +739,13 @@ function M.rename(new_name, opts)
|
||||
--- @param range lsp.Range
|
||||
--- @param position_encoding 'utf-8'|'utf-16'|'utf-32'
|
||||
local function get_text_at_range(range, position_encoding)
|
||||
local vim_range = vim.range.lsp(bufnr, range, position_encoding)
|
||||
return api.nvim_buf_get_text(
|
||||
bufnr,
|
||||
range.start.line,
|
||||
util._get_line_byte_from_position(bufnr, range.start, position_encoding),
|
||||
range['end'].line,
|
||||
util._get_line_byte_from_position(bufnr, range['end'], position_encoding),
|
||||
vim_range.start_row,
|
||||
vim_range.start_col,
|
||||
vim_range.end_row,
|
||||
vim_range.end_col,
|
||||
{}
|
||||
)[1]
|
||||
end
|
||||
@@ -787,26 +788,21 @@ function M.rename(new_name, opts)
|
||||
return
|
||||
end
|
||||
|
||||
local range ---@type lsp.Range?
|
||||
local range ---@type vim.Range?
|
||||
if result.start then
|
||||
---@cast result lsp.Range
|
||||
range = result
|
||||
range = vim.range.lsp(bufnr, result, client.offset_encoding)
|
||||
elseif result.range then
|
||||
---@cast result { range: lsp.Range, placeholder: string }
|
||||
range = result.range
|
||||
range = vim.range.lsp(bufnr, result.range, client.offset_encoding)
|
||||
end
|
||||
if range then
|
||||
local start = range.start
|
||||
local end_ = range['end']
|
||||
local start_idx = util._get_line_byte_from_position(bufnr, start, client.offset_encoding)
|
||||
local end_idx = util._get_line_byte_from_position(bufnr, end_, client.offset_encoding)
|
||||
|
||||
vim.hl.range(
|
||||
bufnr,
|
||||
rename_ns,
|
||||
'LspReferenceTarget',
|
||||
{ start.line, start_idx },
|
||||
{ end_.line, end_idx },
|
||||
{ range.start_row, range.start_col },
|
||||
{ range.end_row, range.end_col },
|
||||
{ priority = vim.hl.priorities.user }
|
||||
)
|
||||
end
|
||||
|
||||
@@ -574,16 +574,12 @@ local function make_type_hierarchy_handler()
|
||||
local client = assert(vim.lsp.get_client_by_id(ctx.client_id))
|
||||
local items = {}
|
||||
for _, type_hierarchy_item in pairs(result) do
|
||||
local col = util._get_line_byte_from_position(
|
||||
ctx.bufnr,
|
||||
type_hierarchy_item.range.start,
|
||||
client.offset_encoding
|
||||
)
|
||||
local pos = vim.pos.lsp(ctx.bufnr, type_hierarchy_item.range.start, client.offset_encoding)
|
||||
table.insert(items, {
|
||||
filename = assert(vim.uri_to_fname(type_hierarchy_item.uri)),
|
||||
text = format_item(type_hierarchy_item),
|
||||
lnum = type_hierarchy_item.range.start.line + 1,
|
||||
col = col + 1,
|
||||
lnum = pos.row + 1,
|
||||
col = pos.col + 1,
|
||||
})
|
||||
end
|
||||
vim.fn.setqflist({}, ' ', { title = 'LSP type hierarchy', items = items })
|
||||
|
||||
@@ -100,12 +100,9 @@ local function refresh(bufnr, client_id)
|
||||
do
|
||||
client:request('textDocument/inlayHint', {
|
||||
textDocument = util.make_text_document_params(bufnr),
|
||||
range = util._make_line_range_params(
|
||||
bufnr,
|
||||
0,
|
||||
api.nvim_buf_line_count(bufnr) - 1,
|
||||
client.offset_encoding
|
||||
),
|
||||
range = vim
|
||||
.range(bufnr, 0, 0, api.nvim_buf_line_count(bufnr), 0)
|
||||
:to_lsp(client.offset_encoding),
|
||||
}, nil, bufnr)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -133,115 +133,9 @@ local function sort_by_key(fn)
|
||||
end
|
||||
end
|
||||
|
||||
--- Gets the zero-indexed lines from the given buffer.
|
||||
--- Works on unloaded buffers by reading the file using libuv to bypass buf reading events.
|
||||
--- Falls back to loading the buffer and nvim_buf_get_lines for buffers with non-file URI.
|
||||
---
|
||||
---@param bufnr integer bufnr to get the lines from
|
||||
---@param rows integer[] zero-indexed line numbers
|
||||
---@return table<integer, string> # a table mapping rows to lines
|
||||
local function get_lines(bufnr, rows)
|
||||
--- @type integer[]
|
||||
rows = type(rows) == 'table' and rows or { rows }
|
||||
local get_lines = vim.pos._get_lines
|
||||
|
||||
-- This is needed for bufload and bufloaded
|
||||
bufnr = vim._resolve_bufnr(bufnr)
|
||||
|
||||
local function buf_lines()
|
||||
local lines = {} --- @type table<integer,string>
|
||||
for _, row in ipairs(rows) do
|
||||
lines[row] = (api.nvim_buf_get_lines(bufnr, row, row + 1, false) or { '' })[1]
|
||||
end
|
||||
return lines
|
||||
end
|
||||
|
||||
-- use loaded buffers if available
|
||||
if vim.fn.bufloaded(bufnr) == 1 then
|
||||
return buf_lines()
|
||||
end
|
||||
|
||||
local uri = vim.uri_from_bufnr(bufnr)
|
||||
|
||||
-- load the buffer if this is not a file uri
|
||||
-- Custom language server protocol extensions can result in servers sending URIs with custom schemes. Plugins are able to load these via `BufReadCmd` autocmds.
|
||||
if uri:sub(1, 4) ~= 'file' then
|
||||
vim.fn.bufload(bufnr)
|
||||
return buf_lines()
|
||||
end
|
||||
|
||||
local filename = api.nvim_buf_get_name(bufnr)
|
||||
if vim.fn.isdirectory(filename) ~= 0 then
|
||||
return {}
|
||||
end
|
||||
|
||||
-- get the data from the file
|
||||
local fd = uv.fs_open(filename, 'r', 438)
|
||||
if not fd then
|
||||
return {}
|
||||
end
|
||||
local stat = assert(uv.fs_fstat(fd))
|
||||
local data = assert(uv.fs_read(fd, stat.size, 0))
|
||||
uv.fs_close(fd)
|
||||
|
||||
local lines = {} --- @type table<integer,true|string> rows we need to retrieve
|
||||
local need = 0 -- keep track of how many unique rows we need
|
||||
for _, row in pairs(rows) do
|
||||
if not lines[row] then
|
||||
need = need + 1
|
||||
end
|
||||
lines[row] = true
|
||||
end
|
||||
|
||||
local found = 0
|
||||
local lnum = 0
|
||||
|
||||
for line in string.gmatch(data, '([^\n]*)\n?') do
|
||||
if lines[lnum] == true then
|
||||
lines[lnum] = line
|
||||
found = found + 1
|
||||
if found == need then
|
||||
break
|
||||
end
|
||||
end
|
||||
lnum = lnum + 1
|
||||
end
|
||||
|
||||
-- change any lines we didn't find to the empty string
|
||||
for i, line in pairs(lines) do
|
||||
if line == true then
|
||||
lines[i] = ''
|
||||
end
|
||||
end
|
||||
return lines --[[@as table<integer,string>]]
|
||||
end
|
||||
|
||||
--- Gets the zero-indexed line from the given buffer.
|
||||
--- Works on unloaded buffers by reading the file using libuv to bypass buf reading events.
|
||||
--- Falls back to loading the buffer and nvim_buf_get_lines for buffers with non-file URI.
|
||||
---
|
||||
---@param bufnr integer
|
||||
---@param row integer zero-indexed line number
|
||||
---@return string the line at row in filename
|
||||
local function get_line(bufnr, row)
|
||||
return get_lines(bufnr, { row })[row]
|
||||
end
|
||||
|
||||
--- Position is a https://microsoft.github.io/language-server-protocol/specifications/specification-current/#position
|
||||
---@param position lsp.Position
|
||||
---@param position_encoding 'utf-8'|'utf-16'|'utf-32'
|
||||
---@return integer
|
||||
local function get_line_byte_from_position(bufnr, position, position_encoding)
|
||||
-- LSP's line and characters are 0-indexed
|
||||
-- Vim's line and columns are 1-indexed
|
||||
local col = position.character
|
||||
-- When on the first character, we can ignore the difference between byte and
|
||||
-- character
|
||||
if col > 0 then
|
||||
local line = get_line(bufnr, position.line) or ''
|
||||
return vim.str_byteindex(line, position_encoding, col, false)
|
||||
end
|
||||
return col
|
||||
end
|
||||
local get_line = vim.pos._get_line
|
||||
|
||||
--- Applies a list of text edits to a buffer. Note: this mutates `text_edits` (sorts in-place and
|
||||
--- adds `_index` fields).
|
||||
@@ -318,10 +212,9 @@ function M.apply_text_edits(text_edits, bufnr, position_encoding, change_annotat
|
||||
text_edit.newText, _ = string.gsub(text_edit.newText, '\r\n?', '\n')
|
||||
|
||||
-- Convert from LSP style ranges to Neovim style ranges.
|
||||
local start_row = text_edit.range.start.line
|
||||
local start_col = get_line_byte_from_position(bufnr, text_edit.range.start, position_encoding)
|
||||
local end_row = text_edit.range['end'].line
|
||||
local end_col = get_line_byte_from_position(bufnr, text_edit.range['end'], position_encoding)
|
||||
local range = vim.range.lsp(bufnr, text_edit.range, position_encoding)
|
||||
local start_row, start_col, end_row, end_col =
|
||||
range.start_row, range.start_col, range.end_row, range.end_col
|
||||
local text = vim.split(text_edit.newText, '\n', { plain = true })
|
||||
|
||||
local max = api.nvim_buf_line_count(bufnr)
|
||||
@@ -991,8 +884,8 @@ function M.show_document(location, position_encoding, opts)
|
||||
local range = location.range or location.targetSelectionRange
|
||||
if range then
|
||||
-- Jump to new location (adjusting for encoding of characters)
|
||||
local row = range.start.line
|
||||
local col = get_line_byte_from_position(bufnr, range.start, position_encoding)
|
||||
local pos = vim.pos.lsp(bufnr, range.start, position_encoding)
|
||||
local row, col = pos.row, pos.col
|
||||
api.nvim_win_set_cursor(win, { row + 1, col })
|
||||
vim._with({ win = win }, function()
|
||||
-- Open folds under the cursor
|
||||
@@ -1764,12 +1657,7 @@ do --[[ References ]]
|
||||
validate('bufnr', bufnr, 'number', true)
|
||||
validate('position_encoding', position_encoding, 'string', false)
|
||||
for _, reference in ipairs(references) do
|
||||
local range = reference.range
|
||||
local start_line = range.start.line
|
||||
local end_line = range['end'].line
|
||||
|
||||
local start_idx = get_line_byte_from_position(bufnr, range.start, position_encoding)
|
||||
local end_idx = get_line_byte_from_position(bufnr, range['end'], position_encoding)
|
||||
local range = vim.range.lsp(bufnr, reference.range, position_encoding)
|
||||
|
||||
local document_highlight_kind = {
|
||||
[protocol.DocumentHighlightKind.Text] = 'LspReferenceText',
|
||||
@@ -1781,8 +1669,8 @@ do --[[ References ]]
|
||||
bufnr,
|
||||
reference_ns,
|
||||
document_highlight_kind[kind],
|
||||
{ start_line, start_idx },
|
||||
{ end_line, end_idx },
|
||||
{ range.start_row, range.start_col },
|
||||
{ range.end_row, range.end_col },
|
||||
{ priority = vim.hl.priorities.user }
|
||||
)
|
||||
end
|
||||
@@ -1872,27 +1760,21 @@ function M.symbols_to_items(symbols, bufnr, position_encoding)
|
||||
|
||||
local items = {} --- @type vim.quickfix.entry[]
|
||||
for _, symbol in ipairs(symbols) do
|
||||
--- @type string?, lsp.Range?
|
||||
--- @type string?, vim.Range?
|
||||
local filename, range
|
||||
|
||||
if symbol.location then
|
||||
--- @cast symbol lsp.SymbolInformation
|
||||
filename = vim.uri_to_fname(symbol.location.uri)
|
||||
range = symbol.location.range
|
||||
range = vim.range.lsp(bufnr, symbol.location.range, position_encoding)
|
||||
elseif symbol.selectionRange then
|
||||
--- @cast symbol lsp.DocumentSymbol
|
||||
filename = api.nvim_buf_get_name(bufnr)
|
||||
range = symbol.selectionRange
|
||||
range = vim.range.lsp(bufnr, symbol.selectionRange, position_encoding)
|
||||
end
|
||||
|
||||
if filename and range then
|
||||
local kind = protocol.SymbolKind[symbol.kind] or 'Unknown'
|
||||
|
||||
local lnum = range['start'].line + 1
|
||||
local col = get_line_byte_from_position(bufnr, range['start'], position_encoding) + 1
|
||||
local end_lnum = range['end'].line + 1
|
||||
local end_col = get_line_byte_from_position(bufnr, range['end'], position_encoding) + 1
|
||||
|
||||
local is_deprecated = not vim.isnil(symbol.deprecated or nil)
|
||||
or (
|
||||
not vim.isnil(symbol.tags)
|
||||
@@ -1908,10 +1790,10 @@ function M.symbols_to_items(symbols, bufnr, position_encoding)
|
||||
|
||||
items[#items + 1] = {
|
||||
filename = filename,
|
||||
lnum = lnum,
|
||||
col = col,
|
||||
end_lnum = end_lnum,
|
||||
end_col = end_col,
|
||||
lnum = range.start_row + 1,
|
||||
col = range.start_col + 1,
|
||||
end_lnum = range.end_row + 1,
|
||||
end_col = range.end_col + 1,
|
||||
kind = kind,
|
||||
text = text,
|
||||
}
|
||||
@@ -1925,23 +1807,6 @@ function M.symbols_to_items(symbols, bufnr, position_encoding)
|
||||
return items
|
||||
end
|
||||
|
||||
---@param win integer?: |window-ID| or 0 for current, defaults to current
|
||||
---@param position_encoding 'utf-8'|'utf-16'|'utf-32'
|
||||
local function make_position_param(win, position_encoding)
|
||||
win = win or 0
|
||||
local buf = api.nvim_win_get_buf(win)
|
||||
local row, col = unpack(api.nvim_win_get_cursor(win))
|
||||
row = row - 1
|
||||
local line = api.nvim_buf_get_lines(buf, row, row + 1, true)[1]
|
||||
if not line then
|
||||
return { line = 0, character = 0 }
|
||||
end
|
||||
|
||||
col = vim.str_utfindex(line, position_encoding, col, false)
|
||||
|
||||
return { line = row, character = col }
|
||||
end
|
||||
|
||||
--- Creates a `TextDocumentPositionParams` object for the current buffer and cursor position.
|
||||
---
|
||||
---@param win integer?: |window-ID| or 0 for current, defaults to current
|
||||
@@ -1953,7 +1818,7 @@ function M.make_position_params(win, position_encoding)
|
||||
local buf = api.nvim_win_get_buf(win)
|
||||
return {
|
||||
textDocument = M.make_text_document_params(buf),
|
||||
position = make_position_param(win, position_encoding),
|
||||
position = vim.pos.cursor(buf, api.nvim_win_get_cursor(win)):to_lsp(position_encoding),
|
||||
}
|
||||
end
|
||||
|
||||
@@ -1966,8 +1831,9 @@ end
|
||||
---@param position_encoding "utf-8"|"utf-16"|"utf-32"
|
||||
---@return { textDocument: { uri: lsp.DocumentUri }, range: lsp.Range }
|
||||
function M.make_range_params(win, position_encoding)
|
||||
local buf = api.nvim_win_get_buf(win or 0)
|
||||
local position = make_position_param(win, position_encoding)
|
||||
win = win or 0
|
||||
local buf = api.nvim_win_get_buf(win)
|
||||
local position = vim.pos.cursor(buf, api.nvim_win_get_cursor(win)):to_lsp(position_encoding)
|
||||
return {
|
||||
textDocument = M.make_text_document_params(buf),
|
||||
range = { start = position, ['end'] = position },
|
||||
@@ -2079,40 +1945,6 @@ function M.character_offset(buf, row, col, position_encoding)
|
||||
return vim.str_utfindex(line, position_encoding, col, false)
|
||||
end
|
||||
|
||||
--- Converts line range (0-based, end-inclusive) to lsp range,
|
||||
--- handles absence of a trailing newline
|
||||
---
|
||||
---@param bufnr integer
|
||||
---@param start_line integer
|
||||
---@param end_line integer
|
||||
---@param position_encoding 'utf-8'|'utf-16'|'utf-32'
|
||||
---@return lsp.Range
|
||||
function M._make_line_range_params(bufnr, start_line, end_line, position_encoding)
|
||||
local last_line = api.nvim_buf_line_count(bufnr) - 1
|
||||
|
||||
---@type lsp.Position
|
||||
local end_pos
|
||||
|
||||
if end_line == last_line and not vim.bo[bufnr].endofline then
|
||||
end_pos = {
|
||||
line = end_line,
|
||||
character = M.character_offset(
|
||||
bufnr,
|
||||
end_line,
|
||||
#get_line(bufnr, end_line),
|
||||
position_encoding
|
||||
),
|
||||
}
|
||||
else
|
||||
end_pos = { line = end_line + 1, character = 0 }
|
||||
end
|
||||
|
||||
return {
|
||||
start = { line = start_line, character = 0 },
|
||||
['end'] = end_pos,
|
||||
}
|
||||
end
|
||||
|
||||
---@class (private) vim.lsp.util._cancel_requests.Filter
|
||||
---@field bufnr? integer
|
||||
---@field clients? vim.lsp.Client[]
|
||||
@@ -2147,8 +1979,6 @@ function M._cancel_requests(filter)
|
||||
end
|
||||
end
|
||||
|
||||
M._get_line_byte_from_position = get_line_byte_from_position
|
||||
|
||||
---@nodoc
|
||||
---@type table<integer,integer>
|
||||
M.buf_versions = setmetatable({}, {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
--- objects.
|
||||
|
||||
local api = vim.api
|
||||
local uv = vim.uv
|
||||
local validate = vim.validate
|
||||
|
||||
--- Represents a well-defined position.
|
||||
@@ -116,11 +117,97 @@ function M.__eq(...)
|
||||
return cmp_pos(...) == 0
|
||||
end
|
||||
|
||||
--- TODO(ofseed): Make it work for unloaded buffers. Check get_line() in vim.lsp.util.
|
||||
---@param buf integer
|
||||
---@param row integer
|
||||
local function get_line(buf, row)
|
||||
return api.nvim_buf_get_lines(buf, row, row + 1, true)[1]
|
||||
--- Gets the zero-indexed lines from the given buffer.
|
||||
--- Works on unloaded buffers by reading the file using libuv to bypass buf reading events.
|
||||
--- Falls back to loading the buffer and nvim_buf_get_lines for buffers with non-file URI.
|
||||
---
|
||||
---@param bufnr integer bufnr to get the lines from
|
||||
---@param rows integer[] zero-indexed line numbers
|
||||
---@return table<integer, string> # a table mapping rows to lines
|
||||
local function get_lines(bufnr, rows)
|
||||
--- @type integer[]
|
||||
rows = type(rows) == 'table' and rows or { rows }
|
||||
|
||||
-- This is needed for bufload and bufloaded
|
||||
bufnr = vim._resolve_bufnr(bufnr)
|
||||
|
||||
local function buf_lines()
|
||||
local lines = {} --- @type table<integer,string>
|
||||
for _, row in ipairs(rows) do
|
||||
lines[row] = (api.nvim_buf_get_lines(bufnr, row, row + 1, false) or { '' })[1]
|
||||
end
|
||||
return lines
|
||||
end
|
||||
|
||||
-- use loaded buffers if available
|
||||
if vim.fn.bufloaded(bufnr) == 1 then
|
||||
return buf_lines()
|
||||
end
|
||||
|
||||
local uri = vim.uri_from_bufnr(bufnr)
|
||||
|
||||
-- load the buffer if this is not a file uri
|
||||
-- Custom language server protocol extensions can result in servers sending URIs with custom schemes. Plugins are able to load these via `BufReadCmd` autocmds.
|
||||
if uri:sub(1, 4) ~= 'file' then
|
||||
vim.fn.bufload(bufnr)
|
||||
return buf_lines()
|
||||
end
|
||||
|
||||
local filename = api.nvim_buf_get_name(bufnr)
|
||||
if vim.fn.isdirectory(filename) ~= 0 then
|
||||
return {}
|
||||
end
|
||||
|
||||
-- get the data from the file
|
||||
local fd = uv.fs_open(filename, 'r', 438)
|
||||
if not fd then
|
||||
return {}
|
||||
end
|
||||
local stat = assert(uv.fs_fstat(fd))
|
||||
local data = assert(uv.fs_read(fd, stat.size, 0))
|
||||
uv.fs_close(fd)
|
||||
|
||||
local lines = {} --- @type table<integer,true|string> rows we need to retrieve
|
||||
local need = 0 -- keep track of how many unique rows we need
|
||||
for _, row in pairs(rows) do
|
||||
if not lines[row] then
|
||||
need = need + 1
|
||||
end
|
||||
lines[row] = true
|
||||
end
|
||||
|
||||
local found = 0
|
||||
local lnum = 0
|
||||
|
||||
for line in string.gmatch(data, '([^\n]*)\n?') do
|
||||
if lines[lnum] == true then
|
||||
lines[lnum] = line
|
||||
found = found + 1
|
||||
if found == need then
|
||||
break
|
||||
end
|
||||
end
|
||||
lnum = lnum + 1
|
||||
end
|
||||
|
||||
-- change any lines we didn't find to the empty string
|
||||
for i, line in pairs(lines) do
|
||||
if line == true then
|
||||
lines[i] = ''
|
||||
end
|
||||
end
|
||||
return lines --[[@as table<integer,string>]]
|
||||
end
|
||||
|
||||
--- Gets the zero-indexed line from the given buffer.
|
||||
--- Works on unloaded buffers by reading the file using libuv to bypass buf reading events.
|
||||
--- Falls back to loading the buffer and nvim_buf_get_lines for buffers with non-file URI.
|
||||
---
|
||||
---@param bufnr integer
|
||||
---@param row integer zero-indexed line number
|
||||
---@return string the line at row in filename
|
||||
local function get_line(bufnr, row)
|
||||
return get_lines(bufnr, { row })[row]
|
||||
end
|
||||
|
||||
--- Converts |vim.Pos| to `lsp.Position`.
|
||||
@@ -143,6 +230,12 @@ function M.to_lsp(pos, position_encoding)
|
||||
-- we can ignore the difference between byte and character.
|
||||
if col > 0 then
|
||||
col = vim.str_utfindex(get_line(buf, row), position_encoding, col, false)
|
||||
elseif col == 0 and row == api.nvim_buf_line_count(buf) and not vim.bo[buf].endofline then
|
||||
-- Some LSP servers reject ranges that end at the virtual EOF position
|
||||
-- (i.e., `[line_count, 0]`) when the buffer has no trailing newline.
|
||||
-- Normalize such positions to the end of the last real line instead.
|
||||
row = row - 1
|
||||
col = vim.str_utfindex(get_line(buf, row), position_encoding)
|
||||
end
|
||||
|
||||
---@type lsp.Position
|
||||
@@ -178,7 +271,7 @@ function M.lsp(buf, pos, position_encoding)
|
||||
if col > 0 then
|
||||
-- `strict_indexing` is disabled, because LSP responses are asynchronous,
|
||||
-- and the buffer content may have changed, causing out-of-bounds errors.
|
||||
col = vim.str_byteindex(get_line(buf, row), position_encoding, col, false)
|
||||
col = vim.str_byteindex(get_line(buf, row) or '', position_encoding, col, false)
|
||||
end
|
||||
|
||||
return M.new(buf, row, col)
|
||||
@@ -202,10 +295,21 @@ end
|
||||
---@param pos vim.Pos
|
||||
---@return integer, integer
|
||||
function M.to_extmark(pos)
|
||||
local line_count = api.nvim_buf_line_count(pos.buf)
|
||||
|
||||
local row, col = pos[1], pos[2]
|
||||
if col == 0 and row == line_count then
|
||||
-- Consider a buffer like this:
|
||||
-- ```
|
||||
-- 0123456
|
||||
-- abcdefg
|
||||
-- ```
|
||||
--
|
||||
-- Two ways to describe the range of the first line, i.e. '0123456':
|
||||
-- 1. `{ start_row = 0, start_col = 0, end_row = 0, end_col = 7 }`
|
||||
-- 2. `{ start_row = 0, start_col = 0, end_row = 1, end_col = 0 }`
|
||||
--
|
||||
-- Both of the above methods satisfy the "end-exclusive" definition,
|
||||
-- but `nvim_buf_set_extmark()` throws an out-of-bounds error for the second method,
|
||||
-- so we need to convert it to the first method.
|
||||
if col == 0 and row == api.nvim_buf_line_count(pos.buf) then
|
||||
row = row - 1
|
||||
col = #get_line(pos.buf, row)
|
||||
end
|
||||
@@ -252,6 +356,11 @@ function M.offset(buf, offset)
|
||||
return M.new(buf, row, col)
|
||||
end
|
||||
|
||||
-- TODO(ofseed): remove these exported functions by replacing their usages with `vim.pos`.
|
||||
M._get_lines = get_lines
|
||||
|
||||
M._get_line = get_line
|
||||
|
||||
-- Overload `Range.new` to allow calling this module as a function.
|
||||
setmetatable(M, {
|
||||
__call = function(_, ...)
|
||||
|
||||
Reference in New Issue
Block a user