11local Str = require (' render-markdown.lib.str' )
2+ local util = require (' render-markdown.core.util' )
3+
4+ --- @class render.md.conceal.Section
5+ --- @field start_col integer
6+ --- @field end_col integer
7+ --- @field width integer
8+ --- @field character ? string
9+
10+ --- @class render.md.conceal.Line
11+ --- @field hidden boolean
12+ --- @field sections render.md.conceal.Section[]
213
314--- @class render.md.Conceal
415--- @field private buf integer
516--- @field private level integer
617--- @field private computed boolean
7- --- @field private rows table<integer, [integer , integer , integer][] >
18+ --- @field private lines table<integer, render.md.conceal.Line >
819local Conceal = {}
920Conceal .__index = Conceal
1021
@@ -16,7 +27,7 @@ function Conceal.new(buf, level)
1627 self .buf = buf
1728 self .level = level
1829 self .computed = false
19- self .rows = {}
30+ self .lines = {}
2031 return self
2132end
2233
@@ -26,59 +37,83 @@ function Conceal:enabled()
2637end
2738
2839--- @param row integer
29- --- @param start_col integer
30- --- @param end_col integer
31- --- @param amount integer
32- --- @param character ? string
33- function Conceal :add (row , start_col , end_col , amount , character )
34- if not self :enabled () or amount == 0 then
40+ --- @param entry boolean | render.md.conceal.Section
41+ function Conceal :add (row , entry )
42+ if not self :enabled () then
3543 return
3644 end
37- if self .rows [row ] == nil then
38- self .rows [row ] = {}
45+ if self .lines [row ] == nil then
46+ self .lines [row ] = { hidden = false , sections = {} }
3947 end
40- -- If the range is already concealed by another don't add it
41- for _ , range in ipairs (self .rows [row ]) do
42- if range [1 ] <= start_col and range [2 ] >= end_col then
48+ local line = self .lines [row ]
49+ if type (entry ) == ' boolean' then
50+ line .hidden = entry
51+ else
52+ if entry .width <= 0 then
4353 return
4454 end
55+ -- If the section is covered by an existing one don't add it
56+ for _ , section in ipairs (line .sections ) do
57+ if section .start_col <= entry .start_col and section .end_col >= entry .end_col then
58+ return
59+ end
60+ end
61+ table.insert (line .sections , entry )
4562 end
46- table.insert (self .rows [row ], { start_col , end_col , self :adjust (amount , character ) })
4763end
4864
49- --- @param amount integer
65+ --- @param width integer
5066--- @param character ? string
5167--- @return integer
52- function Conceal :adjust (amount , character )
68+ function Conceal :adjust (width , character )
5369 if self .level == 1 then
54- -- Level 1: each block is replaced with one character
55- amount = amount - 1
70+ -- each block is replaced with one character
71+ return width - 1
5672 elseif self .level == 2 then
57- -- Level 2: replacement character width is used
58- amount = amount - Str .width (character )
73+ -- replacement character width is used
74+ return width - Str .width (character )
75+ else
76+ return width
5977 end
60- return amount
78+ end
79+
80+ --- @param context render.md.Context
81+ --- @param node render.md.Node
82+ --- @return boolean
83+ function Conceal :hidden (context , node )
84+ -- conceal lines metadata require neovim >= 0.11.0 to function
85+ return util .has_11 and self :line (context , node ).hidden
6186end
6287
6388--- @param context render.md.Context
6489--- @param node render.md.Node
6590--- @return integer
6691function Conceal :get (context , node )
67- if not self .computed then
68- self .computed = true
69- self :compute (context )
70- end
71-
7292 local result = 0
73- local ranges = self . rows [ node . start_row ] or {}
74- for _ , range in ipairs ( ranges ) do
75- if node . start_col < range [ 2 ] and node . end_col > range [ 1 ] then
76- result = result + range [ 3 ]
93+ for _ , section in ipairs ( self : line ( context , node ). sections ) do
94+ if node . start_col < section . end_col and node . end_col > section . start_col then
95+ local amount = self : adjust ( section . width , section . character )
96+ result = result + amount
7797 end
7898 end
7999 return result
80100end
81101
102+ --- @private
103+ --- @param context render.md.Context
104+ --- @param node render.md.Node
105+ function Conceal :line (context , node )
106+ if not self .computed then
107+ self :compute (context )
108+ self .computed = true
109+ end
110+ local line = self .lines [node .start_row ]
111+ if line == nil then
112+ line = { hidden = false , sections = {} }
113+ end
114+ return line
115+ end
116+
82117--- Cached row level implementation of vim.treesitter.get_captures_at_pos
83118--- @private
84119--- @param context render.md.Context
@@ -116,21 +151,40 @@ function Conceal:compute_tree(context, language, root)
116151 end
117152 context :for_each (function (range )
118153 for id , node , metadata in query :iter_captures (root , self .buf , range .top , range .bottom ) do
154+ if metadata .conceal_lines ~= nil then
155+ local node_range = self :node_range (id , node , metadata )
156+ local row = unpack (node_range )
157+ self :add (row , true )
158+ end
119159 if metadata .conceal ~= nil then
120- local node_range = metadata .range
121- if node_range == nil and metadata [id ] ~= nil then
122- node_range = metadata [id ].range
123- end
124- if node_range == nil then
125- --- @diagnostic disable-next-line : missing-fields
126- node_range = { node :range () }
127- end
160+ local node_range = self :node_range (id , node , metadata )
128161 local row , start_col , _ , end_col = unpack (node_range )
129- local amount = Str .width (vim .treesitter .get_node_text (node , self .buf ))
130- self :add (row , start_col , end_col , amount , metadata .conceal )
162+ self :add (row , {
163+ start_col = start_col ,
164+ end_col = end_col ,
165+ width = Str .width (vim .treesitter .get_node_text (node , self .buf )),
166+ character = metadata .conceal ,
167+ })
131168 end
132169 end
133170 end )
134171end
135172
173+ --- @private
174+ --- @param id integer
175+ --- @param node TSNode
176+ --- @param metadata vim.treesitter.query.TSMetadata
177+ --- @return Range
178+ function Conceal :node_range (id , node , metadata )
179+ local range = metadata .range
180+ if range ~= nil then
181+ return range
182+ end
183+ range = metadata [id ] ~= nil and metadata [id ].range or nil
184+ if range ~= nil then
185+ return range
186+ end
187+ return { node :range () }
188+ end
189+
136190return Conceal
0 commit comments