Skip to content

Commit e6c8081

Browse files
fix: support for code border for 0.11, disable conceal_lines highlights
## Details Related issues: - #351 - #352 This change adds back the code border that currently gets removed by the default `conceal_lines` highlight that ships with `nvim-treesitter`. I tried to make this work using virtual lines at first but ran into quite a few issues with screen offset behavior and was unable to fix the fact that LSP hover docs are now truncated based on concealed lines leading to windows that did not take our virtual lines into account. Ultimately to get around this I've added a new option called `patterns` which takes a list of directive names & ids and runs the `disable_pattern` API to turn these off. The default value: ```lua { patterns = { markdown = { disable = true, directives = { { id = 17, name = 'conceal_lines' }, { id = 18, name = 'conceal_lines' }, }, }, }, } ``` Will disable markdown highlight patterns 17 & 18 if they correspond to a `conceal_lines` directive. There is not any guarantee that the ids will remain constant and users can override the highlights with their own which we do not want to break. Hopefully ensuring the ids & names match will work for most people and I can keep the default value working with the most common case going forward. This behavior can be disabled by users by setting `disable = false`. Since removing these highlight patterns also removes the regular character level concealing this plugin has been updated to add these for the delimiter and language nodes itself, rather than relying on the `nvim-treesitter` behavior. Disabling patterns can take a while at startup so this instead runs on just before this plugin actually attaches to a buffer rather than as part of the startup sequence. This behavior is specific to `0.11.0` so the logic is appropriately gated to prevent any older versions from running into errors, we should still be compatible down to `0.9.0`. There's also a new `code.border` value called `hide` which is the new default and applies the `conceal_lines` value to empty fenced code delimiters. Older versions of neovim will continue to use the `thin` border style as a fallback. This largely hides the bottom border, but will also hide the top one if no language icon / name is added or available.
1 parent 5dcadc7 commit e6c8081

25 files changed

+342
-198
lines changed

README.md

