File: toc.vim

package info (click to toggle)
vim-vimtex 2.17-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 8,844 kB
  • sloc: makefile: 360; python: 103
file content (317 lines) | stat: -rw-r--r-- 8,291 bytes parent folder | download | duplicates (2)
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
" VimTeX - LaTeX plugin for Vim
"
" Maintainer: Karl Yngve LervÄg
" Email:      karl.yngve@gmail.com
"
"
" Parses tex project for ToC-like entries.  Each entry is a dictionary
" similar to the following:
"
"   entry = {
"     title  : "Some title",
"     number : "3.1.2",
"     file   : /path/to/file.tex,
"     line   : 142,
"     rank   : cumulative line number,
"     level  : 2,
"     type   : [content | label | todo | include],
"     link   : [0 | 1],
"   }
"

function! vimtex#parser#toc#parse(file) abort " {{{1
  let l:entries = []
  let l:content = vimtex#parser#tex(a:file)
  let l:matchers = vimtex#parser#toc#get_matchers()

  let l:max_level = 0
  for [l:file, l:lnum, l:line] in l:content
    if l:line =~# l:matchers.d['section'].re
      let l:max_level = max([
            \ l:max_level,
            \ vimtex#parser#toc#level(l:matchers.d['section'].level(l:line)),
            \])
    endif
  endfor

  call s:level.reset('preamble', l:max_level)

  " No more parsing if there is no content
  if empty(l:content) | return l:entries | endif

  "
  " Begin parsing LaTeX files
  "
  let l:context = {}
  let l:lnum_total = 0
  let l:matcher_list = l:matchers.preamble
  for [l:file, l:lnum, l:line] in l:content
    let l:lnum_total += 1

    " Handle multi-line entries
    if has_key(l:context, 'continue')
      call extend(l:context, {
            \ 'line': l:line,
            \ 'lnum': l:lnum,
            \ 'lnum_total': l:lnum_total,
            \ 'entry': get(l:entries, -1, {}),
            \})
      call l:matchers.d[l:context.continue].continue(l:context)
      continue
    endif

    let l:context = {
          \ 'file': l:file,
          \ 'line': l:line,
          \ 'lnum': l:lnum,
          \ 'lnum_total': l:lnum_total,
          \ 'level': s:level,
          \ 'max_level': l:max_level,
          \ 'entry': get(l:entries, -1, {}),
          \ 'num_entries': len(l:entries),
          \}

    " Detect end of preamble
    if s:level.preamble && l:line =~# '\v^\s*\\begin\{document\}'
      let s:level.preamble = 0
      let l:matcher_list = l:matchers.content
      continue
    endif

    " Apply prefilter - this gives considerable speedup for large documents
    if l:line !~# l:matchers.prefilter | continue | endif

    " Apply the matchers
    for l:matcher in l:matcher_list
      if l:line =~# l:matcher.re
        let l:entry = l:matcher.get_entry(l:context)

        if type(l:entry) == v:t_list
          call extend(l:entries, l:entry)
        elseif !empty(l:entry)
          call add(l:entries, l:entry)
        endif
      endif
    endfor
  endfor

  for l:matcher in l:matchers.all
    try
      call l:matcher.filter(l:entries)
    catch /E716/
    endtry
  endfor

  return l:entries
endfunction

" }}}1
function! vimtex#parser#toc#get_topmatters() abort " {{{1
  let l:topmatters = s:level.frontmatter
  let l:topmatters += s:level.mainmatter
  let l:topmatters += s:level.appendix
  let l:topmatters += s:level.backmatter

  for l:level in get(s:level, 'old', [])
    let l:topmatters += l:level.frontmatter
    let l:topmatters += l:level.mainmatter
    let l:topmatters += l:level.appendix
    let l:topmatters += l:level.backmatter
  endfor

  return l:topmatters
endfunction

