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
|
" MIT License. Copyright (c) 2013-2018 Bailey Ling et al.
" vim: et ts=2 sts=2 sw=2
scriptencoding utf-8
let s:prototype = {}
" Set the point in the tabline where the builder should insert the titles.
"
" Subsequent calls will overwrite the previous ones, so only the last call
" determines to location to insert titles.
"
" NOTE: The titles are not inserted until |build| is called, so that the
" remaining contents of the tabline can be taken into account.
"
" Callers should define at least |get_title| and |get_group| on the host
" object before calling |build|.
function! s:prototype.insert_titles(current, first, last) dict
let self._first_title = a:first " lowest index
let self._last_title = a:last " highest index
let self._left_title = a:current " next index to add on the left
let self._right_title = a:current + 1 " next index to add on the right
let self._left_position = self.get_position() " left end of titles
let self._right_position = self._left_position " right end of the titles
endfunction
" Insert a title for entry number |index|, of group |group| at position |pos|,
" if there is space for it. Returns 1 if it is inserted, 0 otherwise
"
" |force| inserts the title even if there isn't enough space left for it.
" |sep_size| adjusts the size change that the title is considered to take up,
" to account for changes to the separators
"
" The title is defined by |get_title| on the hosting object, called with
" |index| as its only argument.
" |get_pretitle| and |get_posttitle| may be defined on the host object to
" insert some formatting before or after the title. These should be 0-width.
"
" This method updates |_right_position| and |_remaining_space| on the host
" object, if the title is inserted.
function! s:prototype.try_insert_title(index, group, pos, sep_size, force) dict
let title = self.get_title(a:index)
let title_size = s:tabline_evaluated_length(title) + a:sep_size
if a:force || self._remaining_space >= title_size
let pos = a:pos
if has_key(self, "get_pretitle")
call self.insert_raw(self.get_pretitle(a:index), pos)
let self._right_position += 1
let pos += 1
endif
call self.insert_section(a:group, title, pos)
let self._right_position += 1
let pos += 1
if has_key(self, "get_posttitle")
call self.insert_raw(self.get_posttitle(a:index), pos)
let self._right_position += 1
let pos += 1
endif
let self._remaining_space -= title_size
return 1
endif
return 0
endfunction
function! s:get_separator_change(new_group, old_group, end_group, sep_size, alt_sep_size)
return s:get_separator_change_with_end(a:new_group, a:old_group, a:end_group, a:end_group, a:sep_size, a:alt_sep_size)
endfunction
" Compute the change in size of the tabline caused by separators
"
" This should be kept up-to-date with |s:get_transitioned_seperator| and
" |s:get_separator| in autoload/airline/builder.vim
function! s:get_separator_change_with_end(new_group, old_group, new_end_group, old_end_group, sep_size, alt_sep_size)
let sep_change = 0
if !empty(a:new_end_group) " Separator between title and the end
let sep_change += airline#builder#should_change_group(a:new_group, a:new_end_group) ? a:sep_size : a:alt_sep_size
endif
if !empty(a:old_group) " Separator between the title and the one adjacent
let sep_change += airline#builder#should_change_group(a:new_group, a:old_group) ? a:sep_size : a:alt_sep_size
if !empty(a:old_end_group) " Remove mis-predicted separator
let sep_change -= airline#builder#should_change_group(a:old_group, a:old_end_group) ? a:sep_size : a:alt_sep_size
endif
endif
return sep_change
endfunction
" This replaces the build function of the |airline#builder#new| object, to
" insert titles as specified by the last call to |insert_titles| before
" passing to the original build function.
"
" Callers should define at least |get_title| and |get_group| on the host
" object if |insert_titles| has been called on it.
function! s:prototype.build() dict
if has_key(self, '_left_position') && self._first_title <= self._last_title
let self._remaining_space = &columns - s:tabline_evaluated_length(self._build())
let center_active = get(g:, 'airline#extensions#tabline#center_active', 0)
let sep_size = s:tabline_evaluated_length(self._context.left_sep)
let alt_sep_size = s:tabline_evaluated_length(self._context.left_alt_sep)
let outer_left_group = airline#builder#get_prev_group(self._sections, self._left_position)
let outer_right_group = airline#builder#get_next_group(self._sections, self._right_position)
let overflow_marker = get(g:, 'airline#extensions#tabline#overflow_marker', g:airline_symbols.ellipsis)
let overflow_marker_size = s:tabline_evaluated_length(overflow_marker)
" Allow space for the markers before we begin filling in titles.
if self._left_title > self._first_title
let self._remaining_space -= overflow_marker_size +
\ s:get_separator_change(self.overflow_group, "", outer_left_group, sep_size, alt_sep_size)
endif
if self._left_title < self._last_title
let self._remaining_space -= overflow_marker_size +
\ s:get_separator_change(self.overflow_group, "", outer_right_group, sep_size, alt_sep_size)
endif
" Add the current title
let group = self.get_group(self._left_title)
if self._left_title == self._first_title
let sep_change = s:get_separator_change(group, "", outer_left_group, sep_size, alt_sep_size)
else
let sep_change = s:get_separator_change(group, "", self.overflow_group, sep_size, alt_sep_size)
endif
if self._left_title == self._last_title
let sep_change += s:get_separator_change(group, "", outer_right_group, sep_size, alt_sep_size)
else
let sep_change += s:get_separator_change(group, "", self.overflow_group, sep_size, alt_sep_size)
endif
let left_group = group
let right_group = group
let self._left_title -=
\ self.try_insert_title(self._left_title, group, self._left_position, sep_change, 1)
if get(g:, 'airline#extensions#tabline#current_first', 0)
" always have current title first
let self._left_position += 1
endif
if !center_active && self._right_title <= self._last_title
" Add the title to the right
let group = self.get_group(self._right_title)
if self._right_title == self._last_title
let sep_change = s:get_separator_change_with_end(group, right_group, outer_right_group, self.overflow_group, sep_size, alt_sep_size) - overflow_marker_size
else
let sep_change = s:get_separator_change(group, right_group, self.overflow_group, sep_size, alt_sep_size)
endif
let right_group = group
let self._right_title +=
\ self.try_insert_title(self._right_title, group, self._right_position, sep_change, 1)
endif
while self._remaining_space > 0
let done = 0
if self._left_title >= self._first_title
" Insert next title to the left
let group = self.get_group(self._left_title)
if self._left_title == self._first_title
let sep_change = s:get_separator_change_with_end(group, left_group, outer_left_group, self.overflow_group, sep_size, alt_sep_size) - overflow_marker_size
else
let sep_change = s:get_separator_change(group, left_group, self.overflow_group, sep_size, alt_sep_size)
endif
let left_group = group
let done = self.try_insert_title(self._left_title, group, self._left_position, sep_change, 0)
let self._left_title -= done
endif
" If center_active is set, this |if| operates as an independent |if|,
" otherwise as an |elif|.
if self._right_title <= self._last_title && (center_active || !done)
" Insert next title to the right
let group = self.get_group(self._right_title)
if self._right_title == self._last_title
let sep_change = s:get_separator_change_with_end(group, right_group, outer_right_group, self.overflow_group, sep_size, alt_sep_size) - overflow_marker_size
else
let sep_change = s:get_separator_change(group, right_group, self.overflow_group, sep_size, alt_sep_size)
endif
let right_group = group
let done = self.try_insert_title(self._right_title, group, self._right_position, sep_change, 0)
let self._right_title += done
endif
if !done
break
endif
endwhile
if self._left_title >= self._first_title
if get(g:, 'airline#extensions#tabline#current_first', 0)
let self._left_position -= 1
endif
call self.insert_section(self.overflow_group, overflow_marker, self._left_position)
let self._right_position += 1
endif
if self._right_title <= self._last_title
call self.insert_section(self.overflow_group, overflow_marker, self._right_position)
endif
endif
return self._build()
endfunction
let s:prototype.overflow_group = 'airline_tab'
" Extract the text content a tabline will render. (Incomplete).
"
" See :help 'statusline' for the list of fields.
function! s:evaluate_tabline(tabline)
let tabline = a:tabline
let tabline = substitute(tabline, '%{\([^}]\+\)}', '\=eval(submatch(1))', 'g')
let tabline = substitute(tabline, '%#[^#]\+#', '', 'g')
let tabline = substitute(tabline, '%(\([^)]\+\)%)', '\1', 'g')
let tabline = substitute(tabline, '%\d\+[TX]', '', 'g')
let tabline = substitute(tabline, '%=', '', 'g')
let tabline = substitute(tabline, '%\d*\*', '', 'g')
if has('tablineat')
let tabline = substitute(tabline, '%@[^@]\+@', '', 'g')
endif
return tabline
endfunction
function! s:tabline_evaluated_length(tabline)
return airline#util#strchars(s:evaluate_tabline(a:tabline))
endfunction
function! airline#extensions#tabline#builder#new(context)
let builder = airline#builder#new(a:context)
let builder._build = builder.build
call extend(builder, s:prototype, 'force')
return builder
endfunction
|