fix(fold): don't include whitespace end regions

Some languages that are difficult to parse via Treesitter may
incorrectly include whitespace lines at the end of regions. This can
makes the calculated folds sub-optimal.

To recitfy, use a custom directive (trim), to calculate the range with
the trailing whitespace lines removed. Note this only works if the
region end column is 0.

Also added folds for Make.
This commit is contained in:
Lewis Russell 2022-04-21 10:48:47 +01:00 committed by Lewis Russell
parent 2bc82e814b
commit 2eaf188269
5 changed files with 59 additions and 9 deletions

View file

@ -39,8 +39,13 @@ local folds_levels = tsutils.memoize_by_buf_tick(function(bufnr)
local min_fold_lines = api.nvim_win_get_option(0, "foldminlines")
for _, node in ipairs(matches) do
local start, _, stop, stop_col = node.node:range()
for _, match in ipairs(matches) do
local start, stop, stop_col
if match.metadata and match.metadata.range then
start, _, stop, stop_col = unpack(match.metadata.range)
else
start, _, stop, stop_col = match.node:range()
end
if stop_col == 0 then
stop = stop - 1

View file

@ -209,7 +209,7 @@ function M.iter_prepared_matches(query, qnode, bufnr, start_row, end_row)
local matches = query:iter_matches(qnode, bufnr, start_row, end_row)
local function iterator()
local pattern, match = matches()
local pattern, match, metadata = matches()
if pattern ~= nil then
local prepared_match = {}
@ -219,6 +219,8 @@ function M.iter_prepared_matches(query, qnode, bufnr, start_row, end_row)
if name ~= nil then
local path = split(name .. ".node")
insert_to_path(prepared_match, path, node)
local metadata_path = split(name .. ".metadata")
insert_to_path(prepared_match, metadata_path, metadata[id])
end
end

View file

@ -153,3 +153,36 @@ query.add_directive("exclude_children!", function(match, _pattern, _bufnr, pred,
end
metadata.content = ranges
end)
-- Trim blank lines from end of the region
-- Arguments are the captures to trim.
query.add_directive("trim!", function(match, _, bufnr, pred, metadata)
for _, id in ipairs { select(2, unpack(pred)) } do
local node = match[id]
local start_row, start_col, end_row, end_col = node:range()
-- Don't trim if region ends in middle of a line
if end_col ~= 0 then
return
end
while true do
-- As we only care when end_col == 0, always inspect one line above end_row.
local end_line = vim.api.nvim_buf_get_lines(bufnr, end_row - 1, end_row, true)[1]
if end_line ~= "" then
break
end
end_row = end_row - 1
end
-- If this produces an invalid range, we just skip it.
if start_row < end_row or (start_row == end_row and start_col <= end_col) then
if not metadata[id] then
metadata[id] = {}
end
metadata[id].range = { start_row, start_col, end_row, end_col }
end
end
end)

7
queries/make/folds.scm Normal file
View file

@ -0,0 +1,7 @@
(
[
(conditional)
(rule)
] @fold
(#trim! @fold)
)

View file

@ -1,6 +1,9 @@
[
(fenced_code_block)
(indented_code_block)
(list)
(section)
] @fold
(
[
(fenced_code_block)
(indented_code_block)
(list)
(section)
] @fold
(#trim! @fold)
)