Skip to content

Commit b5f90fe

Browse files
committed
refactor(wrap)!: Reworked how wrap support is implamented
Ref: #393
1 parent 1276282 commit b5f90fe

File tree

2 files changed

+137
-90
lines changed

2 files changed

+137
-90
lines changed

lua/markview/renderers/markdown.lua

Lines changed: 44 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2171,13 +2171,21 @@ markdown.section = function (buffer, item)
21712171

21722172
local range = item.range;
21732173

2174-
for l = range.row_start + 1, range.row_end - 1 do
2174+
for l = range.row_start + 1, (range.org_end or range.row_end) - 1 do
21752175
vim.api.nvim_buf_set_extmark(buffer, markdown.ns, l, 0, {
21762176
undo_restore = false, invalidate = true,
21772177

21782178
virt_text_pos = "inline",
21792179
virt_text = {
2180-
{ string.rep(shift_char, math.max(0, shift_width)) }
2180+
{
2181+
string.rep(
2182+
shift_char,
2183+
math.max(
2184+
0,
2185+
shift_width * item.level
2186+
)
2187+
)
2188+
}
21812189
},
21822190

21832191
right_gravity = false,
@@ -3330,21 +3338,13 @@ markdown.__block_quote = function (buffer, item)
33303338
for l = range.row_start, range.row_end - 1, 1 do
33313339
local l_index = (l - range.row_start) + 1;
33323340

3333-
local line = item.text[l_index];
3334-
3335-
require("markview.wrap").wrap_indent(buffer, {
3336-
line = line,
3337-
row = l,
3338-
indent = {
3339-
{ string.rep(" ", item.__nested and 0 or range.col_start) },
3340-
{
3341-
tbl_clamp(config.border, l_index),
3342-
utils.set_hl(tbl_clamp(config.border_hl, l_index) or config.hl)
3343-
},
3344-
{ " " }
3341+
require("markview.wrap").wrap_indent(buffer, { row = l }, {
3342+
{ string.rep(" ", item.__nested and 0 or range.col_start) },
3343+
{
3344+
tbl_clamp(config.border, l_index),
3345+
utils.set_hl(tbl_clamp(config.border_hl, l_index) or config.hl)
33453346
},
3346-
3347-
ns = markdown.ns
3347+
{ " " }
33483348
});
33493349
end
33503350
end
@@ -3439,22 +3439,26 @@ markdown.__list_item = function (buffer, item)
34393439
local pad_width = (math.floor(item.indent / indent_size) + 1) * shift_width;
34403440

34413441
if config.conceal_on_checkboxes == true and checkbox and checkbox.text then
3442-
pad_width = pad_width + vim.fn.strdisplaywidth(checkbox.text);
3442+
pad_width = pad_width + vim.fn.strdisplaywidth(checkbox.text) + 1;
34433443
else
34443444
pad_width = pad_width + vim.fn.strdisplaywidth(item.marker) + 1;
34453445
end
34463446

3447-
for _, l in ipairs(item.candidates) do
3448-
local line = item.text[l + 1];
3447+
---@type integer Number of spaces to add to `odd-spaced` list items.
3448+
local extra = 0;
34493449

3450-
require("markview.wrap").wrap_indent(buffer, {
3451-
line = line,
3452-
row = range.row_start + l,
3453-
indent = {
3454-
{ string.rep(" ", pad_width) }
3455-
},
3450+
if config.conceal_on_checkboxes == true and checkbox and checkbox.text then
3451+
extra = indent_size + 1 - vim.fn.strdisplaywidth(checkbox.text);
3452+
else
3453+
extra = indent_size + 1 - vim.fn.strdisplaywidth(item.marker);
3454+
end
34563455

3457-
ns = markdown.ns
3456+
for _, l in ipairs(item.candidates) do
3457+
require("markview.wrap").wrap_indent(buffer, {
3458+
row = range.row_start + l
3459+
}, {
3460+
{ string.rep(" ", (item.__nested or item.indent % indent_size == 0) and 0 or extra) },
3461+
{ string.rep(" ", range.col_start + pad_width) }
34583462
});
34593463
end
34603464
end
@@ -3475,20 +3479,20 @@ markdown.__section = function (buffer, item)
34753479
end
34763480

34773481
local shift_width = main_config.org_shift_width or main_config.shift_width or 0;
3478-
local shift_char = main_config.org_shift_char or " ";
3479-
3480-
for l = range.row_start, range.row_end, 1 do
3481-
local l_index = (l - range.row_start) + 1;
3482-
local line = item.text[l_index];
3482+
local shift_char = tostring(item.level) or main_config.org_shift_char or " ";
34833483

3484+
for l = range.row_start, (range.org_end or range.row_end) - 1, 1 do
34843485
require("markview.wrap").wrap_indent(buffer, {
3485-
line = line,
3486-
row = l,
3487-
indent = {
3488-
{ string.rep(shift_char, math.max(0, shift_width * (item.level - 1))) }
3489-
},
3490-
3491-
ns = markdown.ns
3486+
row = l
3487+
}, {
3488+
{
3489+
string.rep(" " or shift_char,
3490+
math.max(
3491+
0,
3492+
shift_width * item.level
3493+
)
3494+
)
3495+
}
34923496
});
34933497
end
34943498
end
@@ -3557,6 +3561,8 @@ markdown.post_render = function (buffer, content)
35573561
});
35583562
end
35593563
end
3564+
3565+
require("markview.wrap").render(buffer, markdown.ns);
35603566
end
35613567

35623568

lua/markview/wrap.lua

Lines changed: 93 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -5,91 +5,132 @@
55
---@field ns integer Namespace for `extmarks`.
66
---@field row integer Row of line(**0-indexed**).
77

8+
---@alias markview.wrap.data.value [ string, string? ][]
9+
10+
---@class markview.wrap.data
11+
---
12+
---@field [integer] [ string, string? ][]
13+
814
------------------------------------------------------------------------------
915

10-
--[[ Text wrapping for `markview.nvim`. ]]
16+
--[[
17+
Text soft-wrapping support for `markview.nvim`.
18+
19+
Usage,
20+
21+
```lua
22+
vim.o.wrap = true;
23+
```
24+
25+
>[!NOTE]
26+
> Make sure not to *disable* wrapping for Nodes!
27+
]]
1128
local wrap = {};
1229
local utils = require("markview.utils");
1330

14-
--[[ Gets extmark for `[ lnum, col ]`. ]]
31+
---@type table<integer, markview.wrap.data>
32+
wrap.cache = {};
33+
34+
--[[ Registers `indent` to be used for `range` in `buffer`. ]]
1535
---@param buffer integer
16-
---@param ns integer
17-
---@param lnum integer
18-
---@param col integer
19-
---@return vim.api.keyset.get_extmark_item
20-
wrap.get_extmark = function (buffer, ns, lnum, col)
21-
local extmarks = vim.api.nvim_buf_get_extmarks(buffer, ns, { lnum, col }, { lnum, col + 1 }, {
22-
details = true,
23-
type = "virt_text"
36+
---@param range { row: integer }
37+
---@param indent markview.wrap.data.value
38+
wrap.wrap_indent = function (buffer, range, indent)
39+
---|fS
40+
41+
if not wrap.cache[buffer] then
42+
wrap.cache[buffer] = {};
43+
end
44+
45+
local row = range.row;
46+
47+
if not wrap.cache[buffer][row] then
48+
wrap.cache[buffer][row] = {};
49+
end
50+
51+
---@type integer? Window to use for
52+
local win = utils.buf_getwin(buffer);
53+
54+
if not win then
55+
return;
56+
end
57+
58+
local height = vim.api.nvim_win_text_height(win, {
59+
start_row = range.row,
60+
end_row = range.row
2461
});
2562

26-
return extmarks[1];
63+
if height.all == 1 then
64+
return;
65+
end
66+
67+
wrap.cache[buffer][row] = vim.list_extend(wrap.cache[buffer][row], indent);
68+
69+
---|fE
2770
end
2871

2972
--[[ Provides `wrapped indentation` to some text. ]]
3073
---@param buffer integer
31-
---@param opts markview.wrap.opts
32-
wrap.wrap_indent = function (buffer, opts)
74+
wrap.render = function (buffer, ns)
3375
---|fS
3476

77+
---@type integer? Window to use for
3578
local win = utils.buf_getwin(buffer);
3679

37-
local win_width = vim.api.nvim_win_get_width(win);
38-
local textoff = vim.fn.getwininfo(win)[1].textoff;
39-
local W = win_width - textoff;
40-
41-
if vim.fn.strdisplaywidth(opts.line or "") < W then
80+
if not win then
4281
return;
4382
end
4483

45-
local win_x = vim.api.nvim_win_get_position(win)[2];
46-
local passed_start = false;
84+
local function render_line (row, indent)
85+
local textoff = vim.fn.getwininfo(win)[1].textoff;
4786

48-
for c = 1, vim.fn.strdisplaywidth(opts.line or "") do
49-
--- `l` should be 1-indexed.
50-
---@type integer
51-
local x = vim.fn.screenpos(win, opts.row + 1, c).col - (win_x + textoff);
87+
local win_x = vim.api.nvim_win_get_position(win)[2];
5288

53-
if x ~= 1 then
54-
goto continue;
55-
elseif passed_start == false then
56-
passed_start = true;
57-
goto continue;
58-
end
89+
local text = vim.api.nvim_buf_get_lines(buffer, row, row + 1, false)[1];
90+
local chars = vim.fn.split(text, "\\zs");
5991

60-
local extmark = wrap.get_extmark(buffer, opts.ns, opts.row, c - 1);
92+
local c = 0;
93+
local before_start = true;
6194

62-
if extmark ~= nil then
63-
local id = extmark[1];
64-
local virt_text = extmark[4].virt_text;
95+
local last_screencol = math.huge;
6596

66-
vim.api.nvim_buf_set_extmark(buffer, opts.ns, opts.row, c - 1, {
67-
id = id,
97+
for _, char in ipairs(chars) do
98+
c = c + #char;
6899

69-
undo_restore = false, invalidate = true,
70-
right_gravity = false,
100+
local x = vim.fn.screenpos(win, row + 1, c).col - (win_x + textoff);
71101

72-
virt_text_pos = "inline",
73-
---@diagnostic disable-next-line: param-type-mismatch
74-
virt_text = vim.list_extend(virt_text, opts.indent or {}),
102+
if x < last_screencol and before_start == true then
103+
before_start = false;
104+
elseif x < last_screencol then
105+
local indent_opts = {
106+
undo_restore = false, invalidate = true,
107+
right_gravity = false,
75108

76-
hl_mode = "combine",
77-
});
78-
else
79-
vim.api.nvim_buf_set_extmark(buffer, opts.ns, opts.row, c - 1, {
80-
undo_restore = false, invalidate = true,
81-
right_gravity = false,
109+
virt_text_pos = "inline",
110+
hl_mode = "combine",
82111

83-
virt_text_pos = "inline",
84-
virt_text = opts.indent or {},
112+
virt_text = indent;
113+
};
85114

86-
hl_mode = "combine",
87-
});
115+
vim.api.nvim_buf_set_extmark(
116+
buffer,
117+
ns,
118+
row,
119+
c - 1,
120+
indent_opts
121+
);
122+
end
123+
124+
last_screencol = x;
88125
end
126+
end
89127

90-
::continue::
128+
for row, indent in pairs(wrap.cache[buffer] or {}) do
129+
render_line(row, indent);
91130
end
92131

132+
wrap.cache[buffer] = {};
133+
93134
---|fE
94135
end
95136

0 commit comments

Comments
 (0)