2014-07-22 18:08:57 -04:00
|
|
|
" LaTeX plugin for Vim
|
|
|
|
"
|
|
|
|
" Maintainer: Karl Yngve Lervåg
|
|
|
|
" Email: karl.yngve@gmail.com
|
|
|
|
"
|
|
|
|
|
2014-07-15 08:52:45 -04:00
|
|
|
function! latex#toc#init(initialized) " {{{1
|
2015-01-26 09:41:40 -05:00
|
|
|
call latex#util#set_default('g:latex_toc_enabled', 1)
|
2014-07-15 08:52:45 -04:00
|
|
|
if !g:latex_toc_enabled | return | endif
|
|
|
|
|
2015-01-26 09:41:40 -05:00
|
|
|
" Set default options
|
|
|
|
call latex#util#set_default('g:latex_toc_fold', 0)
|
|
|
|
call latex#util#set_default('g:latex_toc_fold_levels', 10)
|
|
|
|
call latex#util#set_default('g:latex_toc_hide_help', 0)
|
|
|
|
call latex#util#set_default('g:latex_toc_hide_line_numbers', 1)
|
|
|
|
call latex#util#set_default('g:latex_toc_hide_preamble', 0)
|
|
|
|
call latex#util#set_default('g:latex_toc_numbers', 1)
|
|
|
|
call latex#util#set_default('g:latex_toc_numbers_width', 0)
|
|
|
|
call latex#util#set_default('g:latex_toc_resize', 1)
|
|
|
|
call latex#util#set_default('g:latex_toc_secnumdepth', 3)
|
|
|
|
call latex#util#set_default('g:latex_toc_split_pos', 'vert leftabove')
|
|
|
|
call latex#util#set_default('g:latex_toc_width', 30)
|
|
|
|
call latex#util#error_deprecated('g:latex_toc_split_side')
|
|
|
|
|
2014-07-15 08:52:45 -04:00
|
|
|
" Define commands
|
|
|
|
command! -buffer VimLatexTocOpen call latex#toc#open()
|
|
|
|
command! -buffer VimLatexTocToggle call latex#toc#toggle()
|
|
|
|
|
|
|
|
" Define mappings
|
|
|
|
if g:latex_mappings_enabled
|
2014-11-24 14:25:00 -05:00
|
|
|
nnoremap <buffer> <localleader>lt :call latex#toc#open()<cr>
|
|
|
|
nnoremap <buffer> <localleader>lT :call latex#toc#toggle()<cr>
|
2013-10-05 07:53:42 -04:00
|
|
|
endif
|
|
|
|
endfunction
|
|
|
|
|
2014-07-15 08:52:45 -04:00
|
|
|
function! latex#toc#open() " {{{1
|
2014-07-04 14:15:28 -04:00
|
|
|
" Go to TOC if it already exists
|
2013-10-05 07:53:42 -04:00
|
|
|
let winnr = bufwinnr(bufnr('LaTeX TOC'))
|
|
|
|
if winnr >= 0
|
|
|
|
silent execute winnr . 'wincmd w'
|
|
|
|
return
|
|
|
|
endif
|
|
|
|
|
2014-08-10 12:45:32 -04:00
|
|
|
" Store tex buffer number and position and parse TOC data
|
2013-10-05 07:53:42 -04:00
|
|
|
let calling_buf = bufnr('%')
|
|
|
|
let calling_file = expand('%:p')
|
2014-07-04 14:15:28 -04:00
|
|
|
let calling_line = line('.')
|
2014-07-27 14:41:23 -04:00
|
|
|
let toc = s:parse_toc()
|
2014-07-04 14:15:28 -04:00
|
|
|
|
|
|
|
" Resize vim session if wanted, then create TOC window
|
2013-10-05 07:53:42 -04:00
|
|
|
if g:latex_toc_resize
|
|
|
|
silent exe "set columns+=" . g:latex_toc_width
|
|
|
|
endif
|
2015-01-01 05:31:15 -05:00
|
|
|
silent exe g:latex_toc_split_pos g:latex_toc_width . 'new LaTeX\ TOC'
|
2014-07-04 14:15:28 -04:00
|
|
|
|
|
|
|
" Set buffer local variables
|
2014-07-01 16:34:26 -04:00
|
|
|
let b:toc = toc
|
|
|
|
let b:toc_numbers = 1
|
2014-08-10 12:45:32 -04:00
|
|
|
let b:toc_max_level = s:max_level
|
|
|
|
let b:toc_topmatters = s:count_matters
|
|
|
|
let b:toc_secnumdepth = g:latex_toc_secnumdepth
|
2014-08-10 14:43:57 -04:00
|
|
|
let b:calling_winnr = bufwinnr(calling_buf)
|
2014-08-10 12:45:32 -04:00
|
|
|
let b:calling_file = calling_file
|
|
|
|
let b:calling_line = calling_line
|
2013-10-05 07:53:42 -04:00
|
|
|
|
|
|
|
setlocal filetype=latextoc
|
|
|
|
endfunction
|
|
|
|
|
2014-07-15 08:52:45 -04:00
|
|
|
function! latex#toc#toggle() " {{{1
|
2013-10-05 07:53:42 -04:00
|
|
|
if bufwinnr(bufnr('LaTeX TOC')) >= 0
|
|
|
|
if g:latex_toc_resize
|
|
|
|
silent exe "set columns-=" . g:latex_toc_width
|
|
|
|
endif
|
|
|
|
silent execute 'bwipeout' . bufnr('LaTeX TOC')
|
|
|
|
else
|
|
|
|
call latex#toc#open()
|
|
|
|
silent execute 'wincmd w'
|
|
|
|
endif
|
|
|
|
endfunction
|
2014-07-27 14:41:23 -04:00
|
|
|
|
2013-10-05 07:53:42 -04:00
|
|
|
" }}}1
|
|
|
|
|
2014-07-27 14:41:23 -04:00
|
|
|
" {{{1 TOC variables
|
2014-07-20 08:47:41 -04:00
|
|
|
let s:max_level = 0
|
2014-07-27 13:53:03 -04:00
|
|
|
let s:count_matters = 0
|
2014-07-20 08:47:41 -04:00
|
|
|
|
2014-07-15 08:52:45 -04:00
|
|
|
" Define dictionary to keep track of TOC numbers
|
2014-07-18 18:12:37 -04:00
|
|
|
let s:number = {
|
|
|
|
\ 'part' : 0,
|
|
|
|
\ 'chapter' : 0,
|
|
|
|
\ 'section' : 0,
|
|
|
|
\ 'subsection' : 0,
|
|
|
|
\ 'subsubsection' : 0,
|
2014-07-20 08:47:41 -04:00
|
|
|
\ 'subsubsubsection' : 0,
|
|
|
|
\ 'current_level' : 0,
|
2014-08-11 10:39:13 -04:00
|
|
|
\ 'preamble' : 0,
|
2014-07-18 18:12:37 -04:00
|
|
|
\ 'frontmatter' : 0,
|
|
|
|
\ 'mainmatter' : 0,
|
|
|
|
\ 'appendix' : 0,
|
|
|
|
\ 'backmatter' : 0,
|
|
|
|
\ }
|
2014-07-15 08:52:45 -04:00
|
|
|
|
2014-07-20 08:47:41 -04:00
|
|
|
" Map for section hierarchy
|
|
|
|
let s:sec_to_value = {
|
|
|
|
\ '_' : 0,
|
|
|
|
\ 'subsubsubsection' : 1,
|
|
|
|
\ 'subsubsection' : 2,
|
|
|
|
\ 'subsection' : 3,
|
|
|
|
\ 'section' : 4,
|
|
|
|
\ 'chapter' : 5,
|
|
|
|
\ 'part' : 6,
|
|
|
|
\ }
|
|
|
|
|
2014-07-16 17:20:11 -04:00
|
|
|
" Define regular expressions to match document parts
|
2014-07-15 08:52:45 -04:00
|
|
|
let s:re_input = '\v^\s*\\%(input|include)\s*\{'
|
|
|
|
let s:re_input_file = s:re_input . '\zs[^\}]+\ze}'
|
|
|
|
let s:re_sec = '\v^\s*\\%(part|chapter|%(sub)*section)\*?\s*\{'
|
2014-07-20 08:47:41 -04:00
|
|
|
let s:re_sec_starred = '\v^\s*\\%(part|chapter|%(sub)*section)\*'
|
|
|
|
let s:re_sec_level = '\v^\s*\\\zs%(part|chapter|%(sub)*section)'
|
2014-07-15 08:52:45 -04:00
|
|
|
let s:re_sec_title = s:re_sec . '\zs.{-}\ze\}?$'
|
2014-07-27 13:53:03 -04:00
|
|
|
let s:re_matters = '\v^\s*\\%(front|main|back)matter>'
|
2014-07-18 18:12:37 -04:00
|
|
|
let s:re_structure = '\v^\s*\\((front|main|back)matter|appendix)>'
|
|
|
|
let s:re_structure_match = '\v((front|main|back)matter|appendix)'
|
|
|
|
let s:re_other = {
|
|
|
|
\ 'toc' : {
|
|
|
|
\ 'title' : 'Table of contents',
|
|
|
|
\ 're' : '\v^\s*\\tableofcontents',
|
|
|
|
\ },
|
|
|
|
\ 'index' : {
|
|
|
|
\ 'title' : 'Alphabetical index',
|
|
|
|
\ 're' : '\v^\s*\\printindex\[?',
|
|
|
|
\ },
|
|
|
|
\ 'bib' : {
|
|
|
|
\ 'title' : 'Bibliography',
|
|
|
|
\ 're' : '\v^\s*\\%('
|
|
|
|
\ . 'printbib%(liography|heading)\s*(\{|\[)?'
|
|
|
|
\ . '|begin\s*\{\s*thebibliography\s*\}'
|
|
|
|
\ . '|bibliography\s*\{)',
|
|
|
|
\ },
|
|
|
|
\ }
|
2014-07-15 08:52:45 -04:00
|
|
|
|
2014-07-20 08:47:41 -04:00
|
|
|
" }}}1
|
|
|
|
|
2014-07-27 14:41:23 -04:00
|
|
|
function! s:parse_toc() " {{{1
|
|
|
|
let file = g:latex#data[b:latex.id].tex
|
|
|
|
|
|
|
|
" Reset TOC numbering
|
2014-08-11 10:39:13 -04:00
|
|
|
call s:number_reset('preamble')
|
2014-07-27 14:41:23 -04:00
|
|
|
|
|
|
|
" Find max level and number of \*matter commands
|
|
|
|
let s:max_level = 0
|
|
|
|
let s:count_matters = 0
|
|
|
|
call s:parse_limits(file)
|
|
|
|
|
|
|
|
" Parse TOC data
|
|
|
|
return s:parse_file(file)
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
" }}}1
|
|
|
|
function! s:parse_limits(file) " {{{1
|
|
|
|
if !filereadable(a:file)
|
2014-07-29 03:27:48 -04:00
|
|
|
echoerr "Error in latex#toc s:parse_limits"
|
2014-07-27 14:41:23 -04:00
|
|
|
echoerr "File not readable: " . a:file
|
|
|
|
return ''
|
|
|
|
endif
|
|
|
|
|
|
|
|
for line in readfile(a:file)
|
|
|
|
if line =~# s:re_input
|
2014-07-30 12:10:42 -04:00
|
|
|
call s:parse_limits(s:parse_line_input(line, a:file))
|
2014-07-27 14:41:23 -04:00
|
|
|
elseif line =~# s:re_sec
|
|
|
|
let s:max_level = max([s:max_level,
|
|
|
|
\ s:sec_to_value[matchstr(line, s:re_sec_level)]])
|
|
|
|
elseif line =~# s:re_matters
|
|
|
|
let s:count_matters += 1
|
|
|
|
endif
|
|
|
|
endfor
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
" }}}1
|
|
|
|
function! s:parse_file(file) " {{{1
|
2014-07-04 14:15:28 -04:00
|
|
|
" Parses tex file for TOC entries
|
|
|
|
"
|
|
|
|
" The function returns a list of entries. Each entry is a dictionary:
|
|
|
|
"
|
|
|
|
" entry = {
|
|
|
|
" title : "Some title",
|
|
|
|
" number : "3.1.2",
|
|
|
|
" file : /path/to/file.tex,
|
|
|
|
" line : 142,
|
2014-07-20 08:47:41 -04:00
|
|
|
" level : 2,
|
2014-07-04 14:15:28 -04:00
|
|
|
" }
|
|
|
|
|
2014-07-31 17:27:05 -04:00
|
|
|
if a:file == ''
|
|
|
|
return []
|
|
|
|
elseif !filereadable(a:file)
|
2014-07-27 14:41:23 -04:00
|
|
|
echoerr "Error in latex#toc s:parse_file"
|
2014-07-04 14:15:28 -04:00
|
|
|
echoerr "File not readable: " . a:file
|
|
|
|
return []
|
2013-10-05 07:53:42 -04:00
|
|
|
endif
|
|
|
|
|
2014-07-04 14:15:28 -04:00
|
|
|
let toc = []
|
|
|
|
|
|
|
|
let lnum = 0
|
|
|
|
for line in readfile(a:file)
|
2014-07-01 16:34:26 -04:00
|
|
|
let lnum += 1
|
2014-07-04 14:15:28 -04:00
|
|
|
|
2014-07-08 06:32:41 -04:00
|
|
|
" 1. Parse inputs or includes
|
|
|
|
if line =~# s:re_input
|
2014-07-30 12:10:42 -04:00
|
|
|
call extend(toc, s:parse_file(s:parse_line_input(line, a:file)))
|
2014-07-04 14:15:28 -04:00
|
|
|
continue
|
|
|
|
endif
|
|
|
|
|
2014-08-11 10:39:13 -04:00
|
|
|
" 2. Parse preamble
|
|
|
|
if s:number.preamble
|
|
|
|
if !g:latex_toc_hide_preamble && line =~# '\v^\s*\\documentclass'
|
2014-07-15 06:05:29 -04:00
|
|
|
call add(toc, {
|
|
|
|
\ 'title' : 'Preamble',
|
|
|
|
\ 'number' : '',
|
|
|
|
\ 'file' : a:file,
|
|
|
|
\ 'line' : lnum,
|
2014-07-20 08:47:41 -04:00
|
|
|
\ 'level' : s:max_level,
|
2014-07-15 06:05:29 -04:00
|
|
|
\ })
|
|
|
|
continue
|
|
|
|
endif
|
2014-08-11 10:39:13 -04:00
|
|
|
|
|
|
|
if line =~# '\v^\s*\\begin\{document\}'
|
|
|
|
let s:number.preamble = 0
|
|
|
|
endif
|
|
|
|
|
|
|
|
continue
|
2014-07-15 06:05:29 -04:00
|
|
|
endif
|
|
|
|
|
2014-07-18 18:12:37 -04:00
|
|
|
" 3. Parse document structure (front-/main-/backmatter, appendix)
|
|
|
|
if line =~# s:re_structure
|
|
|
|
call s:number_reset(matchstr(line, s:re_structure_match))
|
2014-07-04 14:15:28 -04:00
|
|
|
continue
|
|
|
|
endif
|
2014-07-09 06:11:55 -04:00
|
|
|
|
2014-07-18 18:12:37 -04:00
|
|
|
" 4. Parse \parts, \chapters, \sections, and \subsections
|
|
|
|
if line =~# s:re_sec
|
|
|
|
call add(toc, s:parse_line_sec(a:file, lnum, line))
|
2014-07-17 13:46:19 -04:00
|
|
|
continue
|
|
|
|
endif
|
|
|
|
|
2014-07-18 18:12:37 -04:00
|
|
|
" 5. Parse other stuff
|
|
|
|
for other in values(s:re_other)
|
|
|
|
if line =~# other.re
|
|
|
|
call add(toc, {
|
|
|
|
\ 'title' : other.title,
|
|
|
|
\ 'number' : '',
|
|
|
|
\ 'file' : a:file,
|
|
|
|
\ 'line' : lnum,
|
2014-07-20 08:47:41 -04:00
|
|
|
\ 'level' : s:max_level,
|
2014-07-18 18:12:37 -04:00
|
|
|
\ })
|
|
|
|
continue
|
|
|
|
endif
|
|
|
|
endfor
|
2014-07-01 16:34:26 -04:00
|
|
|
endfor
|
2013-10-05 07:53:42 -04:00
|
|
|
|
2014-07-01 16:34:26 -04:00
|
|
|
return toc
|
|
|
|
endfunction
|
2013-10-05 07:53:42 -04:00
|
|
|
|
2014-07-30 12:10:42 -04:00
|
|
|
function! s:parse_line_input(line, file) " {{{1
|
2014-07-08 06:32:41 -04:00
|
|
|
let l:file = matchstr(a:line, s:re_input_file)
|
2014-07-30 12:10:42 -04:00
|
|
|
|
2014-11-19 03:13:55 -05:00
|
|
|
" Trim whitespaces from beginning and end of string
|
|
|
|
let l:file = substitute(l:file, '^\s*', '', '')
|
|
|
|
let l:file = substitute(l:file, '\s*$', '', '')
|
|
|
|
|
2014-07-31 17:27:05 -04:00
|
|
|
" Ensure file has extension
|
2014-07-04 14:15:28 -04:00
|
|
|
if l:file !~# '.tex$'
|
|
|
|
let l:file .= '.tex'
|
2013-10-05 07:53:42 -04:00
|
|
|
endif
|
2014-07-31 17:27:05 -04:00
|
|
|
|
|
|
|
" Only return full path names
|
2014-07-31 17:29:05 -04:00
|
|
|
if l:file !~# '\v^(\/|[A-Z]:)'
|
2014-07-30 12:10:42 -04:00
|
|
|
let l:file = fnamemodify(a:file, ':p:h') . '/' . l:file
|
|
|
|
endif
|
2014-07-29 03:27:48 -04:00
|
|
|
|
2014-07-31 17:27:05 -04:00
|
|
|
" Only return filename if it is readable
|
|
|
|
if filereadable(l:file)
|
|
|
|
return l:file
|
|
|
|
else
|
|
|
|
return ''
|
|
|
|
endif
|
2014-07-04 14:15:28 -04:00
|
|
|
endfunction
|
|
|
|
|
2014-07-15 08:52:45 -04:00
|
|
|
function! s:parse_line_sec(file, lnum, line) " {{{1
|
2014-07-08 06:32:41 -04:00
|
|
|
let title = matchstr(a:line, s:re_sec_title)
|
2014-07-20 08:47:41 -04:00
|
|
|
let level = matchstr(a:line, s:re_sec_level)
|
2014-08-10 12:45:32 -04:00
|
|
|
|
|
|
|
" Check if section is starred
|
|
|
|
if a:line =~# s:re_sec_starred
|
|
|
|
let number = ''
|
|
|
|
else
|
|
|
|
let number = s:number_increment(level)
|
|
|
|
endif
|
2014-07-04 14:15:28 -04:00
|
|
|
|
|
|
|
return {
|
2014-07-08 06:32:41 -04:00
|
|
|
\ 'title' : title,
|
2014-07-09 06:11:55 -04:00
|
|
|
\ 'number' : number,
|
2014-07-04 14:15:28 -04:00
|
|
|
\ 'file' : a:file,
|
|
|
|
\ 'line' : a:lnum,
|
2014-07-20 08:47:41 -04:00
|
|
|
\ 'level' : s:number.current_level,
|
2014-07-04 14:15:28 -04:00
|
|
|
\ }
|
2013-10-05 07:53:42 -04:00
|
|
|
endfunction
|
|
|
|
|
2014-07-20 08:47:41 -04:00
|
|
|
" }}}1
|
|
|
|
|
2014-08-11 10:39:13 -04:00
|
|
|
function! s:number_reset(part) " {{{1
|
2014-07-18 18:12:37 -04:00
|
|
|
for key in keys(s:number)
|
|
|
|
let s:number[key] = 0
|
|
|
|
endfor
|
2014-08-11 10:39:13 -04:00
|
|
|
let s:number[a:part] = 1
|
2014-07-09 06:11:55 -04:00
|
|
|
endfunction
|
|
|
|
|
2014-08-10 12:45:32 -04:00
|
|
|
function! s:number_increment(level) " {{{1
|
2014-07-09 06:11:55 -04:00
|
|
|
" Increment numbers
|
|
|
|
if a:level == 'part'
|
|
|
|
let s:number.part += 1
|
|
|
|
let s:number.chapter = 0
|
|
|
|
let s:number.section = 0
|
|
|
|
let s:number.subsection = 0
|
|
|
|
let s:number.subsubsection = 0
|
2014-07-20 08:47:41 -04:00
|
|
|
let s:number.subsubsubsection = 0
|
2014-07-09 06:11:55 -04:00
|
|
|
elseif a:level == 'chapter'
|
|
|
|
let s:number.chapter += 1
|
|
|
|
let s:number.section = 0
|
|
|
|
let s:number.subsection = 0
|
|
|
|
let s:number.subsubsection = 0
|
2014-07-20 08:47:41 -04:00
|
|
|
let s:number.subsubsubsection = 0
|
2014-07-09 06:11:55 -04:00
|
|
|
elseif a:level == 'section'
|
|
|
|
let s:number.section += 1
|
|
|
|
let s:number.subsection = 0
|
|
|
|
let s:number.subsubsection = 0
|
2014-07-20 08:47:41 -04:00
|
|
|
let s:number.subsubsubsection = 0
|
2014-07-09 06:11:55 -04:00
|
|
|
elseif a:level == 'subsection'
|
|
|
|
let s:number.subsection += 1
|
|
|
|
let s:number.subsubsection = 0
|
2014-07-20 08:47:41 -04:00
|
|
|
let s:number.subsubsubsection = 0
|
2014-07-09 06:11:55 -04:00
|
|
|
elseif a:level == 'subsubsection'
|
|
|
|
let s:number.subsubsection += 1
|
2014-07-20 08:47:41 -04:00
|
|
|
let s:number.subsubsubsection = 0
|
|
|
|
elseif a:level == 'subsubsubsection'
|
|
|
|
let s:number.subsubsubsection += 1
|
2014-07-09 06:11:55 -04:00
|
|
|
endif
|
|
|
|
|
2014-08-10 12:45:32 -04:00
|
|
|
" Store current level
|
|
|
|
let s:number.current_level = s:sec_to_value[a:level]
|
2014-07-09 06:43:10 -04:00
|
|
|
|
2014-08-10 12:45:32 -04:00
|
|
|
return copy(s:number)
|
2014-07-04 14:15:28 -04:00
|
|
|
endfunction
|
2014-07-08 06:32:41 -04:00
|
|
|
|
2014-07-04 14:15:28 -04:00
|
|
|
" }}}1
|
|
|
|
|
2014-12-08 14:44:17 -05:00
|
|
|
" vim: fdm=marker sw=2
|