aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--init.lua66
-rw-r--r--util.lua73
2 files changed, 97 insertions, 42 deletions
diff --git a/init.lua b/init.lua
index 2c91114..b8986a4 100644
--- a/init.lua
+++ b/init.lua
@@ -140,32 +140,6 @@ local function get_selection(win)
return {line = win.selection.line, col = win.selection.col}
end
--- get the 0-based byte offset from a selection
--- ATTENTION: this function modifies the primary selection so it is not
--- safe to call it for example during WIN_HIGHLIGHT events
-local function vis_sel_to_pos(win, selection)
- local old_selection = get_selection(win)
- -- move primary selection
- win.selection:to(selection.line, selection.col)
- local pos = win.selection.pos
- -- restore old primary selection
- win.selection:to(old_selection.line, old_selection.col)
- return pos
-end
-
--- get the line and column from a 0-based byte offset
--- ATTENTION: this function modifies the primary selection so it is not
--- safe to call it for example during WIN_HIGHLIGHT events
-local function vis_pos_to_sel(win, pos)
- local old_selection = get_selection(win)
- -- move primary selection
- win.selection.pos = pos
- local sel = get_selection(win)
- -- restore old primary selection
- win.selection:to(old_selection.line, old_selection.col)
- return sel
-end
-
-- convert lsp_position to vis_selection
local function lsp_pos_to_vis_sel(pos)
return {line = pos.line + 1, col = pos.character + 1}
@@ -203,25 +177,34 @@ local function vis_get_doc_pos(win)
}
end
--- convert a lsp_range to a vis_range
--- ATTENTION: this function modifies the primary selection so it is not
--- safe to call it for example during WIN_HIGHLIGHT events
-local function lsp_range_to_vis_range(win, lsp_range)
+--- Convert a lsp_range to a vis_range
+-- @param file the file in which the range lies
+-- @param lsp_range the LSP range that should be converted
+-- @return the according vis range
+local function lsp_range_to_vis_range(file, lsp_range)
local start = lsp_pos_to_vis_sel(lsp_range.start)
- local start_pos = vis_sel_to_pos(win, start)
-
local finish = lsp_pos_to_vis_sel(lsp_range['end'])
- local finish_pos = vis_sel_to_pos(win, finish)
+
+ local positions = util.vis_sorted_selections_to_pos(file, {start, finish})
+ local start_pos = positions[1]
+ local finish_pos = positions[2]
return {start = start_pos, finish = finish_pos}
end
--- return true if p1 is before p2
+--- Check if a lsp_position lies before another.
+-- @param p1 first lsp_position to compare
+-- @param p2 second lsp_position to compare
+-- @return true if p1 lies before p2
local function lsp_pos_before(p1, p2)
return p1.line < p2.line or (p1.line == p2.line and p1.character < p2.character)
end
--- return true if r1 starts before r2
+--- Check if a lsp_range starts before another.
+-- The ranges may overlap since only their start positions are compared.
+-- @param r1 first lsp_range to compare
+-- @param r2 second lsp_range to compare
+-- @return true if r1 is starts before r2
local function lsp_range_starts_before(r1, r2)
return lsp_pos_before(r1.start, r2.start)
end
@@ -420,7 +403,7 @@ end
local function vis_apply_textEdit(win, file, textEdit)
assert(win.file == file)
- local range = lsp_range_to_vis_range(win, textEdit.range)
+ local range = lsp_range_to_vis_range(file, textEdit.range)
file:delete(range)
file:insert(range.start, textEdit.newText)
@@ -437,7 +420,7 @@ local function vis_apply_textEdits(win, file, textEdits)
local edits = {}
for _, textEdit in ipairs(textEdits) do
- local range = lsp_range_to_vis_range(win, textEdit.range)
+ local range = lsp_range_to_vis_range(file, textEdit.range)
table.insert(edits, {
mark = file:mark_set(range.start),
len = range.finish - range.start,
@@ -865,7 +848,7 @@ local function lspc_handle_hover_method_response(win, result, old_pos)
return
end
- local sel = vis_pos_to_sel(win, old_pos)
+ local sel = util.vis_pos_to_sel(win, old_pos)
local hover_header =
'--- hover: ' .. (win.file.path or '') .. ': ' .. sel.line .. ', ' .. sel.col .. ' ---\n'
@@ -901,7 +884,7 @@ local function lspc_handle_signature_help_method_response(win, result, call_pos)
local signatures = result.signatures
- local sel = vis_pos_to_sel(win, call_pos)
+ local sel = util.vis_pos_to_sel(win, call_pos)
local help_header = '--- signature help: ' .. (win.file.path or '') .. ': ' .. sel.line .. ', ' ..
sel.col .. ' ---\n'
@@ -1056,9 +1039,8 @@ local function lspc_handle_publish_diagnostics(ls, uri, diagnostics)
if file then
for _, diagnostic in ipairs(diagnostics) do
-- We convert the lsp_range to a vis_range here to do it only once.
- -- And because we can't do it during a WIN_HIGHLIGHT events because
- -- lsp_range_to_vis_range modifies the primary selection
- diagnostic.vis_range = lsp_range_to_vis_range(vis.win, diagnostic.range)
+ -- It's an expensive operation that involves counting all newlines.
+ diagnostic.vis_range = lsp_range_to_vis_range(file.file, diagnostic.range)
-- In some instances the range defined by the diagnostic starts
-- and ends at the same position. Highlight the exact position.
diff --git a/util.lua b/util.lua
index daaa3fd..04855fb 100644
--- a/util.lua
+++ b/util.lua
@@ -206,4 +206,77 @@ function util.find_upwards(globs, start)
return out:sub(1, #out - 1)
end
+-- get the vis_selection from current primary selection
+local function get_selection(win)
+ return {line = win.selection.line, col = win.selection.col}
+end
+
+--- Calculate the 0-based byte offsets from multiple sorted selections
+-- @param file the file to calculate the positions in
+-- @param sorted_selections a table of sorted vis_selections
+-- @return a table of positions
+function util.vis_sorted_selections_to_pos(file, sorted_selections)
+ local positions = {}
+ if file.pos_by_linecol then
+ for _, sel in ipairs(sorted_selections) do
+ table.insert(positions, file:pos_by_linecol(sel.line, sel.col))
+ end
+ return positions
+ end
+
+ local line_count = 0
+ local pos = 0
+ local sel_i = 1
+ local sel = sorted_selections[sel_i]
+ for line in file:lines_iterator() do
+ line_count = line_count + 1
+ while line_count == sel.line do
+ table.insert(positions, pos + (sel.col - 1))
+ sel_i = sel_i + 1
+ -- no more selections to convert
+ if sel_i > #sorted_selections then
+ break
+ end
+ sel = sorted_selections[sel_i]
+ end
+
+ pos = pos + #line + 1
+ end
+ return positions
+end
+
+local function vis_pos_before(p1, p2)
+ return p1.line < p2.line or (p1.line == p2.line and p1.col < p2.col)
+end
+
+--- Calculate the 0-based byte offsets from multiple selections
+-- @param file the file to calculate the positions in
+-- @param selections a table of selections
+-- @return a table of positions
+function util.vis_selections_to_pos(file, selections)
+ table.sort(selections, vis_pos_before)
+ return util.vis_sorted_selections_to_pos(file, selections)
+end
+
+--- Get the line and column from a 0-based byte offset
+-- ATTENTION: the fallback version of this function modifies the primary
+-- selection so it is not safe to call it for example during WIN_HIGHLIGHT events
+-- @param pos the 0-based byte offset into the file
+-- @return the 1-based line number
+-- @return the 1-based column
+function util.vis_pos_to_sel(win, pos)
+ if win.file.linecol_by_pos then
+ local lineno, col = win.file:linecol_by_pos(pos)
+ return {line = lineno, col = col}
+ end
+
+ local old_selection = get_selection(win)
+ -- move primary selection
+ win.selection.pos = pos
+ local sel = get_selection(win)
+ -- restore old primary selection
+ win.selection:to(old_selection.line, old_selection.col)
+ return sel
+end
+
return util