Skip to content

Commit ea3e801

Browse files
committed
merge: parse options of packages and documentclass
refer: #3181
2 parents 22d9ae5 + f17afa3 commit ea3e801

File tree

5 files changed

+216
-24
lines changed

5 files changed

+216
-24
lines changed

autoload/vimtex/state/class.vim

Lines changed: 76 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,15 @@ function! vimtex#state#class#new(opts) abort " {{{1
3434
\ ? vimtex#parser#preamble(l:new.tex, {'root' : l:new.root})
3535
\ : []
3636

37-
let l:new.documentclass = s:parse_documentclass(l:preamble)
38-
let l:new.packages = s:parse_packages(l:preamble)
39-
let l:new.graphicspath = s:parse_graphicspath(l:preamble, l:new.root)
37+
" Create single-line preamble-string without comments
38+
let l:preamble_joined = join(
39+
\ map(copy(l:preamble), {_, x -> substitute(x, '\\\@<!%.*', '', '')}),
40+
\ '')
41+
42+
let [l:new.documentclass, l:new.documentclass_options] =
43+
\ s:parse_documentclass(l:preamble_joined)
44+
let l:new.packages = s:parse_packages(l:preamble_joined)
45+
let l:new.graphicspath = s:parse_graphicspath(l:preamble_joined, l:new.root)
4046
let l:new.glossaries = s:parse_glossaries(
4147
\ l:preamble,
4248
\ l:new.root,
@@ -74,6 +80,17 @@ function! s:vimtex.__pprint() abort dict " {{{1
7480
call add(l:items, ['document class', self.documentclass])
7581
endif
7682

83+
if exists('self.documentclass_options')
84+
let l:string = join(map(sort(keys(self.documentclass_options)),
85+
\ {_, key -> key .. "=" .. (
86+
\ self.documentclass_options[key] == v:true ? 'true'
87+
\ : self.documentclass_options[key] == v:false ? 'false'
88+
\ : self.documentclass_options[key]
89+
\ )}
90+
\))
91+
call add(l:items, ['document class options', l:string])
92+
endif
93+
7794
if !empty(self.packages)
7895
call add(l:items, ['packages', join(sort(keys(self.packages)))])
7996
endif
@@ -193,38 +210,43 @@ endfunction
193210
" }}}1
194211

195212

196-
function! s:parse_documentclass(preamble) abort " {{{1
197-
let l:preamble_lines = filter(copy(a:preamble), {_, x -> x !~# '^\s*%'})
198-
return matchstr(join(l:preamble_lines, ''),
213+
function! s:parse_documentclass(preamble_joined) abort " {{{1
214+
let l:documentclass = matchstr(
215+
\ a:preamble_joined,
199216
\ '\\documentclass[^{]*{\zs[^}]\+\ze}')
217+
218+
let l:option_string = matchstr(
219+
\ a:preamble_joined,
220+
\ '\\documentclass[^\[]*\[\zs[^\]]\+\ze\]')
221+
let l:options = s:parse_optionlist(l:option_string)
222+
223+
return [l:documentclass, l:options]
200224
endfunction
201225

202226
" }}}1
203-
function! s:parse_packages(preamble) abort " {{{1
204-
let l:usepackages = filter(copy(a:preamble),
205-
\ 'v:val =~# ''\v%(usep|RequireP)ackage''')
206-
let l:pat = g:vimtex#re#not_comment . g:vimtex#re#not_bslash
207-
\ . '\v\\%(usep|RequireP)ackage\s*%(\[[^[\]]*\])?\s*\{\s*\zs%([^{}]+)\ze\s*\}'
208-
call map(l:usepackages, {_, x -> split(matchstr(x, l:pat), '\s*,\s*')})
209-
210-
let l:parsed = {}
211-
for l:packages in l:usepackages
212-
for l:package in l:packages
213-
let l:parsed[l:package] = {}
227+
function! s:parse_packages(preamble_joined) abort " {{{1
228+
" Regex pattern:
229+
" - Match contains package name(s)
230+
" - First submatch contains package options
231+
let l:pat = g:vimtex#re#not_comment .. g:vimtex#re#not_bslash
232+
\ .. '\v\\%(usep|RequireP)ackage\s*%(\[([^[\]]*)\])?\s*\{\s*\zs%([^{}]+\S)\ze\s*\}'
233+
234+
let l:packages = {}
235+
for l:match in matchstrlist([a:preamble_joined], pat, #{submatches: v:true})
236+
let l:new_packages = map(split(l:match.text, ','), {_, x -> trim(x)})
237+
let l:options = s:parse_optionlist(l:match.submatches[0])
238+
for l:pkg in l:new_packages
239+
let l:packages[l:pkg] = l:options
214240
endfor
215241
endfor
216242

217-
return l:parsed
243+
return l:packages
218244
endfunction
219245

220246
" }}}1
221-
function! s:parse_graphicspath(preamble, root) abort " {{{1
222-
" Combine the preamble as one long string of commands
223-
let l:preamble = join(map(copy(a:preamble),
224-
\ {_, x -> substitute(x, '\\\@<!%.*', '', '')}))
225-
247+
function! s:parse_graphicspath(preamble_joined, root) abort " {{{1
226248
" Extract the graphicspath command from this string
227-
let l:graphicspath = matchstr(l:preamble,
249+
let l:graphicspath = matchstr(a:preamble_joined,
228250
\ g:vimtex#re#not_bslash
229251
\ . '\\graphicspath\s*\{\s*\{\s*\zs.{-}\ze\s*\}\s*\}'
230252
\)
@@ -297,3 +319,33 @@ function! s:gather_sources(texfile, root) abort " {{{1
297319
endfunction
298320

299321
" }}}1
322+
323+
function! s:parse_optionlist(string) abort " {{{1
324+
let l:options = {}
325+
for l:element in map(split(a:string, ',', v:true), {_, x -> trim(x)})
326+
if l:element == ''
327+
" Empty option
328+
continue
329+
elseif l:element =~# '='
330+
" Key-value option
331+
let [l:key, l:value] = map(split(l:element, '='), {_, x -> trim(x)})
332+
333+
if l:value ==? 'true'
334+
let l:options[l:key] = v:true
335+
elseif l:value ==? 'false'
336+
let l:options[l:key] = v:false
337+
else
338+
let l:options[l:key] = l:value
339+
endif
340+
else
341+
" Key-only option
342+
let l:options[l:element] = v:true
343+
endif
344+
endfor
345+
346+
return l:options
347+
endfunction
348+
349+
" }}}1
350+
351+
" vim: fdm=marker
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
% Leading comments here
2+
3+
\documentclass[% Options of scrbook
4+
%
5+
% draft,
6+
fontsize=12pt,
7+
% smallheadings,
8+
headings=big,
9+
english,
10+
paper=a4,
11+
twoside,
12+
open=right,
13+
DIV=14,
14+
BCOR=20mm,
15+
headinclude=false,
16+
footinclude=false,
17+
mpinclude=false,
18+
% pagesize,
19+
titlepage,
20+
parskip=half,
21+
headsepline,
22+
chapterprefix=false,
23+
appendixprefix=Appendix,
24+
appendixwithprefixline=true,
25+
bibliography=totoc,
26+
toc=graduated,
27+
numbers=noenddot,
28+
]{scrbook}
29+
30+
\begin{document}
31+
32+
Hello, world!
33+
34+
\end{document}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
set nocompatible
2+
set runtimepath^=../..
3+
filetype plugin on
4+
5+
nnoremap q :qall!<cr>
6+
7+
call vimtex#log#set_silent()
8+
9+
silent edit test_parse_documentclass_options.tex
10+
11+
if empty($INMAKE) | finish | endif
12+
13+
call assert_equal('scrbook', b:vimtex.documentclass)
14+
15+
let s:options = {
16+
\ 'fontsize': '12pt',
17+
\ 'headings': 'big',
18+
\ 'english': v:true,
19+
\ 'paper': 'a4',
20+
\ 'twoside': v:true,
21+
\ 'open': 'right',
22+
\ 'DIV': '14',
23+
\ 'BCOR': '20mm',
24+
\ 'headinclude': v:false,
25+
\ 'footinclude': v:false,
26+
\ 'mpinclude': v:false,
27+
\ 'titlepage': v:true,
28+
\ 'parskip': 'half',
29+
\ 'headsepline': v:true,
30+
\ 'chapterprefix': v:false,
31+
\ 'appendixprefix': 'Appendix',
32+
\ 'appendixwithprefixline': v:true,
33+
\ 'bibliography': 'totoc',
34+
\ 'toc': 'graduated',
35+
\ 'numbers': 'noenddot',
36+
\ }
37+
call assert_equal(s:options, b:vimtex.documentclass_options)
38+
39+
call vimtex#test#finished()
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
\RequirePackage[debrief]{silence}
2+
\documentclass{article}
3+
4+
\usepackage[main=german,english]{babel}
5+
6+
\usepackage[acronyms]{glossaries}
7+
\usepackage[record,style=long]{glossaries-extra}
8+
9+
\usepackage[% comment
10+
backend=biber,
11+
style=numeric-comp,
12+
maxcitenames=99,
13+
% doi=false, % commented option
14+
url=false,
15+
giveninits=true,
16+
]{biblatex}
17+
18+
\usepackage[notes, useibid]{biblatex-chicago} % with space
19+
20+
% Issue in VimTeX 2.16 from January 2025:
21+
% Following multi-line syntax of \usepackage is not detected.
22+
\usepackage{
23+
amsmath,
24+
tikz
25+
}
26+
27+
% Invalid syntax but parsed anyway
28+
\usepackage[draft]{package1, package2} % packages with a shared option
29+
30+
\begin{document}
31+
32+
Hello, world!
33+
34+
\end{document}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
set nocompatible
2+
set runtimepath^=../..
3+
filetype plugin on
4+
5+
nnoremap q :qall!<cr>
6+
7+
call vimtex#log#set_silent()
8+
9+
silent edit test_parse_package_options.tex
10+
11+
if empty($INMAKE) | finish | endif
12+
13+
let s:packages = {
14+
\ 'glossaries-extra': {'style': 'long', 'record': v:true},
15+
\ 'biblatex': {
16+
\ 'backend': 'biber',
17+
\ 'url': v:false,
18+
\ 'giveninits': v:true,
19+
\ 'style': 'numeric-comp',
20+
\ 'maxcitenames': '99'
21+
\ },
22+
\ 'glossaries': {'acronyms': v:true},
23+
\ 'tikz': {},
24+
\ 'babel' : {'main': 'german', 'english': v:true},
25+
\ 'silence': {'debrief': v:true},
26+
\ 'package1': {'draft': v:true},
27+
\ 'biblatex-chicago': {'notes': v:true, 'useibid': v:true},
28+
\ 'amsmath': {},
29+
\ 'package2': {'draft': v:true}
30+
\ }
31+
call assert_equal(s:packages, b:vimtex.packages)
32+
33+
call vimtex#test#finished()

0 commit comments

Comments
 (0)