Skip to content

Commit 726c85c

Browse files
feature: Full anti conceal behavior, wooooo
# Details This feature allows the plugin to hide any virtual text that would be displayed on the line the cursor is currently on. This enables are super fluid editing experience since nothing is concealed or obstructed by the rendering behavior. This is similar conceptually to concealing a link, but rather than hiding information that is already there, we are hiding information added by this plugin. Since this is not a native neovim feature, i.e. I cannot set a nice value in nvim_buf_set_extmark that makes this happen, this feature relies on listening to the 'CursorMoved' event. This event gets triggered on, as you would think, every cursor movement, so this comes at a very real performance penalty. The logic has been optimized to avoid re-parsing the entire file on cursor movements, instead using a cached list of marks and adding the ones outside the current line. This is noticibely faster and I have yet to see a significant degredation. Even so the option to disable this feature is added under the new 'anti_conceal.enabled' option. Other ways to disable this feature (say on large files) can be added, please submit any feature requests as an issue. This should resolve: #15 Though it is doing the exact thing I wanted to avoid, it is easy enough to opt out of and with the caching I have not noticed much of a performance penalty. Not all marks are removed, for instance sign column markers are left as are virtual lines, since they do not interfere with the underlying buffer text. Similarly padded code blocks (if used) are not unpadded since that experience was not great. Since this plugin now effectively controls the life cycle of marks there is a breaking change for anyone using the 'custom_handlers' hook. Rather than directly setting extmarks in the hook the information needs to be passed back to this plugin so it can handle showing the correct ones based on cursor position. As such the 'render' method is on deprecation path, to be removed fully August 19th. Instead the new 'parse' method should be defined which returns effectively the same information
1 parent 0bbc03c commit 726c85c

24 files changed

+690
-322
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
*.cast
2+
medium.md
23
large.md
34
test.md
45
test.log

README.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ Plugin to improve viewing Markdown files in Neovim
1212

