Skip to content

Commit 3a658ab

Browse files
committed
Merge remote-tracking branch 'pr92/master'
2 parents 9069efe + 7553c64 commit 3a658ab

File tree

7 files changed

+223
-24
lines changed

7 files changed

+223
-24
lines changed

lua/nabla.lua

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -308,24 +308,24 @@ function enable_virt(opts)
308308
local virt_lines_above = {}
309309
local virt_lines_below = {}
310310

311-
if mult_virt_ns[buf] then
312-
vim.api.nvim_buf_clear_namespace(buf, mult_virt_ns[buf], 0, -1)
311+
if mult_virt_ns[buf] == nil then
312+
mult_virt_ns[buf] = vim.api.nvim_create_namespace("nabla.nvim")
313313
end
314-
mult_virt_ns[buf] = vim.api.nvim_create_namespace("")
315314

316315
local prev_row = -1
317316
local prev_diff = 0
318317

319318
local next_prev_row
320319
local next_prev_diff
321320

322-
local formula_nodes = utils.get_all_mathzones()
321+
local formula_nodes = utils.get_all_mathzones(opts)
323322
local formulas_loc = {}
324323
for _, node in ipairs(formula_nodes) do
325324
local srow, scol, erow, ecol = ts_utils.get_node_range(node)
326-
table.insert(formulas_loc, {srow, scol, erow, ecol})
325+
table.insert(formulas_loc, {srow, scol, erow, ecol})
327326
end
328327

328+
local conceal_padding = {}
329329
for _, loc in ipairs(formulas_loc) do
330330
local srow, scol, erow, ecol = unpack(loc)
331331
local succ, texts = pcall(vim.api.nvim_buf_get_text, buf, srow, scol, erow, ecol, {})
@@ -364,6 +364,40 @@ function enable_virt(opts)
364364

365365

366366

