@@ -90,6 +90,42 @@ local function split_lines(value)
90
90
return split(value, '\n', true)
91
91
end
92
92
93
+
--- Convert byte index to `encoding` index.
94
+
--- Convenience wrapper around vim.str_utfindex
95
+
---@param line string line to be indexed
96
+
---@param index number byte index (utf-8), or `nil` for length
97
+
---@param encoding string utf-8|utf-16|utf-32|nil defaults to utf-16
98
+
---@return number `encoding` index of `index` in `line`
99
+
function M._str_utfindex_enc(line, index, encoding)
100
+
if encoding ~= 'utf-8' then
101
+
local col32, col16 = vim.str_utfindex(line, index)
102
+
if encoding == 'utf-32' then
103
+
return col32
104
+
else
105
+
return col16
106
+
end
107
+
else
108
+
return index
109
+
end
110
+
end
111
+
112
+
--- Convert UTF index to `encoding` index.
113
+
--- Convenience wrapper around vim.str_byteindex
114
+
---Alternative to vim.str_byteindex that takes an encoding.
115
+
---@param line string line to be indexed
116
+
---@param index number UTF index
117
+
---@param encoding string utf-8|utf-16|utf-32|nil defaults to utf-16
118
+
---@return number byte (utf-8) index of `encoding` index `index` in `line`
119
+
function M._str_byteindex_enc(line, index, encoding)
120
+
if encoding ~= 'utf-8' then
121
+
return vim.str_byteindex(line, index, not encoding or encoding ~= 'utf-32')
122
+
else
123
+
return index
124
+
end
125
+
end
126
+
127
+
local _str_utfindex_enc = M._str_utfindex_enc
128
+
local _str_byteindex_enc = M._str_byteindex_enc
93
129
--- Replaces text in a range with new text.
94
130
---
95
131
--- CAUTION: Changes in-place!
237
273
---@private
238
274
--- Position is a https://microsoft.github.io/language-server-protocol/specifications/specification-current/#position
239
275
--- Returns a zero-indexed column, since set_lines() does the conversion to
276
+
---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to utf-16
240
277
--- 1-indexed
241
278
local function get_line_byte_from_position(bufnr, position, offset_encoding)
242
279
-- LSP's line and characters are 0-indexed
@@ -247,13 +284,7 @@ local function get_line_byte_from_position(bufnr, position, offset_encoding)
247
284
if col > 0 then
248
285
local line = get_line(bufnr, position.line)
249
286
local ok, result
250
-
251
-
if offset_encoding == "utf-16" or not offset_encoding then
252
-
ok, result = pcall(vim.str_byteindex, line, col, true)
253
-
elseif offset_encoding == "utf-32" then
254
-
ok, result = pcall(vim.str_byteindex, line, col, false)
255
-
end
256
-
287
+
ok, result = pcall(_str_byteindex_enc, line, col, offset_encoding)
257
288
if ok then
258
289
return result
259
290
end
@@ -325,12 +356,15 @@ end
325
356
--- Applies a list of text edits to a buffer.
326
357
---@param text_edits table list of `TextEdit` objects
327
358
---@param bufnr number Buffer id
359
+
---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to encoding of first client of `bufnr`
328
360
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textEdit
329
-
function M.apply_text_edits(text_edits, bufnr)
361
+
function M.apply_text_edits(text_edits, bufnr, offset_encoding)
330
362
validate {
331
363
text_edits = { text_edits, 't', false };
332
364
bufnr = { bufnr, 'number', false };
365
+
offset_encoding = { offset_encoding, 'string', true };
333
366
}
367
+
offset_encoding = offset_encoding or M._get_offset_encoding(bufnr)
334
368
if not next(text_edits) then return end
335
369
if not api.nvim_buf_is_loaded(bufnr) then
336
370
vim.fn.bufload(bufnr)
@@ -367,8 +401,7 @@ function M.apply_text_edits(text_edits, bufnr)
367
401
-- Some LSP servers may return +1 range of the buffer content but nvim_buf_set_text can't accept it so we should fix it here.
368
402
local has_eol_text_edit = false
369
403
local max = vim.api.nvim_buf_line_count(bufnr)
370
-
-- TODO handle offset_encoding
371
-
local _, len = vim.str_utfindex(vim.api.nvim_buf_get_lines(bufnr, -2, -1, false)[1] or '')
404
+
local len = _str_utfindex_enc(vim.api.nvim_buf_get_lines(bufnr, -2, -1, false)[1] or '', nil, offset_encoding)
372
405
text_edits = vim.tbl_map(function(text_edit)
373
406
if max <= text_edit.range.start.line then
374
407
text_edit.range.start.line = max - 1
@@ -1432,11 +1465,11 @@ do --[[ References ]]
1432
1465
---
1433
1466
---@param bufnr number Buffer id
1434
1467
---@param references table List of `DocumentHighlight` objects to highlight
1435
-
---@param offset_encoding string One of "utf-8", "utf-16", "utf-32", or nil. Defaults to utf-16
1468
+
---@param offset_encoding string One of "utf-8", "utf-16", "utf-32", or nil. Defaults to `offset_encoding` of first client of `bufnr`
1436
1469
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#documentHighlight
1437
1470
function M.buf_highlight_references(bufnr, references, offset_encoding)
1438
1471
validate { bufnr = {bufnr, 'n', true} }
1439
-
offset_encoding = offset_encoding or 'utf-16'
1472
+
offset_encoding = offset_encoding or M._get_offset_encoding(bufnr)
1440
1473
for _, reference in ipairs(references) do
1441
1474
local start_line, start_char = reference["range"]["start"]["line"], reference["range"]["start"]["character"]
1442
1475
local end_line, end_char = reference["range"]["end"]["line"], reference["range"]["end"]["character"]
@@ -1649,43 +1682,78 @@ function M.try_trim_markdown_code_blocks(lines)
1649
1682
return 'markdown'
1650
1683
end
1651
1684
1652
-
local str_utfindex = vim.str_utfindex
1653
1685
---@private
1654
-
local function make_position_param()
1655
-
local row, col = unpack(api.nvim_win_get_cursor(0))
1686
+
---@param window (optional, number): window handle or 0 for current, defaults to current
1687
+
---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window`
1688
+
local function make_position_param(window, offset_encoding)
1689
+
window = window or 0
1690
+
local buf = vim.api.nvim_win_get_buf(window)
1691
+
local row, col = unpack(api.nvim_win_get_cursor(window))
1692
+
offset_encoding = offset_encoding or M._get_offset_encoding(buf)
1656
1693
row = row - 1
1657
-
local line = api.nvim_buf_get_lines(0, row, row+1, true)[1]
1694
+
local line = api.nvim_buf_get_lines(buf, row, row+1, true)[1]
1658
1695
if not line then
1659
1696
return { line = 0; character = 0; }
1660
1697
end
1661
-
-- TODO handle offset_encoding
1662
-
local _
1663
-
_, col = str_utfindex(line, col)
1698
+
1699
+
col = _str_utfindex_enc(line, col, offset_encoding)
1700
+
1664
1701
return { line = row; character = col; }
1665
1702
end
1666
1703
1667
1704
--- Creates a `TextDocumentPositionParams` object for the current buffer and cursor position.
1668
1705
---
1706
+
---@param window (optional, number): window handle or 0 for current, defaults to current
1707
+
---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window`
1669
1708
---@returns `TextDocumentPositionParams` object
1670
1709
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams
1671
-
function M.make_position_params()
1710
+
function M.make_position_params(window, offset_encoding)
1711
+
window = window or 0
1712
+
local buf = vim.api.nvim_win_get_buf(window)
1713
+
offset_encoding = offset_encoding or M._get_offset_encoding(buf)
1672
1714
return {
1673
-
textDocument = M.make_text_document_params();
1674
-
position = make_position_param()
1715
+
textDocument = M.make_text_document_params(buf);
1716
+
position = make_position_param(window, offset_encoding)
1717
+
}
1718
+
end
1719
+
1720
+
--- Utility function for getting the encoding of the first LSP client on the given buffer.
1721
+
---@param bufnr (number) buffer handle or 0 for current, defaults to current
1722
+
---@returns (string) encoding first client if there is one, nil otherwise
1723
+
function M._get_offset_encoding(bufnr)
1724
+
validate {
1725
+
bufnr = {bufnr, 'n', true};
1675
1726
}
1727
+
1728
+
local offset_encoding
1729
+
1730
+
for _, client in pairs(vim.lsp.buf_get_clients(bufnr)) do
1731
+
local this_offset_encoding = client.offset_encoding or "utf-16"
1732
+
if not offset_encoding then
1733
+
offset_encoding = this_offset_encoding
1734
+
elseif offset_encoding ~= this_offset_encoding then
1735
+
vim.notify("warning: multiple different client offset_encodings detected for buffer, this is not supported yet", vim.log.levels.WARN)
1736
+
end
1737
+
end
1738
+
1739
+
return offset_encoding
1676
1740
end
1677
1741
1678
1742
--- Using the current position in the current buffer, creates an object that
1679
1743
--- can be used as a building block for several LSP requests, such as
1680
1744
--- `textDocument/codeAction`, `textDocument/colorPresentation`,
1681
1745
--- `textDocument/rangeFormatting`.
1682
1746
---
1747
+
---@param window (optional, number): window handle or 0 for current, defaults to current
1748
+
---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window`
1683
1749
---@returns { textDocument = { uri = `current_file_uri` }, range = { start =
1684
1750
---`current_position`, end = `current_position` } }
1685
-
function M.make_range_params()
1686
-
local position = make_position_param()
1751
+
function M.make_range_params(window, offset_encoding)
1752
+
local buf = vim.api.nvim_win_get_buf(window)
1753
+
offset_encoding = offset_encoding or M._get_offset_encoding(buf)
1754
+
local position = make_position_param(window, offset_encoding)
1687
1755
return {
1688
-
textDocument = M.make_text_document_params(),
1756
+
textDocument = M.make_text_document_params(buf),
1689
1757
range = { start = position; ["end"] = position; }
1690
1758
}
1691
1759
end
@@ -1697,27 +1765,29 @@ end
1697
1765
---Defaults to the start of the last visual selection.
1698
1766
---@param end_pos ({number, number}, optional) mark-indexed position.
1699
1767
---Defaults to the end of the last visual selection.
1768
+
---@param bufnr (optional, number): buffer handle or 0 for current, defaults to current
1769
+
---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of `bufnr`
1700
1770
---@returns { textDocument = { uri = `current_file_uri` }, range = { start =
1701
1771
---`start_position`, end = `end_position` } }
1702
-
function M.make_given_range_params(start_pos, end_pos)
1772
+
function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding)
1703
1773
validate {
1704
1774
start_pos = {start_pos, 't', true};
1705
1775
end_pos = {end_pos, 't', true};
1776
+
offset_encoding = {offset_encoding, 's', true};
1706
1777
}
1707
-
local A = list_extend({}, start_pos or api.nvim_buf_get_mark(0, '<'))
1708
-
local B = list_extend({}, end_pos or api.nvim_buf_get_mark(0, '>'))
1778
+
bufnr = bufnr or 0
1779
+
offset_encoding = offset_encoding or M._get_offset_encoding(bufnr)
1780
+
local A = list_extend({}, start_pos or api.nvim_buf_get_mark(bufnr, '<'))
1781
+
local B = list_extend({}, end_pos or api.nvim_buf_get_mark(bufnr, '>'))
1709
1782
-- convert to 0-index
1710
1783
A[1] = A[1] - 1
1711
1784
B[1] = B[1] - 1
1712
-
-- account for encoding.
1713
-
-- TODO handle offset_encoding
1785
+
-- account for offset_encoding.
1714
1786
if A[2] > 0 then
1715
-
local _, char = M.character_offset(0, A[1], A[2])
1716
-
A = {A[1], char}
1787
+
A = {A[1], M.character_offset(bufnr, A[1], A[2], offset_encoding)}
1717
1788
end
1718
1789
if B[2] > 0 then
1719
-
local _, char = M.character_offset(0, B[1], B[2])
1720
-
B = {B[1], char}
1790
+
B = {B[1], M.character_offset(bufnr, B[1], B[2], offset_encoding)}
1721
1791
end
1722
1792
-- we need to offset the end character position otherwise we loose the last
1723
1793
-- character of the selection, as LSP end position is exclusive
@@ -1726,7 +1796,7 @@ function M.make_given_range_params(start_pos, end_pos)
1726
1796
B[2] = B[2] + 1
1727
1797
end
1728
1798
return {
1729
-
textDocument = M.make_text_document_params(),
1799
+
textDocument = M.make_text_document_params(bufnr),
1730
1800
range = {
1731
1801
start = {line = A[1], character = A[2]},
1732
1802
['end'] = {line = B[1], character = B[2]}
@@ -1736,10 +1806,11 @@ end
1736
1806
1737
1807
--- Creates a `TextDocumentIdentifier` object for the current buffer.
1738
1808
---
1809
+
---@param bufnr (optional, number): Buffer handle, defaults to current
1739
1810
---@returns `TextDocumentIdentifier`
1740
1811
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentIdentifier
1741
-
function M.make_text_document_params()
1742
-
return { uri = vim.uri_from_bufnr(0) }
1812
+
function M.make_text_document_params(bufnr)
1813
+
return { uri = vim.uri_from_bufnr(bufnr or 0) }
1743
1814
end
1744
1815
1745
1816
--- Create the workspace params
@@ -1782,14 +1853,16 @@ end
1782
1853
---@param buf buffer id (0 for current)
1783
1854
---@param row 0-indexed line
1784
1855
---@param col 0-indexed byte offset in line
1785
-
---@returns (number, number) UTF-32 and UTF-16 index of the character in line {row} column {col} in buffer {buf}
1786
-
function M.character_offset(bufnr, row, col)
1787
-
local line = get_line(bufnr, row)
1856
+
---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of `buf`
1857
+
---@returns (number, number) `offset_encoding` index of the character in line {row} column {col} in buffer {buf}
1858
+
function M.character_offset(buf, row, col, offset_encoding)
1859
+
local line = get_line(buf, row)
1860
+
offset_encoding = offset_encoding or M._get_offset_encoding(buf)
1788
1861
-- If the col is past the EOL, use the line length.
1789
1862
if col > #line then
1790
-
return str_utfindex(line)
1863
+
return _str_utfindex_enc(line, nil, offset_encoding)
1791
1864
end
1792
-
return str_utfindex(line, col)
1865
+
return _str_utfindex_enc(line, col, offset_encoding)
1793
1866
end
1794
1867
1795
1868
--- Helper function to return nested values in language server settings
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4