+14-2
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,16 @@ require('render-markdown').setup({
220220
]],
221221
},
222222
},
223+
-- Highlight patterns to disable for filetypes, i.e. lines concealed around code blocks
224+
patterns = {
225+
markdown = {
226+
disable = true,
227+
directives = {
228+
{ id = 17, name = 'conceal_lines' },
229+
{ id = 18, name = 'conceal_lines' },
230+
},
231+
},
232+
},
223233
anti_conceal = {
224234
-- This enables hiding any added text on the line the cursor is on.
225235
enabled = true,
@@ -427,7 +437,8 @@ require('render-markdown').setup({
427437
-- | none | do not render a border |
428438
-- | thick | use the same highlight as the code body |
429439
-- | thin | when lines are empty overlay the above & below icons |
430-
border = 'thin',
440+
-- | hide | conceal lines unless language name or icon is added |
441+
border = 'hide',
431442
-- Used above code blocks for thin border.
432443
above = '',
433444
-- Used below code blocks for thin border.
@@ -978,7 +989,8 @@ require('render-markdown').setup({
978989
-- | none | do not render a border |
979990
-- | thick | use the same highlight as the code body |
980991
-- | thin | when lines are empty overlay the above & below icons |
981-
border = 'thin',
992+
-- | hide | conceal lines unless language name or icon is added |
993+
border = 'hide',
982994
-- Used above code blocks for thin border.
983995
above = '',
984996
-- Used below code blocks for thin border.

doc/render-markdown.txt

+14-2
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,16 @@ Default Configuration ~
285285
]],
286286
},
287287
},
288+
-- Highlight patterns to disable for filetypes, i.e. lines concealed around code blocks
289+
patterns = {
290+
markdown = {
291+
disable = true,
292+
directives = {
293+
{ id = 17, name = 'conceal_lines' },
294+
{ id = 18, name = 'conceal_lines' },
295+
},
296+
},
297+
},
288298
anti_conceal = {
289299
-- This enables hiding any added text on the line the cursor is on.
290300
enabled = true,
@@ -492,7 +502,8 @@ Default Configuration ~
492502
-- | none | do not render a border |
493503
-- | thick | use the same highlight as the code body |
494504
-- | thin | when lines are empty overlay the above & below icons |
495-
border = 'thin',
505+
-- | hide | conceal lines unless language name or icon is added |
506+
border = 'hide',
496507
-- Used above code blocks for thin border.
497508
above = '▄',
498509
-- Used below code blocks for thin border.
@@ -1037,7 +1048,8 @@ Code Block Configuration ~
10371048
-- | none | do not render a border |
10381049
-- | thick | use the same highlight as the code body |
10391050
-- | thin | when lines are empty overlay the above & below icons |
1040-
border = 'thin',
1051+
-- | hide | conceal lines unless language name or icon is added |
1052+
border = 'hide',
10411053
-- Used above code blocks for thin border.
10421054
above = '▄',
10431055
-- Used below code blocks for thin border.

lua/render-markdown/core/conceal.lua

+6-6
Original file line numberDiff line numberDiff line change
@@ -72,18 +72,18 @@ function Conceal:has(line, entry)
7272
return false
7373
end
7474

75-
---@param width integer
7675
---@param character? string
7776
---@return integer
78-
function Conceal:adjust(width, character)
77+
function Conceal:width(character)
7978
if self.level == 1 then
8079
-- each block is replaced with one character
81-
return width - 1
80+
return 1
8281
elseif self.level == 2 then
8382
-- replacement character width is used
84-
return width - Str.width(character)
83+
return Str.width(character)
8584
else
86-
return width
85+
-- text is completely hidden
86+
return 0
8787
end
8888
end
8989

@@ -100,7 +100,7 @@ function Conceal:get(node)
100100
local result = 0
101101
for _, section in ipairs(self:line(node).sections) do
102102
if node.start_col < section.end_col and node.end_col > section.start_col then
103-
local width = self:adjust(section.width, section.character)
103+
local width = section.width - self:width(section.character)
104104
result = result + width
105105
end
106106
end

lua/render-markdown/core/treesitter.lua

-45
This file was deleted.

lua/render-markdown/handler/html.lua

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
local Context = require('render-markdown.core.context')
22
local Marks = require('render-markdown.lib.marks')
33
local state = require('render-markdown.state')
4-
local treesitter = require('render-markdown.core.treesitter')
4+
local ts = require('render-markdown.integ.ts')
55

66
---@class render.md.handler.buf.Html
77
---@field private config render.md.buffer.Config
@@ -19,7 +19,7 @@ function Handler.new(buf)
1919
self.config = state.get(buf)
2020
self.context = Context.get(buf)
2121
self.marks = Marks.new(buf, true)
22-
self.query = treesitter.parse(
22+
self.query = ts.parse(
2323
'html',
2424
[[
2525
(comment) @comment

lua/render-markdown/handler/markdown.lua

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
local Context = require('render-markdown.core.context')
22
local Marks = require('render-markdown.lib.marks')
33
local state = require('render-markdown.state')
4-
local treesitter = require('render-markdown.core.treesitter')
4+
local ts = require('render-markdown.integ.ts')
55

66
---@class render.md.handler.buf.Markdown
77
---@field private config render.md.buffer.Config
@@ -19,7 +19,7 @@ function Handler.new(buf)
1919
self.config = state.get(buf)
2020
self.context = Context.get(buf)
2121
self.marks = Marks.new(buf, false)
22-
self.query = treesitter.parse(
22+
self.query = ts.parse(
2323
'markdown',
2424
[[
2525
(section) @section

lua/render-markdown/handler/markdown_inline.lua

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
local Context = require('render-markdown.core.context')
22
local Marks = require('render-markdown.lib.marks')
33
local state = require('render-markdown.state')
4-
local treesitter = require('render-markdown.core.treesitter')
4+
local ts = require('render-markdown.integ.ts')
55

66
---@class render.md.handler.buf.MarkdownInline
77
---@field private config render.md.buffer.Config
@@ -19,7 +19,7 @@ function Handler.new(buf)
1919
self.config = state.get(buf)
2020
self.context = Context.get(buf)
2121
self.marks = Marks.new(buf, true)
22-
self.query = treesitter.parse(
22+
self.query = ts.parse(
2323
'markdown_inline',
2424
[[
2525
(code_span) @code_inline

lua/render-markdown/health.lua

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ local state = require('render-markdown.state')
55
local M = {}
66

77
---@private
8-
M.version = '8.1.20'
8+
M.version = '8.1.21'
99

1010
function M.check()
1111
M.start('version')

lua/render-markdown/init.lua

+22-2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@ local M = {}
4747
---@field render? fun(ctx: render.md.CallbackContext)
4848
---@field clear? fun(ctx: render.md.CallbackContext)
4949

50+
---@class (exact) render.md.UserDirective
51+
---@field id? integer
52+
---@field name? string
53+
54+
---@class (exact) render.md.UserPattern
55+
---@field disable? boolean
56+
---@field directives? render.md.UserDirective[]
57+
5058
---@class (exact) render.md.UserInjection
5159
---@field enabled? boolean
5260
---@field query? string
@@ -207,7 +215,7 @@ local M = {}
207215
---@alias render.md.code.Style 'full'|'normal'|'language'|'none'
208216
---@alias render.md.code.Position 'left'|'right'
209217
---@alias render.md.code.Width 'full'|'block'
210-
---@alias render.md.code.Border 'thin'|'thick'|'none'
218+
---@alias render.md.code.Border 'hide'|'thin'|'thick'|'none'
211219

212220
---@class (exact) render.md.UserCode: render.md.UserBaseComponent
213221
---@field sign? boolean
@@ -340,6 +348,7 @@ local M = {}
340348
---@field file_types? string[]
341349
---@field change_events? string[]
342350
---@field injections? table<string, render.md.UserInjection>
351+
---@field patterns? table<string, render.md.UserPattern>
343352
---@field on? render.md.UserCallback
344353
---@field completions? render.md.UserCompletions
345354
---@field overrides? render.md.UserConfigOverrides
@@ -389,6 +398,16 @@ M.default_config = {
389398
]],
390399
},
391400
},
401+
-- Highlight patterns to disable for filetypes, i.e. lines concealed around code blocks
402+
patterns = {
403+
markdown = {
404+
disable = true,
405+
directives = {
406+
{ id = 17, name = 'conceal_lines' },
407+
{ id = 18, name = 'conceal_lines' },
408+
},
409+
},
410+
},
392411
anti_conceal = {
393412
-- This enables hiding any added text on the line the cursor is on.
394413
enabled = true,
@@ -596,7 +615,8 @@ M.default_config = {
596615
-- | none | do not render a border |
597616
-- | thick | use the same highlight as the code body |
598617
-- | thin | when lines are empty overlay the above & below icons |
599-
border = 'thin',
618+
-- | hide | conceal lines unless language name or icon is added |
619+
border = 'hide',
600620
-- Used above code blocks for thin border.
601621
above = '',
602622
-- Used below code blocks for thin border.

lua/render-markdown/integ/ts.lua

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
local Env = require('render-markdown.lib.env')
2+
3+
---@class render.md.integ.TreeSitter
4+
---@field private initialized boolean
5+
---@field private queries table<string, vim.treesitter.Query>
6+
local M = {
7+
initialized = false,
8+
queries = {},
9+
}
10+
11+
---Should only be called from manager on initial buffer attach
12+
function M.setup()
13+
if M.initialized then
14+
return
15+
end
16+
M.initialized = true
17+
local state = require('render-markdown.state')
18+
for _, language in ipairs(state.file_types) do
19+
M.disable(language, state.patterns[language])
20+
end
21+
end
22+
23+
---@param language string
24+
---@param query string
25+
---@return vim.treesitter.Query
26+
function M.parse(language, query)
27+
local result = M.queries[query]
28+
if result == nil then
29+
result = vim.treesitter.query.parse(language, query)
30+
M.queries[query] = result
31+
end
32+
return result
33+
end
34+
35+
---@param language string
36+
---@param injection? render.md.Injection
37+
function M.inject(language, injection)
38+
if injection == nil or not injection.enabled then
39+
return
40+
end
41+
42+
local query = ''
43+
if Env.has_11 then
44+
query = query .. ';; extends' .. '\n'
45+
else
46+
local files = vim.treesitter.query.get_files(language, 'injections')
47+
for _, file in ipairs(files) do
48+
local f = io.open(file, 'r')
49+
if f ~= nil then
50+
query = query .. f:read('*all') .. '\n'
51+
f:close()
52+
end
53+
end
54+
end
55+
query = query .. injection.query
56+
pcall(vim.treesitter.query.set, language, 'injections', query)
57+
end
58+
59+
---@private
60+
---@param language string
61+
---@param pattern? render.md.Pattern
62+
function M.disable(language, pattern)
63+
if not Env.has_11 then
64+
return
65+
end
66+
if pattern == nil or not pattern.disable then
67+
return
68+
end
69+
local query = vim.treesitter.query.get(language, 'highlights')
70+
if query == nil then
71+
return
72+
end
73+
local query_directives = query.info.patterns
74+
for _, directive in ipairs(pattern.directives) do
75+
local query_directive = query_directives[directive.id]
76+
if M.has_directive(directive.name, query_directive) then
77+
query.query:disable_pattern(directive.id)
78+
end
79+
end
80+
end
81+
82+
---@private
83+
---@param name string
84+
---@param directives? (string|integer)[][]
85+
---@return boolean
86+
function M.has_directive(name, directives)
87+
if directives == nil then
88+
return false
89+
end
90+
for _, directive in ipairs(directives) do
91+
if directive[1] == 'set!' and directive[2] == name then
92+
return true
93+
end
94+
end
95+
return false
96+
end
97+
98+
return M

0 commit comments

Comments
 (0)