367+
if not conceal_padding[srow] then
368+
-- account for treesitter capture conceals
369+
conceal_padding[srow] = vim.iter(vim.gsplit(vim.api.nvim_buf_get_lines(0, srow, srow + 1, false)[1], ''))
370+
:fold({}, function(acc, _)
371+
local conceal_cap = vim.iter(vim.treesitter.get_captures_at_pos(0, srow, #acc))
372+
:filter(function(v)
373+
return v.metadata.conceal
374+
end):next()
375+
acc[#acc + 1] = (#acc == 0 and 0 or acc[#acc]) + 1 - (conceal_cap and vim.fn.strutf16len(conceal_cap.metadata.conceal) or 1)
376+
return acc
377+
end)
378+
379+
local marks = vim.iter(vim.api.nvim_buf_get_extmarks(0, -1, { srow, 0 }, { srow, -1 }, { details = true, }))
380+
local last_stat = marks:filter(function(v) return v[4].virt_text and v[4].virt_text_pos == "inline" end)
381+
:fold({0, 0}, function (stat, mark)
382+
if stat[1] >= mark[3] then
383+
return { stat[1], stat[2] + vim.iter(mark[4].virt_text):fold(0, function(len, v)
384+
-- the third term accounts for replacement of a character with virt_text (1) vs addition of string (0)
385+
return len + vim.fn.strutf16len(v[1]) - 1
386+
end) }
387+
end
388+
for i = stat[1] + 1, mark[3] + 1 do
389+
conceal_padding[srow][i] = conceal_padding[srow][i] - stat[2]
390+
end
391+
return {mark[3] + 1,
392+
stat[2] + vim.iter(mark[4].virt_text):fold(0, function (len, v)
393+
return len + vim.fn.strutf16len(v[1]) - 1
394+
end)}
395+
end)
396+
for i = last_stat[1] + 1, #conceal_padding[srow] do
397+
conceal_padding[srow][i] = conceal_padding[srow][i] - last_stat[2]
398+
end
399+
end
400+
367401
local drawing_virt = {}
368402

369403
for j=1,#drawing do
@@ -469,7 +503,7 @@ function enable_virt(opts)
469503
end
470504

471505
local col = #vline
472-
local padding = desired_col - col
506+
local padding = desired_col - col - conceal_padding[srow][desired_col + 1]
473507
if prev_row == srow then
474508
padding = padding - prev_diff
475509
end
@@ -533,6 +567,7 @@ function enable_virt(opts)
533567
end
534568

535569
-- @place_drawings_above_lines
570+
local cleared_extmarks = {}
536571
for _, conceal in ipairs(inline_virt) do
537572
local chunks, row, p1, p2 = unpack(conceal)
538573

@@ -543,6 +578,11 @@ function enable_virt(opts)
543578
end
544579

545580
if p1+j <= p2 then
581+
if cleared_extmarks[row] == nil then
582+
vim.api.nvim_buf_clear_namespace(buf, mult_virt_ns[buf], row, row + 1)
583+
cleared_extmarks[row] = true
584+
end
585+
546586
vim.api.nvim_buf_set_extmark(buf, mult_virt_ns[buf], row, p1+j-1, {
547587
-- virt_text = {{ c, hl_group }},
548588
end_row = row,
@@ -563,6 +603,11 @@ function enable_virt(opts)
563603
end
564604

565605
if #virt_lines_reversed > 0 then
606+
if cleared_extmarks[row] == nil then
607+
vim.api.nvim_buf_clear_namespace(buf, mult_virt_ns[buf], row, row + 1)
608+
cleared_extmarks[row] = true
609+
end
610+
566611
vim.api.nvim_buf_set_extmark(buf, mult_virt_ns[buf], row, 0, {
567612
virt_lines = virt_lines_reversed,
568613
virt_lines_above = true,
@@ -572,6 +617,11 @@ function enable_virt(opts)
572617

573618
for row, virt_lines in pairs(virt_lines_below) do
574619
if #virt_lines > 0 then
620+
if cleared_extmarks[row] == nil then
621+
vim.api.nvim_buf_clear_namespace(buf, mult_virt_ns[buf], row, row + 1)
622+
cleared_extmarks[row] = true
623+
end
624+
575625
vim.api.nvim_buf_set_extmark(buf, mult_virt_ns[buf], row, 0, {
576626
virt_lines = virt_lines,
577627
})
@@ -586,10 +636,10 @@ function enable_virt(opts)
586636

587637
local win = vim.api.nvim_get_current_win()
588638
saved_wrapsettings[win] = vim.wo[win].wrap
589-
vim.wo[win].wrap = false
639+
-- vim.wo[win].wrap = false
590640

591641
if opts and opts.autogen then
592-
autogen_autocmd[buf] = vim.api.nvim_create_autocmd({"InsertLeave"}, {
642+
autogen_autocmd[buf] = vim.api.nvim_create_autocmd({"InsertLeave", "TextChanged"}, {
593643
buffer = buf,
594644
desc = "nabla.nvim: Regenerates virt_lines automatically when the user exists insert mode",
595645
callback = function()

lua/nabla/utils.lua

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ local MATH_NODES = {
1616
inline_formula = true,
1717
}
1818

19+
RENDER_CACHE = {}
20+
1921
utils.in_mathzone = function()
2022
local function get_node_at_cursor()
2123
local cursor = vim.api.nvim_win_get_cursor(0)
@@ -74,24 +76,71 @@ utils.in_mathzone = function()
7476
end
7577
end
7678

77-
utils.get_all_mathzones = function()
79+
utils.get_all_mathzones = function(opts)
7880
local buf = vim.api.nvim_get_current_buf()
79-
local ok, parser = pcall(ts.get_parser, buf, "latex")
81+
local ok, parser = pcall(ts.get_parser, buf, vim.bo.filetype~="markdown" and "latex" or "markdown")
8082
if not ok or not parser then
8183
vim.api.nvim_echo({{"Latex parser not found. Please install with nvim-treesitter using \":TSInstall latex\".", "ErrorMsg"}}, true, {})
8284

8385
return {}
8486
end
8587

86-
local root_tree = parser:parse()[1]
88+
local top, bottom = vim.fn.line('w0') - 1, vim.fn.line('w$')
89+
local parse_span
90+
local cache = RENDER_CACHE[buf]
91+
if not cache then
92+
RENDER_CACHE[buf] = { top = top, bottom = bottom, changedtick = vim.api.nvim_buf_get_changedtick(buf) }
93+
parse_span = { top = top, bottom = bottom }
94+
else
95+
if cache.changedtick ~= vim.api.nvim_buf_get_changedtick(buf) then
96+
local line = vim.fn.line('.') - 1
97+
parse_span = vim.fn.mode() == 'i' and { top = line, bottom = line + 1 } or { top = top, bottom = bottom }
98+
cache.changedtick = vim.api.nvim_buf_get_changedtick(buf)
99+
else
100+
parse_span = {
101+
top = top < cache.top and top or (top >= cache.bottom and top or cache.bottom),
102+
bottom = bottom > cache.bottom and bottom or (bottom >= cache.top and cache.top or bottom),
103+
}
104+
end
105+
cache.top = math.min(cache.top, parse_span.top)
106+
cache.bottom = math.max(cache.bottom, parse_span.bottom)
107+
end
108+
109+
if not parse_span or parse_span.top >= parse_span.bottom then
110+
return {}
111+
end
112+
113+
local root_tree = parser:parse(opts.render_visible and { parse_span.top, parse_span.bottom } or true)[1]
114+
87115
local root = root_tree and root_tree:root()
88116

89117
if not root then
90118
return {}
91119
end
92120

93121
local out = {}
94-
utils.get_mathzones_in_node(root, out)
122+
if vim.bo.filetype == "markdown" then
123+
local injections = {}
124+
125+
parser:for_each_tree(function(_, parent_ltree)
126+
for _, child in pairs(parent_ltree:children()) do
127+
for _, tree in pairs(child:trees()) do
128+
local r = tree:root()
129+
local sr, sc, er, ec = ts.get_node_range(r)
130+
local key = string.format("%s,%s,%s,%s", sr, sc, er, ec)
131+
if not injections[key] then
132+
local lang = child:lang()
133+
if lang == "latex" then
134+
injections[key] = true
135+
table.insert(out, r)
136+
end
137+
end
138+
end
139+
end
140+
end)
141+
else
142+
utils.get_mathzones_in_node(root, out)
143+
end
95144

96145
return out
97146
end

src/treesitter/detect_for_virt_lines.lua.t

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
##../nabla
22
@utils_functions+=
3-
utils.get_all_mathzones = function()
3+
utils.get_all_mathzones = function(opts)
44
@get_tree_root_ts
55
@go_downwards_and_search_for_math_nodes
66
return out
@@ -11,13 +11,13 @@ vim.api.nvim_echo({{"Latex parser not found. Please install with nvim-treesitter
1111

1212
@get_tree_root_ts+=
1313
local buf = vim.api.nvim_get_current_buf()
14-
local ok, parser = pcall(ts.get_parser, buf, "latex")
14+
local ok, parser = pcall(ts.get_parser, buf, vim.bo.filetype~="markdown" and "latex" or "markdown")
1515
if not ok or not parser then
1616
@warn_if_latex_is_not_installed
1717
return {}
1818
end
1919

20-
local root_tree = parser:parse()[1]
20+
@parse_latex_with_markdown_fix
2121
local root = root_tree and root_tree:root()
2222

2323
if not root then
@@ -52,14 +52,14 @@ end
5252

5353
@go_downwards_and_search_for_math_nodes+=
5454
local out = {}
55-
utils.get_mathzones_in_node(root, out)
55+
@get_mathzones_with_markdown_fix
5656

5757
@detect_all_formulas+=
58-
local formula_nodes = utils.get_all_mathzones()
58+
local formula_nodes = utils.get_all_mathzones(opts)
5959
local formulas_loc = {}
6060
for _, node in ipairs(formula_nodes) do
6161
local srow, scol, erow, ecol = ts_utils.get_node_range(node)
62-
table.insert(formulas_loc, {srow, scol, erow, ecol})
62+
table.insert(formulas_loc, {srow, scol, erow, ecol})
6363
end
6464

6565
@detect_formulas_in_line+=

src/treesitter/detect_mathzone.lua.t

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ local MATH_NODES = {
1717
inline_formula = true,
1818
}
1919

20+
RENDER_CACHE = {}
21+
2022
utils.in_mathzone = function()
2123
local function get_node_at_cursor()
2224
local cursor = vim.api.nvim_win_get_cursor(0)

src/viewmode/autogen.lua.t

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ local autogen_autocmd = {}
44
local autogen_flag = false
55

66
@enable_autogen+=
7-
autogen_autocmd[buf] = vim.api.nvim_create_autocmd({"InsertLeave"}, {
7+
autogen_autocmd[buf] = vim.api.nvim_create_autocmd({"InsertLeave", "TextChanged"}, {
88
buffer = buf,
99
desc = "nabla.nvim: Regenerates virt_lines automatically when the user exists insert mode",
1010
callback = function()

src/viewmode/fix_markdown.lua.t

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
##../nabla
2+
@fix_rendering_markdown+=
3+
if not conceal_padding[srow] then
4+
-- account for treesitter capture conceals
5+
conceal_padding[srow] = vim.iter(vim.gsplit(vim.api.nvim_buf_get_lines(0, srow, srow + 1, false)[1], ''))
6+
:fold({}, function(acc, _)
7+
local conceal_cap = vim.iter(vim.treesitter.get_captures_at_pos(0, srow, #acc))
8+
:filter(function(v)
9+
return v.metadata.conceal
10+
end):next()
11+
acc[#acc + 1] = (#acc == 0 and 0 or acc[#acc]) + 1 - (conceal_cap and vim.fn.strutf16len(conceal_cap.metadata.conceal) or 1)
12+
return acc
13+
end)
14+
15+
local marks = vim.iter(vim.api.nvim_buf_get_extmarks(0, -1, { srow, 0 }, { srow, -1 }, { details = true, }))
16+
local last_stat = marks:filter(function(v) return v[4].virt_text and v[4].virt_text_pos == "inline" end)
17+
:fold({0, 0}, function (stat, mark)
18+
if stat[1] >= mark[3] then
19+
return { stat[1], stat[2] + vim.iter(mark[4].virt_text):fold(0, function(len, v)
20+
-- the third term accounts for replacement of a character with virt_text (1) vs addition of string (0)
21+
return len + vim.fn.strutf16len(v[1]) - 1
22+
end) }
23+
end
24+
for i = stat[1] + 1, mark[3] + 1 do
25+
conceal_padding[srow][i] = conceal_padding[srow][i] - stat[2]
26+
end
27+
return {mark[3] + 1,
28+
stat[2] + vim.iter(mark[4].virt_text):fold(0, function (len, v)
29+
return len + vim.fn.strutf16len(v[1]) - 1
30+
end)}
31+
end)
32+
for i = last_stat[1] + 1, #conceal_padding[srow] do
33+
conceal_padding[srow][i] = conceal_padding[srow][i] - last_stat[2]
34+
end
35+
end
36+
37+
@cleared_extmarks+=
38+
if cleared_extmarks[row] == nil then
39+
vim.api.nvim_buf_clear_namespace(buf, mult_virt_ns[buf], row, row + 1)
40+
cleared_extmarks[row] = true
41+
end
42+
43+
@parse_latex_with_markdown_fix+=
44+
local top, bottom = vim.fn.line('w0') - 1, vim.fn.line('w$')
45+
local parse_span
46+
local cache = RENDER_CACHE[buf]
47+
if not cache then
48+
RENDER_CACHE[buf] = { top = top, bottom = bottom, changedtick = vim.api.nvim_buf_get_changedtick(buf) }
49+
parse_span = { top = top, bottom = bottom }
50+
else
51+
if cache.changedtick ~= vim.api.nvim_buf_get_changedtick(buf) then
52+
local line = vim.fn.line('.') - 1
53+
parse_span = vim.fn.mode() == 'i' and { top = line, bottom = line + 1 } or { top = top, bottom = bottom }
54+
cache.changedtick = vim.api.nvim_buf_get_changedtick(buf)
55+
else
56+
parse_span = {
57+
top = top < cache.top and top or (top >= cache.bottom and top or cache.bottom),
58+
bottom = bottom > cache.bottom and bottom or (bottom >= cache.top and cache.top or bottom),
59+
}
60+
end
61+
cache.top = math.min(cache.top, parse_span.top)
62+
cache.bottom = math.max(cache.bottom, parse_span.bottom)
63+
end
64+
65+
if not parse_span or parse_span.top >= parse_span.bottom then
66+
return {}
67+
end
68+
69+
local root_tree = parser:parse(opts.render_visible and { parse_span.top, parse_span.bottom } or true)[1]
70+
71+
@get_mathzones_with_markdown_fix+=
72+
if vim.bo.filetype == "markdown" then
73+
local injections = {}
74+
75+
parser:for_each_tree(function(_, parent_ltree)
76+
for _, child in pairs(parent_ltree:children()) do
77+
for _, tree in pairs(child:trees()) do
78+
local r = tree:root()
79+
local sr, sc, er, ec = ts.get_node_range(r)
80+
local key = string.format("%s,%s,%s,%s", sr, sc, er, ec)
81+
if not injections[key] then
82+
local lang = child:lang()
83+
if lang == "latex" then
84+
injections[key] = true
85+
table.insert(out, r)
86+
end
87+
end
88+
end
89+
end
90+
end)
91+
else
92+
utils.get_mathzones_in_node(root, out)
93+
end

0 commit comments

Comments
 (0)