Skip to content

Commit 58c6d48

Browse files
committed
tui: support multiple hlgroups per Element
Element.hlgroup (now hlgroups) accepted only a single string or a function returning one, making it impossible to apply two orthogonal highlights to the same element. In particular, interactive_code's diff-status highlight was silently dropped whenever the element was also selectable, because the locations function assignment overwrote the diff string. hlgroups is now always a string[] (or a function returning one). interactive_code returns both groups when an element has a diff status and is simultaneously selected.
1 parent c35bc29 commit 58c6d48

15 files changed

Lines changed: 121 additions & 50 deletions

lua/lean/infoview/components.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ local widgets = require 'lean.widgets'
2222
local components = {
2323
LSP_HAS_DIED = Element:new {
2424
text = '🪦 The Lean language server is dead.',
25-
hlgroup = 'DiagnosticError',
25+
hlgroups = { 'DiagnosticError' },
2626
},
2727
NO_INFO = Element:new { text = 'No info.', name = 'no-info' },
2828
PROCESSING = Element:new { text = 'Processing file...', name = 'processing' },

lua/lean/infoview/locations.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ function Locations:toggle_subexpr_selection(pos)
176176
self:toggle_selection(self:template_with_subexpr_pos(pos))
177177
end
178178

179-
--TODO: have Locations handle constructing hlgroup()
179+
--TODO: have Locations handle constructing hlgroups()
180180
---@param pos SubexprPos
181181
function Locations:is_subexpr_selected(pos)
182182
return self:is_selected(self:template_with_subexpr_pos(pos))

lua/lean/tui.lua

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ local log = require 'lean.log'
4242
---@field events EventCallbacks functions to fire for events which this element responds to
4343
---@field text string the text to show when rendering this element
4444
---@field name string a named handle for this element, used when path-searching
45-
---@field hlgroup? string|fun(string):string? the highlight group for this element's text, or a function that returns it
45+
---@field hlgroups? string[]|fun():string[]|nil the highlight group(s) for this element's text, or a function that returns them
4646
---@field tooltip? Element? tooltip
4747
---@field highlightable boolean (for buffer rendering) whether to highlight this element when hovering over it
4848
---@field _size? integer Computed size of this element, updated by `Element:to_string`
@@ -73,7 +73,7 @@ BufRenderer.__index = BufRenderer
7373
---@field events? EventCallbacks event function map
7474
---@field text? string the text to show when rendering this element
7575
---@field name? string a named handle for this element, used when path-searching
76-
---@field hlgroup? string|fun():string? the highlight group for this element's text, or a function that returns it
76+
---@field hlgroups? string[]|fun():string[]|nil the highlight group(s) for this element's text, or a function that returns them
7777
---@field highlightable boolean? (for buffer rendering) whether to highlight this element when hovering over it
7878
---@field children? Element[] this element's children
7979
---@field private __async_init? fun(on_result: fun(Element):nil):nil
@@ -86,7 +86,7 @@ function Element:new(args)
8686
local obj = {
8787
text = args.text or '',
8888
name = args.name or '',
89-
hlgroup = args.hlgroup,
89+
hlgroups = args.hlgroups,
9090
highlightable = args.highlightable or false,
9191
events = args.events or {},
9292
__children = args.children or {},
@@ -114,7 +114,7 @@ function Element:titled(opts)
114114
return body
115115
end
116116

117-
local title = self:new { text = opts.title, hlgroup = opts.title_hlgroup }
117+
local title = self:new { text = opts.title, hlgroups = opts.title_hlgroup and { opts.title_hlgroup } or nil }
118118

119119
if not body then
120120
return title
@@ -189,7 +189,7 @@ function Element.select(choices, opts, on_choice)
189189
Element:new { text = '' },
190190
},
191191
highlightable = true,
192-
hlgroup = 'widgetSelect',
192+
hlgroups = { 'widgetSelect' },
193193
events = {
194194
click = function(ctx)
195195
vim.ui.select(choices, {
@@ -214,7 +214,7 @@ end
214214
---@param key string
215215
---@return Element
216216
function Element.kbd(key)
217-
return Element:new { text = key, hlgroup = 'widgetKbd' }
217+
return Element:new { text = key, hlgroups = { 'widgetKbd' } }
218218
end
219219

220220
---Create an Element whose click event does nothing.
@@ -308,16 +308,18 @@ function Element:_get_highlights()
308308
---@param element Element
309309
---@param pos integer
310310
local function go(element, pos)
311-
local hlgroup = element.hlgroup
312-
if type(hlgroup) == 'function' then
313-
hlgroup = hlgroup(element)
311+
local hlgroups = element.hlgroups
312+
if type(hlgroups) == 'function' then
313+
hlgroups = hlgroups(element)
314314
end
315-
if hlgroup then
316-
table.insert(hls, {
317-
start = pos,
318-
['end'] = pos + element._size,
319-
hlgroup = hlgroup,
320-
})
315+
if hlgroups then
316+
for _, hg in ipairs(hlgroups) do
317+
table.insert(hls, {
318+
start = pos,
319+
['end'] = pos + element._size,
320+
hlgroup = hg,
321+
})
322+
end
321323
end
322324

323325
pos = pos + #element.text

lua/lean/widget/interactive_code.lua

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ local InteractiveCode
3131
local function render_subexpr_info(subexpr_info, tag, sess, locations)
3232
local element = Element:new { highlightable = true }
3333
if subexpr_info.diffStatus then
34-
element.hlgroup = 'leanInfoDiff' .. subexpr_info.diffStatus
34+
element.hlgroups = { 'leanInfoDiff' .. subexpr_info.diffStatus }
3535
end
3636

3737
local info_open = false
@@ -66,7 +66,7 @@ local function render_subexpr_info(subexpr_info, tag, sess, locations)
6666
if subexpr_info.diffStatus then
6767
tooltip_element:add_child(Element:new { text = '\n\n' })
6868
tooltip_element:add_child(Element:new {
69-
hlgroup = 'Comment',
69+
hlgroups = { 'Comment' },
7070
text = DIFF_TAG_TO_EXPLANATION[subexpr_info.diffStatus],
7171
})
7272
end
@@ -146,8 +146,16 @@ local function render_subexpr_info(subexpr_info, tag, sess, locations)
146146
}
147147

148148
if locations then
149-
function element.hlgroup()
150-
return locations:is_subexpr_selected(subexpr_info.subexprPos) and 'leanInfoSelected' or nil
149+
local diff_hl = element.hlgroups ---@type string[]?
150+
function element.hlgroups()
151+
local selected = locations:is_subexpr_selected(subexpr_info.subexprPos)
152+
if diff_hl and selected then
153+
return vim.iter({ diff_hl, { 'leanInfoSelected' } }):flatten():totable()
154+
elseif selected then
155+
return { 'leanInfoSelected' }
156+
else
157+
return diff_hl
158+
end
151159
end
152160
element.events.select = function()
153161
locations:toggle_subexpr_selection(subexpr_info.subexprPos)

lua/lean/widget/interactive_goal.lua

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,21 @@ end
1919
---@param fvar_id FVarId
2020
---@param locations? Locations
2121
local function to_hypothesis_name(name, mvar_id, fvar_id, locations)
22-
local function accessible_hlgroup()
23-
return is_accessible(name) and 'leanInfoHypName' or 'leanInfoInaccessibleHypName'
22+
local function accessible_hlgroups()
23+
return { is_accessible(name) and 'leanInfoHypName' or 'leanInfoInaccessibleHypName' }
2424
end
2525

2626
local highlightable, select
27-
local hlgroup = accessible_hlgroup
27+
local hlgroups = accessible_hlgroups
2828

2929
if locations and mvar_id and fvar_id then
3030
highlightable = true
3131

3232
---@type GoalsLocation
3333
local location = { mvarId = mvar_id, loc = { hyp = fvar_id } }
3434

35-
hlgroup = function()
36-
return locations:is_selected(location) and 'leanInfoSelected' or accessible_hlgroup()
35+
hlgroups = function()
36+
return locations:is_selected(location) and { 'leanInfoSelected' } or accessible_hlgroups()
3737
end
3838

3939
select = function()
@@ -44,7 +44,7 @@ local function to_hypothesis_name(name, mvar_id, fvar_id, locations)
4444
return Element:new {
4545
text = name,
4646
highlightable = highlightable,
47-
hlgroup = hlgroup,
47+
hlgroups = hlgroups,
4848
events = { select = select },
4949
}
5050
end
@@ -84,8 +84,8 @@ local function to_hypothesis_element(hyp, mvar_id, opts, sess, locations)
8484
name = 'hyp',
8585
children = {
8686
Element:concat(names, ' ', {
87-
hlgroup = hyp.isInserted and 'leanInfoHypNameInserted'
88-
or hyp.isRemoved and 'leanInfoHypNameRemoved'
87+
hlgroups = hyp.isInserted and { 'leanInfoHypNameInserted' }
88+
or hyp.isRemoved and { 'leanInfoHypNameRemoved' }
8989
or nil,
9090
}),
9191
Element:new { text = ' : ' },
@@ -166,7 +166,7 @@ function interactive_goal.Goal(goal, sess, locations)
166166
if goal.userName then
167167
case = Element:new {
168168
children = {
169-
Element:new { text = 'case ', hlgroup = 'leanInfoGoalCase' },
169+
Element:new { text = 'case ', hlgroups = { 'leanInfoGoalCase' } },
170170
Element:new { text = goal.userName .. '\n' },
171171
},
172172
}
@@ -182,7 +182,7 @@ function interactive_goal.Goal(goal, sess, locations)
182182
local goal_element = Element:new {
183183
name = 'goal',
184184
children = {
185-
Element:new { text = goal.goalPrefix or '', hlgroup = 'leanInfoGoalPrefix' },
185+
Element:new { text = goal.goalPrefix or '', hlgroups = { 'leanInfoGoalPrefix' } },
186186
InteractiveCode(goal.type, sess, goal_locations),
187187
},
188188
}

lua/lean/widgets/GoToModuleLink.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ return function(ctx, props)
1616
return Element:new {
1717
text = props.modName,
1818
highlightable = true,
19-
hlgroup = 'widgetLink',
19+
hlgroups = { 'widgetLink' },
2020
events = {
2121
go_to_def = function(_)
2222
local last_window = ctx.get_last_window()

lua/lean/widgets/Lean/Meta/Hint/textInsertionWidget.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ return function(ctx, props)
3232
text = props.acceptSuggestionProps.linkText,
3333
children = Element:new { text = props.suggestion },
3434
highlightable = true,
35-
hlgroup = 'widgetLink',
35+
hlgroups = { 'widgetLink' },
3636
events = {
3737
click = function()
3838
ctx:apply_edits {

lua/lean/widgets/Lean/Meta/Hint/tryThisDiffWidget.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ return function(ctx, props)
2828
return Element:new {
2929
text = props.suggestion,
3030
highlightable = true,
31-
hlgroup = 'widgetLink',
31+
hlgroups = { 'widgetLink' },
3232
events = {
3333
click = function()
3434
ctx:apply_edits {

lua/lean/widgets/Lean/Meta/Tactic/TryThis/tryThisWidget.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ return function(ctx, props)
2626
Element:new {
2727
text = each.suggestion,
2828
highlightable = true,
29-
hlgroup = 'widgetLink',
29+
hlgroups = { 'widgetLink' },
3030
events = {
3131
click = function()
3232
ctx:apply_edits {

lua/lean/widgets/Lean/errorDescriptionWidget.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ return function(_, props)
1313
Element:new {
1414
text = 'View explanation',
1515
highlightable = true,
16-
hlgroup = 'widgetLink',
16+
hlgroups = { 'widgetLink' },
1717
events = {
1818
click = function()
1919
vim.ui.open(props.explanationUrl)

0 commit comments

Comments
 (0)