aboutsummaryrefslogtreecommitdiff
path: root/.vim/autoload/airline/extensions/tabline/builder.vim
blob: 20964b15865ef5d68ee49f58f446c29ca1ff25a6 (plain)
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