1313
- Functions entirely inside of Neovim with no external windows
1414
- Changes between `rendered` view in normal mode and `raw` view in all other modes
15+
- Supports anti-conceal behavior, removing any virtual text added by this plugin
16+
on the line the cursor is on, this does have a performance penalty and can be disabled
1517
- Changes window options between `rendered` and `raw` view based on configuration
1618
- Effects `conceallevel` & `concealcursor` by default
1719
- Supports rendering `markdown` injected into other file types
@@ -166,6 +168,11 @@ require('render-markdown').setup({
166168
-- Buftypes ignored by this plugin, see :h 'buftype'
167169
buftypes = {},
168170
},
171+
anti_conceal = {
172+
-- This enables hiding any added text on the line the cursor is on
173+
-- This does have a performance penalty as we must listen to the 'CursorMoved' event
174+
enabled = true,
175+
},
169176
latex = {
170177
-- Whether LaTeX should be rendered, mainly used for health check
171178
enabled = true,
@@ -366,8 +373,8 @@ require('render-markdown').setup({
366373
concealcursor = {
367374
-- Used when not being rendered, get user setting
368375
default = vim.api.nvim_get_option_value('concealcursor', {}),
369-
-- Used when being rendered, conceal text in all modes
370-
rendered = 'nvic',
376+
-- Used when being rendered, disable concealing text in all modes
377+
rendered = '',
371378
},
372379
},
373380
-- Mapping from treesitter language to user defined handlers

demo/box_dash_quote.gif

32.6 KB
Loading

demo/callout.gif

213 KB
Loading

demo/heading_code.gif

106 KB
Loading

demo/latex.gif

38 KB
Loading

demo/list_table.gif

144 KB
Loading

demo/record.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import time
22
from argparse import ArgumentParser
3+
from pathlib import Path
34

45
import pyautogui
56

@@ -9,6 +10,9 @@ def main(cols: int, rows: int, file: str, cast: str, content: str) -> None:
910
pyautogui.hotkey("`", "c")
1011
time.sleep(1.0)
1112

13+
# Get length of file so we can scroll down it
14+
num_lines = len(Path(file).read_text().splitlines())
15+
1216
# Start recording demo file
1317
# https://docs.asciinema.org/manual/cli/usage/
1418
record_command: list[str] = [
@@ -30,12 +34,14 @@ def main(cols: int, rows: int, file: str, cast: str, content: str) -> None:
3034
pyautogui.press("esc")
3135
time.sleep(2.0)
3236

33-
# Swith between insert and normal mode a few times
34-
for i in range(2):
35-
pyautogui.press("i")
36-
time.sleep(i + 1)
37-
pyautogui.press("esc")
38-
time.sleep((i + 1) * 2)
37+
insert_normal(1)
38+
39+
# Slowly scroll down
40+
for _ in range(num_lines):
41+
pyautogui.press("j")
42+
time.sleep(0.1)
43+
44+
insert_normal(1)
3945

4046
# Close demo file
4147
pyautogui.write(":q!")
@@ -47,6 +53,13 @@ def main(cols: int, rows: int, file: str, cast: str, content: str) -> None:
4753
pyautogui.press("enter")
4854

4955

56+
def insert_normal(seconds: float) -> None:
57+
pyautogui.press("i")
58+
time.sleep(seconds)
59+
pyautogui.press("esc")
60+
time.sleep(seconds)
61+
62+
5063
if __name__ == "__main__":
5164
parser = ArgumentParser(description="Generate a demo recording using asciinema")
5265
parser.add_argument("--cols", type=int, required=True)

doc/custom-handlers.md

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,28 @@
33
Custom handlers allow users to integrate custom rendering for either unsupported
44
languages or to override / extend builtin implementations.
55

6-
Custom handlers are ran identically to builtin ones, so by writing custom `extmark`s
7-
(see :h nvim_buf_set_extmark()) to the `namespace` this plugin will handle clearing
8-
the `extmark`s on mode changes as well as re-rendering when needed.
6+
Custom handlers are ran identically to builtin ones, so by returning `extmark` data
7+
this plugin will handle clearing the `extmark`s on mode changes, re-rendering when
8+
needed, and concealing when the cursor enters.
99

1010
## Interface
1111

1212
Each handler must conform to the following interface:
1313

1414
```lua
15+
---@class render.md.Mark
16+
---@field public conceal boolean
17+
---@field public start_row integer
18+
---@field public start_col integer
19+
---@field public opts vim.api.keyset.set_extmark
20+
1521
---@class render.md.Handler
16-
---@field public render fun(namespace: integer, root: TSNode, buf: integer)
22+
---@field public parse fun(root: TSNode, buf: integer): render.md.Mark[]
1723
---@field public extends? boolean
1824
```
1925

20-
The `render` function parameters are:
26+
The `parse` function parameters are:
2127

22-
- `namespace`: The id that this plugin interacts with when setting and clearing `extmark`s
2328
- `root`: The root treesitter node for the specified language
2429
- `buf`: The buffer containing the root node
2530

@@ -31,45 +36,66 @@ a treesitter query is entirely up to the user if the functionality they want nee
3136
this. We do not provide any convenience functions, but you are more than welcome
3237
to use patterns from the builtin handlers.
3338

39+
For each `mark` in the return value the fields mean:
40+
41+
- `conceal`: determines if the mark should be hidden when cursor enters
42+
- `start_row`: only value used to check whether cursor is inside the `mark`
43+
- `start_col`: passed to `nvim_buf_set_extmark` as the 3rd argument
44+
- `opts`: passed directly to `nvim_buf_set_extmark`, no special handling
45+
3446
## Example 1: Disable a Builtin
3547

36-
By not specifying the `extends` field and leaving the `render` implementation blank
37-
we can disable a builtin handler. Though this has little benefit and can be accomplished
38-
in other ways such as setting `{ latex_enabled = false }` for `LaTeX`.
48+
By not specifying the `extends` field and having the `parse` implementation return
49+
an empty table we can disable a builtin handler. Though this has little benefit and
50+
can be accomplished in other ways like setting `{ latex = { enabled = false } }`
51+
for `LaTeX`.
3952

4053
Still as a toy example disabling the `LaTeX` handler can be done with:
4154

4255
```lua
4356
require('render-markdown').setup({
4457
custom_handlers = {
45-
latex = { render = function() end },
58+
latex = {
59+
parse = function()
60+
return {}
61+
end,
62+
},
4663
},
47-
}
64+
})
4865
```
4966

5067
## Example 2: Highlight `python` Function Definitions
5168

5269
This will require a treesitter query and using the range values of nodes.
5370

5471
```lua
55-
-- Parse query outside of the render function to avoid doing it for each call
72+
-- Parse query outside of the function to avoid doing it for each call
5673
local query = vim.treesitter.query.parse('python', '(function_definition) @def')
57-
local function render_pyth for id, node in query:iter_captures(root, buf) do
74+
local function parse_python(root, buf)
75+
local marks = {}
76+
for id, node in query:iter_captures(root, buf) do
5877
local capture = query.captures[id]
5978
local start_row, _, _, _ = node:range()
6079
if capture == 'def' then
61-
vim.api.nvim_buf_set_extmark(buf, namespace, start_row, 0, {
62-
end_row = start_row + 1,
63-
end_col = 0,
64-
hl_group = 'DiffDelete',
65-
hl_eol = true,
80+
table.insert(marks, {
81+
conceal = true,
82+
start_row = start_row,
83+
start_col = 0,
84+
opts = {
85+
end_row = start_row + 1,
86+
end_col = 0,
87+
hl_group = 'DiffDelete',
88+
hl_eol = true,
89+
},
6690
})
6791
end
6892
end
93+
return marks
6994
end
7095
require('render-markdown').setup({
96+
file_types = { 'markdown', 'python' },
7197
custom_handlers = {
72-
python = { render = render_python },
98+
python = { parse = parse_python },
7399
},
74-
}
100+
})
75101
```

doc/render-markdown.txt

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
*render-markdown.txt* For 0.10.0 Last change: 2024 July 15
1+
*render-markdown.txt* For 0.10.0 Last change: 2024 July 16
22

33
==============================================================================
44
Table of Contents *render-markdown-table-of-contents*
@@ -41,6 +41,8 @@ Plugin to improve viewing Markdown files in Neovim
4141

4242
- Functions entirely inside of Neovim with no external windows
4343
- Changes between `rendered` view in normal mode and `raw` view in all other modes
44+
- Supports anti-conceal behavior, removing any virtual text added by this plugin
45+
on the line the cursor is on, this does have a performance penalty and can be disabled
4446
- Changes window options between `rendered` and `raw` view based on configuration
4547
- Effects `conceallevel` & `concealcursor` by default
4648
- Supports rendering `markdown` injected into other file types
@@ -202,6 +204,11 @@ Full Default Configuration ~
202204
-- Buftypes ignored by this plugin, see :h 'buftype'
203205
buftypes = {},
204206
},
207+
anti_conceal = {
208+
-- This enables hiding any added text on the line the cursor is on
209+
-- This does have a performance penalty as we must listen to the 'CursorMoved' event
210+
enabled = true,
211+
},
205212
latex = {
206213
-- Whether LaTeX should be rendered, mainly used for health check
207214
enabled = true,
@@ -402,8 +409,8 @@ Full Default Configuration ~
402409
concealcursor = {
403410
-- Used when not being rendered, get user setting
404411
default = vim.api.nvim_get_option_value('concealcursor', {}),
405-
-- Used when being rendered, conceal text in all modes
406-
rendered = 'nvic',
412+
-- Used when being rendered, disable concealing text in all modes
413+
rendered = '',
407414
},
408415
},
409416
-- Mapping from treesitter language to user defined handlers

0 commit comments

Comments
 (0)