" }}}1
function! vimtex#parser#toc#get_matchers() abort " {{{1
  let l:matchers = {
        \ 'all': [],
        \ 'preamble': [],
        \ 'content': [],
        \ 'd': {},
        \}

  " Collect all matchers
  for l:name in s:matchers
    let l:matcher = extend(
          \ vimtex#parser#toc#{l:name}#new(),
          \ get(g:vimtex_toc_config_matchers, l:name, {}))
    let l:matcher.name = l:name
    call add(l:matchers.all, l:matcher)
  endfor
  let l:matchers.all += g:vimtex_toc_custom_matchers

  " Remove disabled matchers
  call filter(l:matchers.all, {_, x -> !get(x, 'disable')})

  " Add dictionary that gives access to specific matchers
  let l:counter = 1
  for l:matcher in l:matchers.all
    if !has_key(l:matcher, 'name')
      let l:matcher.name = 'custom' . l:counter
      let l:counter += 1
    endif

    let l:matchers.d[l:matcher.name] = l:matcher
  endfor

  " Sort the matchers by priority
  function! s:sort_by_priority(d1, d2) abort
    let l:p1 = get(a:d1, 'priority')
    let l:p2 = get(a:d2, 'priority')
    return l:p1 >= l:p2 ? l:p1 > l:p2 : -1
  endfunction
  call sort(l:matchers.all, function('s:sort_by_priority'))

  " Further processing of the matchers
  for l:matcher in l:matchers.all
    " Initialize matchers if relevant
    try
      call l:matcher.init()
    catch /E716/
    endtry

    " Ensure the matcher have 'get_entry'
    if !has_key(l:matcher, 'get_entry')
      function! l:matcher.get_entry(context) abort dict
        return {
              \ 'title'  : self.title,
              \ 'number' : '',
              \ 'file'   : a:context.file,
              \ 'line'   : a:context.lnum,
              \ 'rank'   : a:context.lnum_total,
              \ 'level'  : 0,
              \ 'type'   : 'content',
              \}
      endfunction
    endif

    " Populate the 'preamble' and 'content' lists
    if get(l:matcher, 'in_preamble')
      call add(l:matchers.preamble, l:matcher)
    endif
    if get(l:matcher, 'in_content', 1)
      call add(l:matchers.content, l:matcher)
    endif
  endfor

  " Populate the prefilter
  let l:cmds = []
  let l:re = ''
  for l:matcher in l:matchers.all
    let l:cmds += get(l:matcher, 'prefilter_cmds', [])
    if has_key(l:matcher, 'prefilter_re')
      let l:re .= '|' . l:matcher.prefilter_re
    endif
  endfor
  let l:cmds = vimtex#util#uniq_unsorted(l:cmds)
  let l:matchers.prefilter = '\v\\%(' . join(l:cmds, '|') . ')' . l:re

  return l:matchers
endfunction

let s:matchers = map(
      \ glob(expand('<sfile>:r') . '/*.vim', 0, 1),
      \ { _, x -> fnamemodify(x, ':t:r') })

" }}}1
function! vimtex#parser#toc#level(level) abort " {{{1
  return s:sec_to_value[a:level]
endfunction

let s:sec_to_value = {
      \ '_': 0,
      \ 'subparagraph': 1,
      \ 'paragraph': 2,
      \ 'subsubsubsubsection': 3,
      \ 'subsubsubsection': 4,
      \ 'subsubsection': 5,
      \ 'subsection': 6,
      \ 'section': 7,
      \ 'chapter': 8,
      \ 'part': 9,
      \}

" }}}1

"
" Section level counter
"
let s:level = get(s:, 'level', {})
function! s:level.reset(part, level) abort dict " {{{1
  if a:part ==# 'preamble'
    let self.old = []
  else
    let self.old += [copy(self)]
  endif

  let self.preamble = 0
  let self.frontmatter = 0
  let self.mainmatter = 0
  let self.appendix = 0
  let self.backmatter = 0
  let self.part = 0
  let self.chapter = 0
  let self.section = 0
  let self.subsection = 0
  let self.subsubsection = 0
  let self.subsubsubsection = 0
  let self.paragraph = 0
  let self.subparagraph = 0
  let self.current = a:level
  let self[a:part] = 1
endfunction

" }}}1
function! s:level.increment(level) abort dict " {{{1
  let self.current = vimtex#parser#toc#level(a:level)

  let self.part_toggle = 0

  if a:level ==# 'part'
    let self.part += 1
    let self.part_toggle = 1
  elseif a:level ==# 'chapter'
    let self.chapter += 1
    let self.section = 0
    let self.subsection = 0
    let self.subsubsection = 0
    let self.subsubsubsection = 0
    let self.paragraph = 0
    let self.subparagraph = 0
  elseif a:level ==# 'section'
    let self.section += 1
    let self.subsection = 0
    let self.subsubsection = 0
    let self.subsubsubsection = 0
    let self.paragraph = 0
    let self.subparagraph = 0
  elseif a:level ==# 'subsection'
    let self.subsection += 1
    let self.subsubsection = 0
    let self.subsubsubsection = 0
    let self.paragraph = 0
    let self.subparagraph = 0
  elseif a:level ==# 'subsubsection'
    let self.subsubsection += 1
    let self.subsubsubsection = 0
    let self.paragraph = 0
    let self.subparagraph = 0
  elseif a:level ==# 'subsubsubsection'
    let self.subsubsubsection += 1
    let self.paragraph = 0
    let self.subparagraph = 0
  elseif a:level ==# 'paragraph'
    let self.paragraph += 1
    let self.subparagraph = 0
  elseif a:level ==# 'subparagraph'
    let self.subparagraph += 1
  endif
endfunction

" }}}1
function! s:level.set_current(level) abort dict " {{{1
  let self.current = vimtex#parser#toc#level(a:level)
endfunction

" }}}1