1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
--- @class blink.cmp.DrawColumn
--- @field components blink.cmp.DrawComponent[]
--- @field gap number
--- @field lines string[][]
--- @field width number
--- @field ctxs blink.cmp.DrawItemContext[]
---
--- @field new fun(components: blink.cmp.DrawComponent[], gap: number): blink.cmp.DrawColumn
--- @field render fun(self: blink.cmp.DrawColumn, ctxs: blink.cmp.DrawItemContext[])
--- @field get_line_text fun(self: blink.cmp.DrawColumn, line_idx: number): string
--- @field get_line_highlights fun(self: blink.cmp.DrawColumn, line_idx: number): blink.cmp.DrawHighlight[]
local text_lib = require('blink.cmp.completion.windows.render.text')
--- @type blink.cmp.DrawColumn
--- @diagnostic disable-next-line: missing-fields
local column = {}
function column.new(components, gap)
local self = setmetatable({}, { __index = column })
self.components = components
self.gap = gap
self.lines = {}
self.width = 0
self.ctxs = {}
return self
end
function column:render(ctxs)
--- render text and get the max widths of each component
--- @type string[][]
local lines = {}
local max_component_widths = {}
for _, ctx in ipairs(ctxs) do
--- @type string[]
local line = {}
for component_idx, component in ipairs(self.components) do
local text = text_lib.apply_component_width(component.text(ctx) or '', component)
table.insert(line, text)
max_component_widths[component_idx] =
math.max(max_component_widths[component_idx] or 0, vim.api.nvim_strwidth(text))
end
table.insert(lines, line)
end
--- get the total width of the column
local column_width = 0
for _, max_component_width in ipairs(max_component_widths) do
if max_component_width > 0 then column_width = column_width + max_component_width + self.gap end
end
column_width = math.max(column_width - self.gap, 0)
--- find the component that will fill the empty space
local fill_idx = -1
for component_idx, component in ipairs(self.components) do
if component.width and component.width.fill then
fill_idx = component_idx
break
end
end
if fill_idx == -1 then fill_idx = #self.components end
--- and add extra spaces until we reach the column width
for _, line in ipairs(lines) do
local line_width = 0
for _, component_text in ipairs(line) do
if #component_text > 0 then line_width = line_width + vim.api.nvim_strwidth(component_text) + self.gap end
end
line_width = line_width - self.gap
local remaining_width = column_width - line_width
line[fill_idx] = text_lib.pad(line[fill_idx], vim.api.nvim_strwidth(line[fill_idx]) + remaining_width)
end
-- store results for later
self.width = column_width
self.lines = lines
self.ctxs = ctxs
end
function column:get_line_text(line_idx)
local text = ''
local line = self.lines[line_idx]
for _, component in ipairs(line) do
if #component > 0 then text = text .. component .. string.rep(' ', self.gap) end
end
return text:sub(1, -self.gap - 1)
end
function column:get_line_highlights(line_idx)
local ctx = self.ctxs[line_idx]
local offset = 0
local highlights = {}
for component_idx, component in ipairs(self.components) do
local text = self.lines[line_idx][component_idx]
if #text > 0 then
local column_highlights = type(component.highlight) == 'function' and component.highlight(ctx, text)
or component.highlight
if type(column_highlights) == 'string' then
table.insert(highlights, { offset, offset + #text, group = column_highlights })
elseif type(column_highlights) == 'table' then
for _, highlight in ipairs(column_highlights) do
table.insert(highlights, {
offset + highlight[1],
offset + highlight[2],
group = highlight.group,
params = highlight.params,
})
end
end
offset = offset + #text + self.gap
end
end
return highlights
end
return column
|