-
-
Notifications
You must be signed in to change notification settings - Fork 236
Expand file tree
/
Copy pathui.lua
More file actions
737 lines (652 loc) · 26 KB
/
Copy pathui.lua
File metadata and controls
737 lines (652 loc) · 26 KB
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
-----------------------------------------------------------------------------//
-- UI
-----------------------------------------------------------------------------//
local lazy = require("bufferline.lazy")
local utils = lazy.require("bufferline.utils") ---@module "bufferline.utils"
local config = lazy.require("bufferline.config") ---@module "bufferline.config"
local constants = lazy.require("bufferline.constants") ---@module "bufferline.constants"
local highlights = lazy.require("bufferline.highlights") ---@module "bufferline.highlights"
local pick = lazy.require("bufferline.pick") ---@module "bufferline.pick"
local groups = lazy.require("bufferline.groups") ---@module "bufferline.groups"
local diagnostics = lazy.require("bufferline.diagnostics") ---@module "bufferline.diagnostics"
local duplicates = lazy.require("bufferline.duplicates") ---@module "bufferline.duplicates"
local numbers = lazy.require("bufferline.numbers") ---@module "bufferline.numbers"
local custom_area = lazy.require("bufferline.custom_area") ---@module "bufferline.custom_area"
local offset = lazy.require("bufferline.offset") ---@module "bufferline.offset"
local state = lazy.require("bufferline.state") ---@module "bufferline.state"
local M = {}
local api = vim.api
local sep_names = constants.sep_names
local sep_chars = constants.sep_chars
-- string.len counts number of bytes and so the unicode icons are counted
-- larger than their display width. So we use nvim's strwidth
local strwidth = vim.api.nvim_strwidth
local padding = constants.padding
local components = {
id = {
diagnostics = "diagnostics",
name = "name",
icon = "icon",
number = "number",
groups = "groups",
duplicates = "duplicates",
close = "close",
modified = "modified",
pick = "pick",
},
}
----------------------------------------------------------------------------------------------------
-- Hover events
----------------------------------------------------------------------------------------------------
---@param item bufferline.Component?
local function set_hover_state(item)
state.set({ hovered = item })
M.refresh()
end
---@class HoverOpts
---@field cursor_pos integer
---@param _ integer
---@param opts HoverOpts
function M.on_hover_over(_, opts)
local mouse_pos, pos = opts.cursor_pos, state.left_offset_size
for _, item in pairs(state.visible_components) do
-- This value can be incorrect as truncation markers might push things off center
local next_pos = pos + item.length
if mouse_pos >= pos and mouse_pos <= next_pos then return set_hover_state(item) end
pos = next_pos
end
end
function M.on_hover_out() set_hover_state(vim.NIL) end
---@param component bufferline.Segment?
---@param id string
---@return bufferline.Segment?
local function set_id(component, id)
if component then
component.attr = component.attr or {}
component.attr.__id = id
end
return component
end
local function get_id(component) return component and component.attr and component.attr.__id end
-----------------------------------------------------------------------------//
-- Context
-----------------------------------------------------------------------------//
---@type bufferline.RenderContext
local Context = {}
---@param ctx bufferline.RenderContext
---@return bufferline.RenderContext
function Context:new(ctx)
assert(ctx.tab, "A tab view entity is required to create a context")
self.tab = ctx.tab
self.__index = self
return setmetatable(ctx, self)
end
-----------------------------------------------------------------------------//
---@param s bufferline.Segment?
---@return boolean
local function has_text(s)
if s == nil or s.text == nil or s.text == "" then return false end
return true
end
---@param parts bufferline.Segment[]
---@return bufferline.Segment[]
local function filter_invalid(parts)
local result = {}
for _, p in pairs(parts) do
if p ~= nil then result[#result + 1] = p end
end
return result
end
---@param segments bufferline.Segment[]
---@return integer
local function get_component_size(segments)
assert(utils.is_list(segments), "Segments must be a list")
local sum = 0
for _, s in pairs(segments) do
if has_text(s) then sum = sum + strwidth(tostring(s.text)) end
end
return sum
end
local function get_marker_size(count, element_size) return count > 0 and strwidth(tostring(count)) + element_size or 0 end
function M.refresh()
vim.schedule(function() vim.cmd.redrawtabline() end)
end
---Add click action to a component
---@param func_name string
---@param id number
---@param component bufferline.Segment
function M.make_clickable(func_name, id, component)
component.attr = component.attr or {}
component.attr.prefix = "%" .. id .. "@v:lua.___bufferline_private." .. func_name .. "@"
-- the %X works as a closing label. @see :h tabline
component.attr.suffix = "%X"
return component
end
---@class PadSide
---@field size integer
---@field hl string
---@class PadOpts
---@field left PadSide?
---@field right PadSide?
---Add padding to either side of a component
---@param opts PadOpts
---@return bufferline.Segment, bufferline.Segment
local function pad(opts)
opts.left, opts.right = opts.left or {}, opts.right or {}
local left, left_hl = opts.left.size or 0, opts.left.hl or ""
local right, right_hl = opts.right.size or 0, opts.right.hl or left_hl
local left_p, right_p = string.rep(padding, left), string.rep(padding, right)
return { text = left_p, highlight = left_hl }, { text = right_p, highlight = right_hl }
end
---@param options bufferline.Options
---@param hls bufferline.Highlights
---@return bufferline.Segment[]
local function get_tab_close_button(options, hls)
if options.show_close_icon and (#vim.api.nvim_list_tabpages() > 1) then
return {
{
text = padding .. options.close_icon .. padding,
highlight = hls.tab_close.hl_group,
attr = { prefix = "%999X" },
},
}
end
return {}
end
---@param items bufferline.Component[]
---@return bufferline.Section
---@return bufferline.Section
---@return bufferline.Section
local function get_sections(items)
local Section = require("bufferline.models").Section
local current = Section:new()
local before = Section:new()
local after = Section:new()
for _, tab_view in ipairs(items) do
if not tab_view.hidden then
if tab_view:current() then
current:add(tab_view)
elseif current.length == 0 then -- We haven't reached the current buffer yet
before:add(tab_view)
else
after:add(tab_view)
end
end
end
return before, current, after
end
---@param ctx bufferline.RenderContext
---@param length number
---@return bufferline.Segment?, bufferline.Segment?
local function add_space(ctx, length)
local options = config.options
local curr_hl = ctx.current_highlights
local left_size, right_size = 0, 0
local icon = options.buffer_close_icon
-- pad each tab smaller than the max tab size to make it consistent
local difference = options.tab_size - length
if difference > 0 then
local size = math.floor(difference / 2)
left_size, right_size = size + left_size, size + right_size
end
if not options.show_buffer_close_icons then
right_size = right_size > 0 and right_size - strwidth(icon) or right_size
left_size = left_size + strwidth(icon)
end
return pad({
left = { size = left_size, hl = curr_hl.buffer },
right = { size = right_size },
})
end
--- @param buffer bufferline.TabElement
--- @param hl_defs bufferline.Highlights
--- @return bufferline.Segment?
local function get_icon(buffer, hl_defs)
local icon = buffer.icon
local original_hl = buffer.icon_highlight
if not icon or icon == "" then return end
if not original_hl or original_hl == "" then return { text = icon } end
local icon_hl = highlights.set_icon_highlight(buffer:visibility(), hl_defs, original_hl)
return { text = icon, highlight = icon_hl, attr = { text = "%*" } }
end
---Determine if the separator style is one of the slant options
---@param style string
---@return boolean
local function is_slant(style)
return vim.tbl_contains({ sep_names.slant, sep_names.padded_slant, sep_names.slope, sep_names.padded_slope }, style)
end
--- "▍" "░"
--- Reference: https://en.wikipedia.org/wiki/Block_Elements
--- @param focused boolean
--- @param style table | string
local function get_separator(focused, style)
if type(style) == "table" then return focused and style[1] or style[2] end
---@diagnostic disable-next-line: undefined-field
local chars = sep_chars[style] or sep_chars.thin
if is_slant(style) then return chars[1], chars[2] end
return focused and chars[1] or chars[2]
end
--- @param buf_id number
--- @return bufferline.Segment?
local function get_close_icon(buf_id, context)
local options = config.options
if options.hover.enabled and not context.tab:current() and vim.tbl_contains(options.hover.reveal, "close") then
if not state.hovered or state.hovered.id ~= context.tab.id then return end
end
local buffer_close_icon = options.buffer_close_icon
local close_button_hl = context.current_highlights.close_button
if not options.show_buffer_close_icons then return end
return M.make_clickable("handle_close", buf_id, {
text = buffer_close_icon,
highlight = close_button_hl,
})
end
--- @param context bufferline.RenderContext
--- @return bufferline.Segment?
local function add_indicator(context)
local element = context.tab
local hl = config.highlights
local curr_hl = context.current_highlights
local options = config.options
local style = options.separator_style
local symbol, highlight = padding, nil
if is_slant(style) then return { text = symbol, highlight = highlight } end
local is_current = element:current()
symbol = is_current and options.indicator.icon or symbol
highlight = is_current and hl.indicator_selected.hl_group
or element:visible() and hl.indicator_visible.hl_group
or curr_hl.buffer
if options.indicator.style ~= "icon" then return { text = padding, highlight = highlight } end
-- since all non-current buffers do not have an indicator they need
-- to be padded to make up the difference in size
return { text = symbol, highlight = highlight }
end
--- @param context bufferline.RenderContext
--- @return bufferline.Segment?
local function add_icon(context)
local element = context.tab
local options = config.options
if context.is_picking and element.letter then
return pick.component(context)
elseif options.show_buffer_icons and element.icon then
return get_icon(element, config.highlights)
end
end
--- The suffix can be either the modified icon, space to replace the icon if
--- a user has turned them off or the close icon if the element is not currently modified
--- @param context bufferline.RenderContext
--- @return bufferline.Segment?
local function add_suffix(context)
local element = context.tab
local hl = context.current_highlights
local symbol = config.options.modified_icon
-- If the buffer is modified add an icon, if it isn't pad
-- the buffer so it doesn't "jump" when it becomes modified i.e. due
-- to the sudden addition of a new character
local modified = {
text = element.modified and symbol or string.rep(padding, strwidth(symbol)),
highlight = element.modified and hl.modified or nil,
}
local close = get_close_icon(element.id, context)
return not element.modified and close or modified
end
--- TODO: We increment the buffer length by the separator although the final
--- buffer will not have a separator so we are technically off by 1
--- @param context bufferline.RenderContext
--- @return bufferline.Segment?, bufferline.Segment
local function add_separators(context)
local hl = config.highlights
local options = config.options
local style = options.separator_style
local focused = context.tab:current() or context.tab:visible()
local right_sep, left_sep = get_separator(focused, style)
local sep_hl = is_slant(style) and context.current_highlights.separator or hl.separator.hl_group
local left_separator = left_sep and { text = left_sep, highlight = sep_hl } or nil
local right_separator = { text = right_sep, highlight = sep_hl }
return left_separator, right_separator
end
-- if we are enforcing regular tab size then all components will try and fit
-- into the maximum tab size. If not we enforce a minimum tab size
-- and allow components to be larger than the max.
---@param context bufferline.RenderContext
---@return number
local function get_max_length(context)
local modified = config.options.modified_icon
local options = config.options
local element = context.tab
local icon_size = strwidth(element.icon)
local padding_size = strwidth(padding) * 2
local max_length = options.max_name_length
local autosize = not options.truncate_names and not options.enforce_regular_tabs
local name_size = strwidth(context.tab.name)
if autosize and name_size >= max_length then return name_size end
if not options.enforce_regular_tabs then return max_length end
-- estimate the maximum allowed size of a filename given that it will be
-- padded and prefixed with a file icon
return options.tab_size - strwidth(modified) - icon_size - padding_size
end
---@param ctx bufferline.RenderContext
---@return bufferline.Segment
local function get_name(ctx)
local name = utils.truncate_name(ctx.tab.name, get_max_length(ctx))
-- escape filenames that contain "%" as this breaks in statusline patterns
name = name:gsub("%%", "%%%1")
return { text = name, highlight = ctx.current_highlights.buffer }
end
---Create the render function that components need to position their
---separators once rendering calculations are complete
---@param left_separator bufferline.Segment?
---@param right_separator bufferline.Segment?
---@param component bufferline.Segment[]
---@return fun(next: bufferline.Component): bufferline.Segment[]
local function create_renderer(left_separator, right_separator, component)
--- We return a function from render buffer as we do not yet have access to
--- information regarding which buffers will actually be rendered
--- @param next_item bufferline.Component
--- @return string
return function(next_item)
-- if using the non-slanted tab style then we must check if the component is at the end of
-- of a section e.g. the end of a group and if so it should not be wrapped with separators
-- as it can use those of the next item
if not is_slant(config.options.separator_style) and next_item and next_item:is_end() then return component end
if left_separator then
table.insert(component, 1, left_separator)
table.insert(component, right_separator)
return component
end
if next_item then table.insert(component, right_separator) end
return component
end
end
---@param id number
---@return bufferline.Segment
local function tab_click_handler(id) return M.make_clickable("handle_click", id, { attr = { global = true } }) end
---@class SpacingOpts
---@field when any
---@field highlight string
---Create a spacing component that can be dependent on other items in a component
---@param opts SpacingOpts?
---@return bufferline.Segment?
local function spacing(opts)
opts = opts or { when = true }
if not opts.when then return end
return { text = constants.padding, highlight = opts.highlight }
end
---@param trunc_icon string
---@param count_hl string
---@param icon_hl string
---@param count number
---@return bufferline.Segment[]?
local function get_left_trunc_marker(trunc_icon, count_hl, icon_hl, count)
if count > 0 then
return {
{ highlight = icon_hl, text = padding .. trunc_icon },
{ highlight = count_hl, text = padding .. count .. padding },
}
end
end
---@param trunc_icon string
---@param count_hl string
---@param icon_hl string
---@param count number
---@return bufferline.Segment[]?
local function get_right_trunc_marker(trunc_icon, count_hl, icon_hl, count)
if count > 0 then
return {
{ highlight = count_hl, text = padding .. count .. padding },
{ highlight = icon_hl, text = trunc_icon .. padding },
}
end
end
---@param tab_indicators table<string, bufferline.Segment>
---@param options bufferline.Options
---@return bufferline.Segment[]
---@return integer
local function get_tab_indicator(tab_indicators, options)
local items, length = {}, 0
if not options.show_tab_indicators or #tab_indicators <= 1 then return items, length end
for _, tab in ipairs(tab_indicators) do
local component = tab.component
table.insert(items, component)
length = length + get_component_size(component)
end
return items, length
end
--- @param current_state bufferline.State
--- @param element bufferline.TabElement
--- @return bufferline.TabElement
function M.element(current_state, element)
local curr_hl = highlights.for_element(element)
local ctx = Context:new({
tab = element,
current_highlights = curr_hl,
is_picking = current_state.is_picking,
})
local duplicate_prefix = duplicates.component(ctx)
local group_item = element.group and groups.component(ctx) or nil
local diagnostic = diagnostics.component(ctx)
local icon = add_icon(ctx)
local number_item = numbers.component(ctx)
local suffix = add_suffix(ctx)
local indicator = add_indicator(ctx)
local left, right = add_separators(ctx)
local name = get_name(ctx)
-- Guess how much space there will for padding based on the buffer's name
local name_size = get_component_size({ duplicate_prefix, name, spacing(), icon, suffix })
local left_space, right_space = add_space(ctx, name_size)
local component = filter_invalid({
tab_click_handler(element.id),
indicator,
left_space,
set_id(number_item, components.id.number),
spacing({ when = number_item }),
set_id(icon, components.id.icon),
spacing({ when = icon }),
set_id(group_item, components.id.groups),
spacing({ when = group_item }),
set_id(duplicate_prefix, components.id.duplicates),
set_id(name, components.id.name),
spacing({ when = name, highlight = curr_hl.buffer }),
set_id(diagnostic, components.id.diagnostics),
spacing({ when = diagnostic and #diagnostic.text > 0 }),
right_space,
suffix,
spacing({ when = suffix }),
})
element.component = create_renderer(left, right, component)
-- NOTE: we must count the size of the separators here although we do not
-- add them yet, since by the time they are added the component will already have rendered
element.length = get_component_size(filter_invalid({ left, right, unpack(component) }))
return element
end
-- The extends field means that the components highlights should be applied
-- to another with a matching ID. This function does an initial scan of the component
-- parts and updates the highlights for any part that has an extension.
---@param component bufferline.Segment[]
---@return bufferline.Segment[]
local function extend_highlight(component)
local locations, extension_map = {}, {}
for index, part in pairs(component) do
local id = get_id(part)
if id then locations[id] = index end
local extends = vim.tbl_get(part, "attr", "extends")
if extends then
for _, target in pairs(extends) do
extension_map[target.id] = target.highlight or part.highlight
end
end
end
for id, hl in pairs(extension_map) do
local target = component[locations[id]]
if target then target.highlight = hl end
end
return component
end
--- Takes a list of Segments of the shape {text = <text>, highlight = <hl>, attr = <table>}
--- and converts them into an nvim tabline format string i.e. `%#HL#text`. It handles cases
--- like applying global or local attributes like click handlers. As well as extending highlights
---@param component bufferline.Segment[]
local function to_tabline_str(component)
component = component or {}
local str = {}
local globals = {}
extend_highlight(component)
for _, part in ipairs(component) do
local attr = part.attr
if attr and attr.global then table.insert(globals, { attr.prefix or "", attr.suffix or "" }) end
local hl = highlights.hl(part.highlight)
table.insert(str, {
hl,
((attr and not attr.global) and attr.prefix or ""),
(part.text or ""),
((attr and not attr.global) and attr.suffix or ""),
})
end
for _, attr in ipairs(globals) do
table.insert(str, 1, attr[1])
table.insert(str, #str + 1, attr[1])
end
return table.concat(utils.tbl_flatten(str))
end
--- PREREQUISITE: active buffer always remains in view
--- 1. Find amount of available space in the window
--- 2. Find the amount of space the bufferline will take up
--- 3. If the bufferline will be too long remove one tab from the before or after
--- section
--- 4. Re-check the size, if still too long truncate recursively till it fits
--- 5. Add the number of truncated buffers as an indicator
---@param before bufferline.Section
---@param current bufferline.Section
---@param after bufferline.Section
---@param available_width number
---@param marker table
---@param visible bufferline.Component[]
---@return bufferline.Segment[][]
---@return table
---@return bufferline.Buffer[]
local function truncate(before, current, after, available_width, marker, visible)
visible = visible or {}
local left_trunc_marker = get_marker_size(marker.left_count, marker.left_element_size)
local right_trunc_marker = get_marker_size(marker.right_count, marker.right_element_size)
local markers_length = left_trunc_marker + right_trunc_marker
local total_length = before.length + current.length + after.length + markers_length
if available_width >= total_length then
local items = {}
visible = utils.merge_lists(before.items, current.items, after.items)
for index, item in ipairs(visible) do
table.insert(items, item.component(visible[index + 1]))
end
return items, marker, visible
-- if we aren't even able to fit the current buffer into the
-- available space that means the window is really narrow
-- so don't show anything
elseif available_width < current.length then
return {}, marker, visible
else
if before.length >= after.length then
before:drop(1)
marker.left_count = marker.left_count + 1
else
after:drop(#after.items)
marker.right_count = marker.right_count + 1
end
-- drop the markers if the window is too narrow
-- this assumes we have dropped both before and after
-- sections since if the space available is this small
-- we have likely removed these
if (current.length + markers_length) > available_width then
marker.left_count = 0
marker.right_count = 0
end
return truncate(before, current, after, available_width, marker, visible)
end
end
---@param list bufferline.Segment[][]
local function join(list)
local str = ""
for _, item in pairs(list) do
str = str .. to_tabline_str(item)
end
return str
end
--- Get the width of statusline/tabline format string
---@vararg string
---@return integer
local function statusline_str_width(...)
local str = table.concat({ ... }, "")
return api.nvim_eval_statusline(str, { use_tabline = true }).width
end
---@class BufferlineTablineData
---@field str string
---@field left_offset_size integer
---@field right_offset_size integer
---@field segments bufferline.Segment[][]
---@field visible_components bufferline.TabElement[]
--- TODO: All components should return Segment[] that are then combined in one go into a tabline
--- @param items bufferline.Component[]
--- @param tab_indicators bufferline.Segment[]
--- @return BufferlineTablineData
function M.tabline(items, tab_indicators)
local options = config.options
local hl = config.highlights
local right_align = { { highlight = hl.fill.hl_group, text = "%=" } }
local tab_close_button = get_tab_close_button(options, hl)
local tab_close_button_length = get_component_size(tab_close_button)
local tab_indicator_segments, tab_indicator_length = get_tab_indicator(tab_indicators, options)
-- NOTE: this estimates the size of the truncation marker as we don't know how big it will be yet
local left_trunc_icon = options.left_trunc_marker
local right_trunc_icon = options.right_trunc_marker
local max_padding = string.rep(padding, 2)
local left_element_size = utils.measure(max_padding, left_trunc_icon, max_padding)
local right_element_size = utils.measure(max_padding, right_trunc_icon, max_padding)
local offsets = offset.get()
local custom_area_size, left_area, right_area = custom_area.get()
local available_width = vim.o.columns
- custom_area_size
- offsets.total_size
- tab_indicator_length
- tab_close_button_length
local before, current, after = get_sections(items)
local segments, marker, visible_components = truncate(before, current, after, available_width, {
left_count = 0,
right_count = 0,
left_element_size = left_element_size,
right_element_size = right_element_size,
})
local marker_hl = hl.trunc_marker.hl_group
local left_marker = get_left_trunc_marker(left_trunc_icon, marker_hl, marker_hl, marker.left_count)
local right_marker = get_right_trunc_marker(right_trunc_icon, marker_hl, marker_hl, marker.right_count)
local core = join(
utils.merge_lists(
{ left_marker },
segments,
{ right_marker, right_align },
tab_indicator_segments,
{ tab_close_button }
)
)
--- NOTE: the custom areas are essentially mini tablines a user can define so they can't
--- be safely converted to segments so they are concatenated to string and joined with
--- the rest of the tabline
local tabline = utils.join(offsets.left, left_area, core, right_area, offsets.right)
local left_offset_size = offsets.left_size + statusline_str_width(left_area)
local left_marker_size = left_marker and get_component_size(left_marker) or 0
local right_offset_size = offsets.right_size + statusline_str_width(right_area)
local right_marker_size = right_marker and get_component_size(right_marker) or 0
return {
str = tabline,
segments = segments,
visible_components = visible_components,
right_offset_size = right_offset_size + right_marker_size,
left_offset_size = left_offset_size + left_marker_size,
}
end
M.get_component_size = get_component_size
M.components = components
if utils.is_test() then
M.to_tabline_str = to_tabline_str
M.set_id = set_id
M.add_indicator = add_indicator
M.get_name = get_name
end
return M