lervag / vimtex

VimTeX: A modern Vim and neovim filetype plugin for LaTeX files.
MIT License
5.53k stars 391 forks source link

Slow plugin commentary due to extensive calls to vimtex#fold#level() #944

Closed kiryph closed 7 years ago

kiryph commented 7 years ago

Explain the issue

When commenting/uncommenting code blocks, I often use the mapping gcip from vim-commentary. However, when using this in a complex latex file with extensive folding, this becomes incredibly slow.

Minimal working example

Cursor in line 62.

% Preamble {{{

\documentclass[
  draft,
  ngerman,
  english,
]{scrbook}

\usepackage[
  main=english,
  ngerman,
]{babel}
\usepackage[
  backend=biber,
  style=numeric-comp,
  maxcitenames=99,
  doi=false,
  url=false,
  giveninits=true,
]{biblatex}
\usepackage[
  inline,
  shortlabels,
]{enumitem}
\usepackage[
  markifdraft,
  markifdirty,
  dirty={[dirty]},
]{gitinfo2}
\usepackage[
  final,
]{pdfpages}
\usepackage[
  mode=image, % tex,tex|image,image,build,buildmissing,buildnew
]{standalone}
\usepackage[
  capitalise,
  nameinlink,
]{cleveref} % DEP: after 'hyperref', i.e. also 'bookmark'
\usepackage[
  english=american
]{csquotes} % DEP: after 'inputenc'

% Latinabbreviations {{{
% Omit final dot from each def.
\ExplSyntaxOn
\newcommand{\latinabbrev}[1]{%
  \peek_meaning:NTF . {% Same as \@ifnextchar
    #1\@}%
  { \peek_catcode:NTF a {% Check whether next char has same catcode as \'a, i.e., is a letter
      #1.\@ }%
    {#1.\@}}%
}
\ExplSyntaxOff
\def\eg{\latinabbrev{e.g}}
\def\etal{\latinabbrev{et al}}
\def\etc{\latinabbrev{etc}}
\def\ie{\latinabbrev{i.e}}
% }}}

% https://tex.stackexchange.com/q/263523/is-it-possible-to-remove-parenthesis-from-single-cref-references
% >>>CURSOR<<<
% \crefformat{equation}{\eqA eq.~\eqB #2#1#3)}
% \newcommand{\eqA}{}
% \newcommand{\eqB}{(}
% \DeclareRobustCommand{\pcref}[1]{%
%   \begingroup
%   \renewcommand{\eqA}{(}\renewcommand{\eqB}{}%
%   \cref{#1}%
%   \endgroup
% }

% }}}
\begin{document}

\end{document}

Minimal vimrc file

" invoke with
" $ vim -u ~/.vim/mwe-vimrc
set nocompatible
let g:dein#types#git#clone_depth = 1
set runtimepath+=~/.cache/vim/dein/repos/github.com/Shougo/dein.vim
if dein#load_state('~/.cache/vim/dein')
  call dein#begin('~/.cache/vim/dein')
  call dein#add('Shougo/dein.vim')
  call dein#add('tpope/vim-commentary')
  call dein#add('lervag/vimtex')
  call dein#end()
  call dein#save_state()
endif

" vimtex-folding {{{
let  g:vimtex_fold_types = {
        \ 'preamble' : {'enabled' : 0},
        \ 'envs' : {
        \   'blacklist' : ['figure', 'table'],
        \ },
        \ 'sections' : {
        \   'sections' : [
        \     'part',
        \     'chapter',
        \     'section',
        \     'subsection',
        \     'subsubsection',
        \     'paragraph',
        \     'subparagraph',
        \   ],
        \ },
        \ 'cmd_single' : {
        \   'cmds' : [
        \     'addplot',
        \     'bottomcaption',
        \     'defaultfontfeatures',
        \     'directlua',
        \     'graphicspath',
        \     'hypersetup',
        \     'includeonly',
        \     'institute',
        \     'komaoptions',
        \     'mbox',
        \     'pgfplotstableread',
        \     'tablecaption',
        \     'tablefirsthead',
        \     'tablehead',
        \     'tablelasthead',
        \     'tablelasttail',
        \     'tabletail',
        \     'tcbset',
        \     'tikzset',
        \     'title',
        \     'titlegraphic',
        \     'topcaption',
        \     'usetikzlibrary',
        \   ],
        \ },
        \ 'cmd_single_opt' : {
        \   'cmds' : [
        \     'declarenewlayer',
        \     'documentclass',
        \     'includepdf',
        \     'set%(main|sans|mono)font',
        \     'usepackage',
        \     'usetheme',
        \   ],
        \ },
        \ 'cmd_multi' : {
        \   'cmds' : [
        \     '%(re)?new%(command|environment)',
        \     'declare%(index)?%(field|list|name)%(format|alias)',
        \     'declare%(multi|auto)?citecommand',
        \     'itemboxed',
        \     'newfontfeature',
        \     'newglossaryentry',
        \     'newtcbox',
        \     'newtcolorbox',
        \     'presetkeys',
        \     'providecommand',
        \     'setbeamertemplate',
        \   ],
        \ },
        \ 'cmd_addplot' : {
        \   'cmds' : [
        \     'addplot[+3]?',
        \   ],
        \ },
        \}
let g:vimtex_fold_enabled = 1

Close all folds with zM, move cursor with:62, zv to reveal cursor and start profiling with :profile start output.log and :profile func *. Then press gcip and u on line 62 and have a look into the output.log. The summary on my system looks as following:

FUNCTIONS SORTED ON TOTAL TIME
count  total (s)   self (s)  function
 2604   0.411949   0.211454  vimtex#fold#level()
    1   0.203777   0.007558  <SNR>9_go()
   30   0.083295   0.002657  vimtex#fold#text()
   27   0.080584   0.001361  122()
   27   0.079223   0.000702  vimtex#cmd#get_at()
   27   0.076208   0.001250  vimtex#cmd#get_current()
   27   0.071779   0.002858  <SNR>124_get_cmd()
   81   0.067260   0.004379  <SNR>124_get_cmd_part()
  156   0.061063             113()
  156   0.059637   0.001220  133()
  156   0.058417             134()
   81   0.042532   0.001086  vimtex#delim#get_next()
   82   0.041968   0.017320  <SNR>127_get_delim()
 1038   0.031405             141()
   81   0.021115   0.010324  <SNR>127_parser_delim()
 1038   0.020156             121()
  433   0.014961   0.002955  vimtex#pos#set_cursor()
  514   0.014092   0.013385  <SNR>131_parse_args()
  338   0.011188             138()
   54   0.010887   0.004542  vimtex#delim#get_matching()

FUNCTIONS SORTED ON SELF TIME
count  total (s)   self (s)  function
 2604   0.411949   0.211454  vimtex#fold#level()
  156              0.061063  113()
  156              0.058417  134()
 1038              0.031405  141()
 1038              0.020156  121()
   82   0.041968   0.017320  <SNR>127_get_delim()
  514   0.014092   0.013385  <SNR>131_parse_args()
  338              0.011188  138()
   81   0.021115   0.010324  <SNR>127_parser_delim()
    1   0.203777   0.007558  <SNR>9_go()
  162              0.005937  <SNR>127_parser_delim_get_regexp()
   81              0.004854  <SNR>127_parser_delim_get_corr()
  258              0.004673  131()
   54   0.010887   0.004542  vimtex#delim#get_matching()
  138              0.004487  <SNR>124_text_between()
  258              0.004440  123()
  258              0.004434  129()
   81   0.067260   0.004379  <SNR>124_get_cmd_part()
  258   0.007933   0.003499  126()
  433   0.014961   0.002955  vimtex#pos#set_cursor()
CLICK HERE FOR FULL PROFILING REPORT ``` FUNCTION 1() Called 4 times Total time: 0.000098 Self time: 0.000098 count total (s) self (s) 4 0.000009 if s:delayed_show.show_timer 4 0.000014 call timer_stop(s:delayed_show.show_timer) 4 0.000004 endif 4 0.000006 if s:delayed_show.hide_timer call timer_stop(s:delayed_show.hide_timer) call s:delayed_show.hide_cb(s:delayed_show.hide_timer) endif 4 0.000020 let s:delayed_show.show_timer = timer_start(g:matchparen_show_delay, s:delayed_show.show_cb) 4 0.000017 let s:delayed_show.show_pos = getpos('.') FUNCTION 2() Called 2 times Total time: 0.000704 Self time: 0.000262 count total (s) self (s) 2 0.000057 if s:delayed_show.show_pos == getpos('.') 2 0.000589 0.000147 call s:Highlight_Matching_Pair() " If something was highlighted, start a timer to hide it. 2 0.000014 if exists('w:paren_hl_on') && w:paren_hl_on if s:delayed_show.hide_timer call timer_stop(s:delayed_show.hide_timer) endif let s:delayed_show.hide_timer = timer_start(g:matchparen_hide_delay, s:delayed_show.hide_cb) endif 2 0.000002 endif FUNCTION vimtex#util#in_syntax() Called 2 times Total time: 0.000639 Self time: 0.000639 count total (s) self (s) " Usage: vimtex#util#in_syntax(name, [line, col]) " Get position and correct it if necessary 2 0.000013 let l:pos = a:0 > 0 ? [a:1, a:2] : [line('.'), col('.')] 2 0.000005 if mode() ==# 'i' let l:pos[1] -= 1 endif 2 0.000015 call map(l:pos, 'max([v:val, 1])') " Check syntax at position 2 0.000593 return match(map(synstack(l:pos[0], l:pos[1]), "synIDattr(v:val, 'name')"), '^' . a:name) >= 0 FUNCTION neomake#GetCurrentErrorMsg() Called 2 times Total time: 0.000205 Self time: 0.000205 count total (s) self (s) 2 0.000019 let buf = bufnr('%') 2 0.000010 let ln = line('.') 2 0.000008 let ln_errors = [] 6 0.000024 for maker_type in ['file', 'project'] 4 0.000046 let buf_errors = get(s:current_errors[maker_type], buf, {}) 4 0.000033 let ln_errors += get(buf_errors, ln, []) 4 0.000010 endfor 2 0.000010 if empty(ln_errors) 2 0.000006 return '' endif if len(ln_errors) > 1 let ln_errors = copy(ln_errors) call sort(ln_errors, function('neomake#utils#sort_by_col')) endif let entry = ln_errors[0] let r = entry.maker_name . ': ' . entry.text let suffix = entry.type . (entry.nr != -1 ? entry.nr : '') if !empty(suffix) let r .= ' ('.suffix.')' endif return r FUNCTION 127_get_delim() Called 82 times Total time: 0.041968 Self time: 0.017320 count total (s) self (s) " " Arguments: " opts = { " 'direction' : next " prev " current " 'type' : env_tex " env_math " env_all " delim_tex " delim_math " delim_modq_math (possibly modified math delimiter) " delim_mod_math (modified math delimiter) " delim_all " all " 'side' : open " close " both " 'syn_exclude' : Don't match in given syntax " } " " Returns: " delim = { " type : env | delim " side : open | close " name : name of environment [only for type env] " lnum : number " cnum : number " match : unparsed matched delimiter " corr : corresponding delimiter " re : { " open : regexp for the opening part " close : regexp for the closing part " } " } " 82 0.000903 0.000382 let l:save_pos = vimtex#pos#get_cursor() 82 0.000309 let l:re = g:vimtex#delim#re[a:opts.type][a:opts.side] 82 0.000113 while 1 82 0.002031 let [l:lnum, l:cnum] = a:opts.direction ==# 'next' ? searchpos(l:re, 'cnW', line('.') + s:stopline) : a:opts.direction ==# 'prev' ? searchpos(l:re, 'bcnW', max([line('.') - s:stopline, 1])) : searchpos(l:re, 'bcnW', line('.')) 82 0.000284 if l:lnum == 0 | break | endif 81 0.000394 if has_key(a:opts, 'syn_exclude') && vimtex#util#in_syntax(a:opts.syn_exclude, l:lnum, l:cnum) call vimtex#pos#set_cursor(vimtex#pos#prev(l:lnum, l:cnum)) continue endif 81 0.000069 break endwhile 82 0.003439 0.000427 call vimtex#pos#set_cursor(l:save_pos) 82 0.000951 let l:match = matchstr(getline(l:lnum), '^' . l:re, l:cnum-1) 82 0.000474 if a:opts.direction ==# 'current' && l:cnum + strlen(l:match) + (mode() ==# 'i' ? 1 : 0) <= col('.') 1 0.000002 let l:match = '' 1 0.000001 let l:lnum = 0 1 0.000001 let l:cnum = 0 1 0.000000 endif 82 0.000383 let l:result = { 'type' : '', 'lnum' : l:lnum, 'cnum' : l:cnum, 'match' : l:match,} 411 0.000559 for l:type in s:types 410 0.004251 if l:match =~# '^' . l:type.re 81 0.022375 0.001260 let l:result = extend( l:type.parser(l:match, l:lnum, l:cnum, a:opts.side, a:opts.type, a:opts.direction), l:result, 'keep') 81 0.000089 break endif 329 0.000237 endfor 82 0.000281 return empty(l:result.type) ? {} : l:result FUNCTION 127_parser_delim() Called 81 times Total time: 0.021115 Self time: 0.010324 count total (s) self (s) 81 0.000205 let result = {} 81 0.000187 let result.type = 'delim' 81 0.001576 let result.side = a:match =~# g:vimtex#delim#re.delim_all.open ? 'open' : 'close' 81 0.000258 let result.is_open = result.side ==# 'open' 81 0.000515 let result.get_matching = function('s:get_matching_delim') " " Find corresponding delimiter and the regexps " 81 0.001155 if a:match =~# '^' . g:vimtex#delim#re.mods.both let m1 = matchstr(a:match, '^' . g:vimtex#delim#re.mods.both) let d1 = substitute(strpart(a:match, len(m1)), '^\s*', '', '') let s1 = !result.is_open let re1 = s:parser_delim_get_regexp(m1, s1, 'mods') . '\s*' . s:parser_delim_get_regexp(d1, s1, 'delim_math') let m2 = s:parser_delim_get_corr(m1, 'mods') let d2 = s:parser_delim_get_corr(d1, 'delim_math') let s2 = result.is_open let re2 = s:parser_delim_get_regexp(m2, s2, 'mods') . '\s*' . (m1 =~# '\\\%(left\|right\)' ? '\%(' . s:parser_delim_get_regexp(d2, s2, 'delim_math') . '\|\.\)' : s:parser_delim_get_regexp(d2, s2, 'delim_math')) else 81 0.000156 let d1 = a:match 81 0.000113 let m1 = '' 81 0.003674 0.000582 let re1 = s:parser_delim_get_regexp(a:match, !result.is_open) 81 0.005382 0.000528 let d2 = s:parser_delim_get_corr(a:match) 81 0.000132 let m2 = '' 81 0.003367 0.000522 let re2 = s:parser_delim_get_regexp(d2, result.is_open) 81 0.000083 endif 81 0.000173 let result.delim = d1 81 0.000157 let result.mod = m1 81 0.000222 let result.corr = m2 . d2 81 0.000146 let result.corr_delim = d2 81 0.000139 let result.corr_mod = m2 81 0.000484 let result.re = { 'this' : re1, 'corr' : re2, 'open' : result.is_open ? re1 : re2, 'close' : result.is_open ? re2 : re1,} 81 0.000097 return result FUNCTION 127_get_matching_delim() Called 54 times Total time: 0.002467 Self time: 0.002467 count total (s) self (s) 54 0.000579 let [re, flags, stopline] = self.is_open ? [self.re.close, 'nW', line('.') + s:stopline] : [self.re.open, 'bnW', max([line('.') - s:stopline, 1])] 54 0.001091 let [lnum, cnum] = searchpairpos(self.re.open, '', self.re.close, flags, '', stopline) 54 0.000555 let match = matchstr(getline(lnum), '^' . re, cnum-1) 54 0.000141 return [match, lnum, cnum] FUNCTION 113() Called 156 times Total time: 0.061063 Self time: 0.061063 count total (s) self (s) 156 0.058668 let l:env = matchstr(a:line, self.re.name) 156 0.000606 if !empty(l:env) && self.validate(l:env) if a:line =~# self.re.start if a:line !~# '\\end' return 'a1' endif elseif a:line =~# self.re.end if a:line !~# '\\begin' return 's1' endif endif endif FUNCTION vimtex#util#in_comment() Called 2 times Total time: 0.000661 Self time: 0.000022 count total (s) self (s) 2 0.000660 0.000021 return call('vimtex#util#in_syntax', ['texComment'] + a:000) FUNCTION 121() Called 1038 times Total time: 0.020156 Self time: 0.020156 count total (s) self (s) 1038 0.011761 if a:line =~# self.re.start 360 0.000691 let self.opened = 1 360 0.000476 return 'a1' elseif self.opened && a:line =~# self.re.end 340 0.000638 let self.opened = 0 340 0.000438 return 's1' endif FUNCTION 122() Called 27 times Total time: 0.080584 Self time: 0.001361 count total (s) self (s) 27 0.000265 let l:col = strlen(matchstr(a:line, '^\s*')) + 1 27 0.080292 0.001069 return matchstr(a:line, self.re.text) . '[...]{' . vimtex#cmd#get_at(v:foldstart, l:col).args[0].text . '}' FUNCTION 123() Called 258 times Total time: 0.004440 Self time: 0.004440 count total (s) self (s) 258 0.001888 if a:line =~# self.re.start 60 0.000122 let s:self.opened = 1 60 0.000070 return 'a1' elseif a:line =~# self.re.end 42 0.000081 let s:self.opened = 0 42 0.000056 return 's1' endif FUNCTION 126() Called 258 times Total time: 0.007933 Self time: 0.003499 count total (s) self (s) 258 0.005586 0.001152 call self.refresh() " Fold chapters and sections 258 0.000608 for [l:part, l:level] in self.folds if a:line =~# l:part return '>' . l:level endif endfor FUNCTION 129() Called 258 times Total time: 0.004434 Self time: 0.004434 count total (s) self (s) " " Parse current buffer to find which sections to fold and their levels. The " patterns are predefined to optimize the folding. " " We ignore top level parts such as \frontmatter, \appendix, \part, and " similar, unless there are at least two such commands in a document. " " Only refresh if file has been changed 258 0.002518 let l:time = getftime(expand('%')) 258 0.000789 if l:time == self.time | return | endif let self.time = l:time " Initialize let self.folds = [] let level = 0 let buffer = getline(1,'$') " Parse part commands (frontmatter, appendix, etc) " Note: We want a minimum of two top level parts let lines = filter(copy(buffer), 'v:val =~ ''' . self.re.parts . '''') if len(lines) >= 2 let level += 1 call insert(self.folds, [self.re.parts, level]) endif " Parse section commands (part, chapter, [sub...]section) let lines = filter(copy(buffer), 'v:val =~ ''' . self.re.sections . '''') for part in self.sections let partpattern = '^\s*\%(\\\|% Fake\)' . part . ':\?\>' for line in lines if line =~# partpattern let level += 1 call insert(self.folds, [partpattern, level]) break endif endfor endfor FUNCTION 54_wrap() Called 1 time Total time: 0.000028 Self time: 0.000028 count total (s) self (s) 1 0.000021 if mode() == 'c' && stridx('/?', getcmdtype()) < 0 1 0.000004 return a:seq endif silent! autocmd! slash set hlsearch return a:seq."\(slash-trailer)" FUNCTION 131() Called 258 times Total time: 0.004673 Self time: 0.004673 count total (s) self (s) 258 0.002629 if a:line =~# self.re.start let self.opened = 1 return 'a1' elseif self.opened && a:line =~# self.re.end let self.opened = 0 return 's1' endif FUNCTION 133() Called 156 times Total time: 0.059637 Self time: 0.001220 count total (s) self (s) 156 0.059555 0.001138 return self.opened ? self.fold_closed(a:line, a:lnum) : self.fold_opened(a:line, a:lnum) FUNCTION 134() Called 156 times Total time: 0.058417 Self time: 0.058417 count total (s) self (s) 156 0.057776 if a:line =~# self.re.start let self.opened = 1 return 'a1' endif FUNCTION 4_Highlight_Matching_Pair() Called 2 times Total time: 0.000442 Self time: 0.000442 count total (s) self (s) " Remove any previous match. 2 0.000023 if exists('w:paren_hl_on') && w:paren_hl_on silent! call matchdelete(3) let w:paren_hl_on = 0 endif " Avoid that we remove the popup menu. " Return when there are no colors (looks like the cursor jumps). 2 0.000034 if pumvisible() || (&t_Co < 8 && !has("gui_running")) return endif " Get the character under the cursor and check if it's in 'matchpairs'. 2 0.000015 let c_lnum = line('.') 2 0.000011 let c_col = col('.') 2 0.000005 let before = 0 2 0.000013 let text = getline(c_lnum) 2 0.000075 let matches = matchlist(text, '\(.\)\=\%'.c_col.'c\(.\=\)') 2 0.000011 if empty(matches) let [c_before, c] = ['', ''] else 2 0.000019 let [c_before, c] = matches[1:2] 2 0.000004 endif 2 0.000094 let plist = split(&matchpairs, '.\zs[:,]') 2 0.000016 let i = index(plist, c) 2 0.000005 if i < 0 " not found, in Insert mode try character before the cursor 2 0.000013 if c_col > 1 && (mode() == 'i' || mode() == 'R') let before = strlen(c_before) let c = c_before let i = index(plist, c) endif 2 0.000004 if i < 0 " not found, nothing to do 2 0.000004 return endif endif " Figure out the arguments for searchpairpos(). if i % 2 == 0 let s_flags = 'nW' let c2 = plist[i + 1] else let s_flags = 'nbW' let c2 = c let c = plist[i - 1] endif if c == '[' let c = '\[' let c2 = '\]' endif " Find the match. When it was just before the cursor move it there for a " moment. if before > 0 let has_getcurpos = exists("*getcurpos") if has_getcurpos " getcurpos() is more efficient but doesn't exist before 7.4.313. let save_cursor = getcurpos() else let save_cursor = winsaveview() endif call cursor(c_lnum, c_col - before) endif " Build an expression that detects whether the current cursor position is in " certain syntax types (string, comment, etc.), for use as searchpairpos()'s " skip argument. " We match "escape" for special items, such as lispEscapeSpecial. let s_skip = '!empty(filter(map(synstack(line("."), col(".")), ''synIDattr(v:val, "name")''), ' . '''v:val =~? "string\\|character\\|singlequote\\|escape\\|comment"''))' " If executing the expression determines that the cursor is currently in " one of the syntax types, then we want searchpairpos() to find the pair " within those syntax types (i.e., not skip). Otherwise, the cursor is " outside of the syntax types and s_skip should keep its value so we skip any " matching pair inside the syntax types. execute 'if' s_skip '| let s_skip = 0 | endif' " Limit the search to lines visible in the window. let stoplinebottom = line('w$') let stoplinetop = line('w0') if i % 2 == 0 let stopline = stoplinebottom else let stopline = stoplinetop endif " Limit the search time to 300 msec to avoid a hang on very long lines. " This fails when a timeout is not supported. if mode() == 'i' || mode() == 'R' let timeout = exists("b:matchparen_insert_timeout") ? b:matchparen_insert_timeout : g:matchparen_insert_timeout else let timeout = exists("b:matchparen_timeout") ? b:matchparen_timeout : g:matchparen_timeout endif try let [m_lnum, m_col] = searchpairpos(c, '', c2, s_flags, s_skip, stopline, timeout) catch /E118/ " Can't use the timeout, restrict the stopline a bit more to avoid taking " a long time on closed folds and long lines. " The "viewable" variables give a range in which we can scroll while " keeping the cursor at the same position. " adjustedScrolloff accounts for very large numbers of scrolloff. let adjustedScrolloff = min([&scrolloff, (line('w$') - line('w0')) / 2]) let bottom_viewable = min([line('$'), c_lnum + &lines - adjustedScrolloff - 2]) let top_viewable = max([1, c_lnum-&lines+adjustedScrolloff + 2]) " one of these stoplines will be adjusted below, but the current values are " minimal boundaries within the current window if i % 2 == 0 if has("byte_offset") && has("syntax_items") && &smc > 0 let stopbyte = min([line2byte("$"), line2byte(".") + col(".") + &smc * 2]) let stopline = min([bottom_viewable, byte2line(stopbyte)]) else let stopline = min([bottom_viewable, c_lnum + 100]) endif let stoplinebottom = stopline else if has("byte_offset") && has("syntax_items") && &smc > 0 let stopbyte = max([1, line2byte(".") + col(".") - &smc * 2]) let stopline = max([top_viewable, byte2line(stopbyte)]) else let stopline = max([top_viewable, c_lnum - 100]) endif let stoplinetop = stopline endif let [m_lnum, m_col] = searchpairpos(c, '', c2, s_flags, s_skip, stopline) endtry if before > 0 if has_getcurpos call setpos('.', save_cursor) else call winrestview(save_cursor) endif endif " If a match is found setup match highlighting. if m_lnum > 0 && m_lnum >= stoplinetop && m_lnum <= stoplinebottom if exists('*matchaddpos') call matchaddpos('MatchParen', [[c_lnum, c_col - before], [m_lnum, m_col]], 10, 3) else exe '3match MatchParen /\(\%' . c_lnum . 'l\%' . (c_col - before) . 'c\)\|\(\%' . m_lnum . 'l\%' . m_col . 'c\)/' endif let w:paren_hl_on = 1 endif FUNCTION lightline#link() Called 7 times Total time: 0.000111 Self time: 0.000111 count total (s) self (s) 7 0.000071 let mode = get(s:lightline._mode_, a:0 ? a:1 : mode(), 'normal') 7 0.000020 if s:mode == mode 7 0.000014 return '' endif let s:mode = mode if !has_key(s:highlight, mode) call lightline#highlight(mode) endif let types = map(s:uniq(sort(filter(values(s:lightline.component_type), 'v:val !=# "raw"'))), '[v:val, 1]') for [p, l] in [['Left', len(s:lightline.active.left)], ['Right', len(s:lightline.active.right)]] for [i, t] in map(range(0, l), '[v:val, 0]') + types if i != l exec printf('hi link Lightline%s_active_%s Lightline%s_%s_%s', p, i, p, mode, i) endif for [j, s] in map(range(0, l), '[v:val, 0]') + types if i + 1 == j || t || s && i != l exec printf('hi link Lightline%s_active_%s_%s Lightline%s_%s_%s_%s', p, i, j, p, mode, i, j) endif endfor endfor endfor exec printf('hi link LightlineMiddle_active LightlineMiddle_%s', mode) return '' FUNCTION vimtex#delim#get_next() Called 81 times Total time: 0.042532 Self time: 0.001086 count total (s) self (s) 81 0.042492 0.001046 return s:get_delim(extend({ 'direction' : 'next', 'type' : a:type, 'side' : a:side,}, get(a:, '1', {}))) FUNCTION 141() Called 1038 times Total time: 0.031405 Self time: 0.031405 count total (s) self (s) 1038 0.022865 if a:line =~# self.re.start let self.opened = 1 return 'a1' elseif self.opened && a:line =~# self.re.end let self.opened = 0 return 's1' endif FUNCTION vimtex#delim#get_matching() Called 54 times Total time: 0.010887 Self time: 0.004542 count total (s) self (s) 54 0.000335 if empty(a:delim) || !has_key(a:delim, 'lnum') | return {} | endif " " Get the matching position " 54 0.000607 0.000260 let l:save_pos = vimtex#pos#get_cursor() 54 0.001842 0.000267 call vimtex#pos#set_cursor(a:delim) 54 0.002926 0.000459 let [l:match, l:lnum, l:cnum] = a:delim.get_matching() 54 0.002242 0.000286 call vimtex#pos#set_cursor(l:save_pos) " " Create the match result " 54 0.000398 let l:matching = deepcopy(a:delim) 54 0.000126 let l:matching.lnum = l:lnum 54 0.000103 let l:matching.cnum = l:cnum 54 0.000114 let l:matching.match = l:match 54 0.000124 let l:matching.corr = a:delim.match 54 0.000135 let l:matching.side = a:delim.is_open ? 'close' : 'open' 54 0.000111 let l:matching.is_open = !a:delim.is_open 54 0.000116 let l:matching.re.corr = a:delim.re.this 54 0.000111 let l:matching.re.this = a:delim.re.corr 54 0.000115 if l:matching.type ==# 'delim' 54 0.000110 let l:matching.corr_delim = a:delim.delim 54 0.000109 let l:matching.corr_mod = a:delim.mod 54 0.000101 let l:matching.delim = a:delim.corr_delim 54 0.000099 let l:matching.mod = a:delim.corr_mod 54 0.000164 elseif l:matching.type ==# 'env' && has_key(l:matching, 'name') if l:matching.is_open let l:matching.env_cmd = vimtex#cmd#get_at(l:lnum, l:cnum) else unlet l:matching.env_cmd endif endif 54 0.000076 return l:matching FUNCTION vimtex#pos#set_cursor() Called 433 times Total time: 0.014961 Self time: 0.002955 count total (s) self (s) 433 0.014773 0.002767 call cursor(s:parse_args(a:000)) FUNCTION 9_go() Called 1 time Total time: 0.203777 Self time: 0.007558 count total (s) self (s) 1 0.000011 if a:0 let [lnum1, lnum2] = [a:type, a:1] else 1 0.000010 let [lnum1, lnum2] = [line("'["), line("']")] 1 0.000001 endif 1 0.000053 0.000014 let [l_, r_] = s:surroundings() 1 0.000003 let uncomment = 2 11 0.000019 for lnum in range(lnum1,lnum2) 10 0.000300 let line = matchstr(getline(lnum),'\S.*\s\@ 2 && l.r !~# '\\' let line = substitute(line,'\M'.r[0:-2].'\zs\d\*\ze'.r[-1:-1].'\|'.l[0].'\zs\d\*\ze'.l[1:-1],'\=substitute(submatch(0)+1-uncomment,"^0$\\|^-\\d*$","","")','g') endif 10 0.000013 if uncomment 10 0.000348 let line = substitute(line,'\S.*\s\@124_text_between() Called 138 times Total time: 0.004487 Self time: 0.004487 count total (s) self (s) 138 0.000687 let [l1, c1] = [a:p1.lnum, a:p1.cnum - (a:0 > 0)] 138 0.000579 let [l2, c2] = [a:p2.lnum, a:p2.cnum - (a:0 <= 0)] 138 0.000514 let lines = getline(l1, l2) 138 0.000302 if !empty(lines) 138 0.000674 let lines[0] = strpart(lines[0], c1) 138 0.000818 let lines[-1] = strpart(lines[-1], 0, l1 == l2 ? c2 - c1 : c2) 138 0.000140 endif 138 0.000444 return join(lines, '') FUNCTION 127_parser_delim_get_corr() Called 81 times Total time: 0.004854 Self time: 0.004854 count total (s) self (s) 81 0.000213 let l:type = a:0 > 0 ? a:1 : 'delim_all' 432 0.000664 for l:pair in g:vimtex#delim#lists[l:type].name 432 0.001171 if a:delim ==# l:pair[0] 81 0.000174 return l:pair[1] elseif a:delim ==# l:pair[1] return l:pair[0] endif 351 0.000236 endfor FUNCTION 124_get_cmd_name() Called 27 times Total time: 0.000801 Self time: 0.000801 count total (s) self (s) 27 0.000368 let [l:lnum, l:cnum] = searchpos('\v\\\a+\*?', a:next ? 'nW' : 'cbnW') 27 0.000329 let l:match = matchstr(getline(l:lnum), '^\v\\\a*\*?', l:cnum-1) 27 0.000077 return [l:lnum, l:cnum, l:match] FUNCTION 188() Called 2 times Total time: 0.000033 Self time: 0.000033 count total (s) self (s) 2 0.000014 silent! call matchdelete(w:vimtex_match_id1) 2 0.000010 silent! call matchdelete(w:vimtex_match_id2) 2 0.000005 unlet! w:vimtex_match_id1 2 0.000003 unlet! w:vimtex_match_id2 FUNCTION 189() Called 2 times Total time: 0.001276 Self time: 0.000045 count total (s) self (s) 2 0.000045 0.000012 call self.clear() 2 0.000675 0.000014 if vimtex#util#in_comment() | return | endif 1 0.000548 0.000011 let l:current = vimtex#delim#get_current('all', 'both') 1 0.000004 if empty(l:current) | return | endif let l:corresponding = vimtex#delim#get_matching(l:current) if empty(l:corresponding) | return | endif let [l:open, l:close] = l:current.is_open ? [l:current, l:corresponding] : [l:corresponding, l:current] let w:vimtex_match_id1 = matchadd('MatchParen', '\%' . l:open.lnum . 'l\%' . l:open.cnum . 'c' . l:open.re.this) let w:vimtex_match_id2 = matchadd('MatchParen', '\%' . l:close.lnum . 'l\%' . l:close.cnum . 'c' . l:close.re.this) FUNCTION vimtex#fold#text() Called 30 times Total time: 0.083295 Self time: 0.002657 count total (s) self (s) 30 0.000125 let l:line = getline(v:foldstart) 30 0.000217 let l:level = v:foldlevel > 1 ? repeat('-', v:foldlevel-2) . g:vimtex_fold_levelmarker : '' 72 0.000141 for l:type in b:vimtex.fold_types_ordered 72 0.001369 if l:line =~# l:type.re.start 30 0.080875 0.000237 let l:text = l:type.text(l:line, l:level) 60 0.000129 if !empty(l:text) | return l:text | endif endif 42 0.000036 endfor FUNCTION 162_cursormoved_delayed_cb() Called 2 times Total time: 0.000474 Self time: 0.000109 count total (s) self (s) 2 0.000060 if getpos('.') == s:cursormoved_last_pos 2 0.000401 0.000036 call neomake#CursorMoved() 2 0.000003 endif FUNCTION 9_strip_white_space() Called 10 times Total time: 0.000185 Self time: 0.000185 count total (s) self (s) 10 0.000041 let [l, r] = [a:l, a:r] 10 0.000084 if stridx(a:line,l) == -1 && stridx(a:line,l[0:-2]) == 0 && a:line[strlen(a:line)-strlen(r[1:]):-1] == r[1:] return [l[0:-2], r[1:]] endif 10 0.000019 return [l, r] FUNCTION vimtex#delim#get_current() Called 1 time Total time: 0.000537 Self time: 0.000015 count total (s) self (s) 1 0.000537 0.000015 return s:get_delim(extend({ 'direction' : 'current', 'type' : a:type, 'side' : a:side,}, get(a:, '1', {}))) FUNCTION lightline#mode() Called 7 times Total time: 0.000041 Self time: 0.000041 count total (s) self (s) 7 0.000036 return get(s:lightline.mode_map, mode(), '') FUNCTION vimtex#fold#level() Called 2604 times Total time: 0.411949 Self time: 0.211454 count total (s) self (s) 2604 0.008370 let l:line = getline(a:lnum) " Filter out lines that do not start any folds (optimization) 2604 0.142610 if l:line !~# b:vimtex.fold_re | return '=' | endif " Never fold \begin|end{document} 1040 0.007038 if l:line =~# '^\s*\\\%(begin\|end\){document}' 2 0.000002 return 0 endif 3656 0.005699 for l:type in b:vimtex.fold_types_ordered 3500 0.220599 0.020104 let l:value = l:type.level(l:line, a:lnum) 4382 0.011395 if !empty(l:value) | return l:value | endif 2618 0.002032 endfor " Return foldlevel of previous line 156 0.000194 return '=' FUNCTION 124_get_cmd_part() Called 81 times Total time: 0.067260 Self time: 0.004379 count total (s) self (s) 81 0.000880 0.000389 let l:save_pos = vimtex#pos#get_cursor() 81 0.002732 0.000393 call vimtex#pos#set_cursor(a:start_pos) 81 0.042985 0.000453 let l:open = vimtex#delim#get_next('delim_tex', 'open') 81 0.003448 0.000443 call vimtex#pos#set_cursor(l:save_pos) " " Ensure that the delimiter " 1) exists, " 2) is of the right type, " 3) and is the next non-whitespace character. " 81 0.002926 0.001056 if empty(l:open) || l:open.match !=# a:part || strlen(substitute( s:text_between(a:start_pos, l:open), ' ', '', 'g')) != 0 27 0.000041 return {} endif 54 0.011266 0.000379 let l:close = vimtex#delim#get_matching(l:open) 54 0.000125 if empty(l:close) return {} endif 54 0.002231 0.000474 return { 'open' : l:open, 'close' : l:close, 'text' : s:text_between(l:open, l:close),} FUNCTION vimtex#pos#val() Called 54 times Total time: 0.002064 Self time: 0.000685 count total (s) self (s) 54 0.001745 0.000366 let [l:lnum, l:cnum; l:rest] = s:parse_args(a:000) 54 0.000252 return 100000*l:lnum + min([l:cnum, 90000]) FUNCTION neomake#CursorMoved() Called 2 times Total time: 0.000365 Self time: 0.000033 count total (s) self (s) 2 0.000361 0.000029 call neomake#EchoCurrentError() FUNCTION 124() Called 3 times Total time: 0.000054 Self time: 0.000054 count total (s) self (s) 3 0.000050 return a:line =~# self.re.parser1 ? ' ' . matchstr(a:line, self.re.parser1 . '\s*\zs.*') : ' ' . matchstr(a:line, self.re.parser2) FUNCTION vimtex#pos#get_cursor() Called 271 times Total time: 0.001713 Self time: 0.001713 count total (s) self (s) 271 0.001591 return exists('*getcurpos') ? getcurpos() : getpos('.') FUNCTION 131_parse_args() Called 514 times Total time: 0.014092 Self time: 0.013385 count total (s) self (s) " " The arguments should be in one of the following forms (when unpacked): " " [lnum, cnum] " [bufnum, lnum, cnum, ...] " {'lnum' : lnum, 'cnum' : cnum} " 514 0.001305 if len(a:args) > 1 27 0.000107 return s:parse_args([a:args]) elseif len(a:args) == 1 487 0.002187 if type(a:args[0]) == type({}) 162 0.000995 return [get(a:args[0], 'lnum'), get(a:args[0], 'cnum')] else 325 0.000996 if len(a:args[0]) == 2 27 0.000059 return a:args[0] else 298 0.000879 return a:args[0][1:] endif endif else return a:args endif FUNCTION lightline#update_once() Called 2 times Total time: 0.000015 Self time: 0.000015 count total (s) self (s) 2 0.000008 if !exists('w:lightline') || w:lightline call lightline#update() endif FUNCTION 138() Called 338 times Total time: 0.011188 Self time: 0.011188 count total (s) self (s) 338 0.007173 if a:line =~# self.re.start 40 0.000093 let self.opened += 1 40 0.000060 return 'a1' elseif self.opened > 0 && a:line =~# self.re.end 40 0.000090 let self.opened -= 1 40 0.000054 return 's1' endif FUNCTION neomake#EchoCurrentError() Called 2 times Total time: 0.000332 Self time: 0.000127 count total (s) self (s) " a:1 might be a timer from the VimResized event. 2 0.000015 let force = a:0 ? a:1 : 0 2 0.000017 if !force && !get(g:, 'neomake_echo_current_error', 1) return endif 2 0.000240 0.000035 let message = neomake#GetCurrentErrorMsg() 2 0.000009 if empty(message) 2 0.000012 if exists('s:neomake_last_echoed_error') echon '' unlet s:neomake_last_echoed_error endif 2 0.000002 return endif if !force && exists('s:neomake_last_echoed_error') && s:neomake_last_echoed_error == message return endif let s:neomake_last_echoed_error = message call neomake#utils#WideMessage(message) FUNCTION 124_get_cmd() Called 27 times Total time: 0.071779 Self time: 0.002858 count total (s) self (s) 27 0.001027 0.000226 let [lnum, cnum, match] = s:get_cmd_name(a:direction ==# 'next') 27 0.000089 if lnum == 0 | return {} | endif 27 0.000283 let res = { 'name' : match, 'pos_start' : { 'lnum' : lnum, 'cnum' : cnum }, 'pos_end' : { 'lnum' : lnum, 'cnum' : cnum + strlen(match) - 1 }, 'args' : [],} " Environments always start with environment name and allows option " afterwords 27 0.000061 if res.name ==# '\begin' let arg = s:get_cmd_part('{', res.pos_end) call add(res.args, arg) let res.pos_end.lnum = arg.close.lnum let res.pos_end.cnum = arg.close.cnum endif " Get options 27 0.024612 0.000193 let res.opt = s:get_cmd_part('[', res.pos_end) 27 0.000067 if !empty(res.opt) 27 0.000061 let res.pos_end.lnum = res.opt.close.lnum 27 0.000045 let res.pos_end.cnum = res.opt.close.cnum 27 0.000020 endif " Get arguments 27 0.026603 0.000182 let arg = s:get_cmd_part('{', res.pos_end) 54 0.000130 while !empty(arg) 27 0.000082 call add(res.args, arg) 27 0.000059 let res.pos_end.lnum = arg.close.lnum 27 0.000049 let res.pos_end.cnum = arg.close.cnum 27 0.016725 0.000305 let arg = s:get_cmd_part('{', res.pos_end) 27 0.000029 endwhile " Include entire cmd text 27 0.001077 0.000217 let res.text = s:text_between(res.pos_start, res.pos_end, 1) 27 0.000034 return res FUNCTION neomake#CursorMovedDelayed() Called 2 times Total time: 0.000047 Self time: 0.000047 count total (s) self (s) 2 0.000008 if exists('s:cursormoved_timer') 2 0.000005 call timer_stop(s:cursormoved_timer) 2 0.000001 endif 2 0.000021 let s:cursormoved_timer = timer_start(get(g:, 'neomake_cursormoved_delay', 100), function('s:cursormoved_delayed_cb')) 2 0.000009 let s:cursormoved_last_pos = getpos('.') FUNCTION vimtex#cmd#get_at() Called 27 times Total time: 0.079223 Self time: 0.000702 count total (s) self (s) 27 0.000336 0.000155 let l:pos_saved = vimtex#pos#get_cursor() 27 0.001378 0.000165 call call('vimtex#pos#set_cursor', a:000) 27 0.076378 0.000170 let l:cmd = vimtex#cmd#get_current() 27 0.001058 0.000139 call vimtex#pos#set_cursor(l:pos_saved) 27 0.000045 return l:cmd FUNCTION vimtex#cmd#get_current() Called 27 times Total time: 0.076208 Self time: 0.001250 count total (s) self (s) 27 0.000298 0.000125 let l:save_pos = vimtex#pos#get_cursor() 27 0.001319 0.000179 let l:pos_val_cursor = vimtex#pos#val(l:save_pos) 27 0.000050 let l:depth = 3 27 0.000047 while l:depth > 0 27 0.000045 let l:depth -= 1 27 0.071954 0.000175 let l:cmd = s:get_cmd('prev') 27 0.000104 if empty(l:cmd) | break | endif 27 0.001085 0.000161 let l:pos_val = vimtex#pos#val(l:cmd.pos_end) 27 0.000065 if l:pos_val >= l:pos_val_cursor 27 0.001071 0.000129 call vimtex#pos#set_cursor(l:save_pos) 27 0.000056 return l:cmd else call vimtex#pos#set_cursor(vimtex#pos#prev(l:cmd.pos_start)) endif endwhile call vimtex#pos#set_cursor(l:save_pos) return {} FUNCTION 127_parser_delim_get_regexp() Called 162 times Total time: 0.005937 Self time: 0.005937 count total (s) self (s) 162 0.000437 let l:type = a:0 > 0 ? a:1 : 'delim_all' " First check for unmatched math delimiter 162 0.000324 if a:delim ==# '.' return g:vimtex#delim#re.delim_math[a:side ? 'open' : 'close'] endif " Next check normal delimiters 162 0.003432 let l:index = index(map(copy(g:vimtex#delim#lists[l:type].name), 'v:val[' . a:side . ']'), a:delim) 162 0.000801 return l:index >= 0 ? g:vimtex#delim#lists[l:type].re[l:index][a:side] : '' FUNCTION 9_surroundings() Called 1 time Total time: 0.000039 Self time: 0.000039 count total (s) self (s) 1 0.000038 return split(get(b:, 'commentary_format', substitute(substitute( &commentstring, '\S\zs%s',' %s','') ,'%s\ze\S', '%s ', '')), '%s', 1) FUNCTIONS SORTED ON TOTAL TIME count total (s) self (s) function 2604 0.411949 0.211454 vimtex#fold#level() 1 0.203777 0.007558 9_go() 30 0.083295 0.002657 vimtex#fold#text() 27 0.080584 0.001361 122() 27 0.079223 0.000702 vimtex#cmd#get_at() 27 0.076208 0.001250 vimtex#cmd#get_current() 27 0.071779 0.002858 124_get_cmd() 81 0.067260 0.004379 124_get_cmd_part() 156 0.061063 113() 156 0.059637 0.001220 133() 156 0.058417 134() 81 0.042532 0.001086 vimtex#delim#get_next() 82 0.041968 0.017320 127_get_delim() 1038 0.031405 141() 81 0.021115 0.010324 127_parser_delim() 1038 0.020156 121() 433 0.014961 0.002955 vimtex#pos#set_cursor() 514 0.014092 0.013385 131_parse_args() 338 0.011188 138() 54 0.010887 0.004542 vimtex#delim#get_matching() FUNCTIONS SORTED ON SELF TIME count total (s) self (s) function 2604 0.411949 0.211454 vimtex#fold#level() 156 0.061063 113() 156 0.058417 134() 1038 0.031405 141() 1038 0.020156 121() 82 0.041968 0.017320 127_get_delim() 514 0.014092 0.013385 131_parse_args() 338 0.011188 138() 81 0.021115 0.010324 127_parser_delim() 1 0.203777 0.007558 9_go() 162 0.005937 127_parser_delim_get_regexp() 81 0.004854 127_parser_delim_get_corr() 258 0.004673 131() 54 0.010887 0.004542 vimtex#delim#get_matching() 138 0.004487 124_text_between() 258 0.004440 123() 258 0.004434 129() 81 0.067260 0.004379 124_get_cmd_part() 258 0.007933 0.003499 126() 433 0.014961 0.002955 vimtex#pos#set_cursor() ```

Apparently the problematic function is vimtex#fold#level() which is evaluated 2604 times and results in 0.42sec. When there are more folds, this gets even worse (several seconds).

using tcomment instead of commentary.vim, nothing changes and the summary is


count  total (s)   self (s)
    1              0.000006     unlet! s:temp_options s:options_cache

FUNCTIONS SORTED ON TOTAL TIME
count  total (s)   self (s)  function
 2604   0.421835   0.217044  vimtex#fold#level()
    1   0.217931   0.000050  TCommentOpFunc_gc()
    1   0.217835   0.000286  tcomment#Operator()
    1   0.217512   0.007903  tcomment#Comment()
   40   0.130173   0.004361  vimtex#fold#text()
   36   0.125733   0.002158  124()
   36   0.123575   0.001076  vimtex#cmd#get_at()
   36   0.118899   0.001927  vimtex#cmd#get_current()
   36   0.112141   0.004331  <SNR>61_get_cmd()
  108   0.105186   0.006824  <SNR>61_get_cmd_part()
  108   0.066630   0.001964  vimtex#delim#get_next()
  109   0.065222   0.027175  <SNR>64_get_delim()
  156   0.062177             115()
  156   0.060951   0.001213  135()
  156   0.059738             136()
  108   0.032610   0.015845  <SNR>64_parser_delim()
 1038   0.032087             143()
  577   0.023308   0.004634  vimtex#pos#set_cursor()
  685   0.021826   0.020730  <SNR>68_parse_args()
 1038   0.020759             123()

FUNCTIONS SORTED ON SELF TIME
count  total (s)   self (s)  function
 2604   0.421835   0.217044  vimtex#fold#level()
  156              0.062177  115()
  156              0.059738  136()
 1038              0.032087  143()
  109   0.065222   0.027175  <SNR>64_get_delim()
 1038              0.020759  123()
  685   0.021826   0.020730  <SNR>68_parse_args()
  108   0.032610   0.015845  <SNR>64_parser_delim()
  338              0.011372  140()
  216              0.009055  <SNR>64_parser_delim_get_regexp()
    1   0.217512   0.007903  tcomment#Comment()
  108              0.007710  <SNR>64_parser_delim_get_corr()
   72   0.016849   0.007118  vimtex#delim#get_matching()
  184              0.006997  <SNR>61_text_between()
  108   0.105186   0.006824  <SNR>61_get_cmd_part()
  258              0.004700  133()
  577   0.023308   0.004634  vimtex#pos#set_cursor()
  258              0.004528  125()
  258              0.004384  131()
   40   0.130173   0.004361  vimtex#fold#text()
CLICK HERE FOR FULL PROFILING REPORT ``` FUNCTION 92_ExtractCommentsPart() Called 1 time Total time: 0.000345 Self time: 0.000345 count total (s) self (s) 1 0.000013 let comments = { 'line': {'string': '', 'flags': ''}, 's': {'string': '', 'flags': ''}, 'm': {'string': '', 'flags': ''}, 'e': {'string': '', 'flags': ''}, } 1 0.000003 let rx = '\C\([nbfsmelrOx0-9-]*\):\(\%(\\,\|[^,]\)*\)' 1 0.000032 let comparts = split(&l:comments, rx .'\zs,') 5 0.000012 for part in comparts 4 0.000079 let ml = matchlist(part, '^'. rx .'$') 4 0.000021 let flags = ml[1] 4 0.000038 let string = substitute(ml[2], '\\,', ',', 'g') 4 0.000020 if flags !~# 'O' 1 0.000010 let flag = matchstr(flags, '^[sme]') 1 0.000004 if empty(flag) 1 0.000002 let flag = 'line' 1 0.000002 endif 1 0.000009 let string = substitute(string, '%', '%%', 'g') 1 0.000008 let comments[flag] = {'string': string, 'flags': flags} 1 0.000001 endif 4 0.000004 endfor 1 0.000003 return comments FUNCTION 92_ConstructFromCommentsOption() Called 1 time Total time: 0.000571 Self time: 0.000107 count total (s) self (s) " TLogVAR a:comment_mode 1 0.000003 let cdef = {} 1 0.000365 0.000020 let comments = s:ExtractCommentsPart() " TLogVAR comments 1 0.000009 if a:comment_mode =~? '[bi]' && !empty(comments.s.string) let cdef.mode = a:comment_mode let cdef.commentstring = comments.s.string .' %s '. comments.e.string if a:comment_mode =~? '[b]' && !empty(comments.m.string) let mshift = str2nr(matchstr(comments.s.flags, '\(^\|[^-]\)\zs\d\+')) let mindent = repeat(' ', mshift) let cdef.middle = mindent . comments.m.string endif " TLogVAR cdef return cdef endif 1 0.000002 let ccmodes = 'r' 1 0.000004 if !empty(comments.e.string) let ccmodes .= 'bi' endif 1 0.000130 0.000011 let comment_mode = s:GuessCommentMode(a:comment_mode, ccmodes) 1 0.000004 if !empty(comments.line.string) 1 0.000004 let cdef.mode = comment_mode 1 0.000005 let cdef.commentstring = comments.line.string .' %s' 1 0.000003 elseif !empty(comments.s.string) let cdef.mode = comment_mode let cdef.commentstring = comments.s.string .' %s '. comments.e.string endif 1 0.000001 return cdef FUNCTION 64_get_delim() Called 109 times Total time: 0.065222 Self time: 0.027175 count total (s) self (s) " " Arguments: " opts = { " 'direction' : next " prev " current " 'type' : env_tex " env_math " env_all " delim_tex " delim_math " delim_modq_math (possibly modified math delimiter) " delim_mod_math (modified math delimiter) " delim_all " all " 'side' : open " close " both " 'syn_exclude' : Don't match in given syntax " } " " Returns: " delim = { " type : env | delim " side : open | close " name : name of environment [only for type env] " lnum : number " cnum : number " match : unparsed matched delimiter " corr : corresponding delimiter " re : { " open : regexp for the opening part " close : regexp for the closing part " } " } " 109 0.001429 0.000621 let l:save_pos = vimtex#pos#get_cursor() 109 0.000470 let l:re = g:vimtex#delim#re[a:opts.type][a:opts.side] 109 0.000203 while 1 109 0.003121 let [l:lnum, l:cnum] = a:opts.direction ==# 'next' ? searchpos(l:re, 'cnW', line('.') + s:stopline) : a:opts.direction ==# 'prev' ? searchpos(l:re, 'bcnW', max([line('.') - s:stopline, 1])) : searchpos(l:re, 'bcnW', line('.')) 109 0.000434 if l:lnum == 0 | break | endif 108 0.000634 if has_key(a:opts, 'syn_exclude') && vimtex#util#in_syntax(a:opts.syn_exclude, l:lnum, l:cnum) call vimtex#pos#set_cursor(vimtex#pos#prev(l:lnum, l:cnum)) continue endif 108 0.000102 break endwhile 109 0.005282 0.000653 call vimtex#pos#set_cursor(l:save_pos) 109 0.001476 let l:match = matchstr(getline(l:lnum), '^' . l:re, l:cnum-1) 109 0.000749 if a:opts.direction ==# 'current' && l:cnum + strlen(l:match) + (mode() ==# 'i' ? 1 : 0) <= col('.') 1 0.000002 let l:match = '' 1 0.000002 let l:lnum = 0 1 0.000001 let l:cnum = 0 1 0.000001 endif 109 0.000574 let l:result = { 'type' : '', 'lnum' : l:lnum, 'cnum' : l:cnum, 'match' : l:match,} 546 0.000883 for l:type in s:types 545 0.006842 if l:match =~# '^' . l:type.re 108 0.034582 0.001972 let l:result = extend( l:type.parser(l:match, l:lnum, l:cnum, a:opts.side, a:opts.type, a:opts.direction), l:result, 'keep') 108 0.000165 break endif 437 0.000402 endfor 109 0.000433 return empty(l:result.type) ? {} : l:result FUNCTION 92_AltFiletype() Called 1 time Total time: 0.000223 Self time: 0.000079 count total (s) self (s) 1 0.000160 0.000016 let filetype = empty(a:filetype) ? s:GetFiletype(&filetype, [-1]) : a:filetype 1 0.000002 Tlibtrace 'tcomment', a:filetype, filetype 1 0.000008 if g:tcommentGuessFileType || (exists('g:tcommentGuessFileType_'. filetype) && g:tcommentGuessFileType_{filetype} =~# '[^0]') if g:tcommentGuessFileType_{filetype} == 1 if filetype =~# '^.\{-}\..\+$' let alt_filetype = s:GetFiletype(filetype) else let alt_filetype = '' endif else let alt_filetype = g:tcommentGuessFileType_{filetype} endif Tlibtrace 'tcomment', 1, alt_filetype return [1, alt_filetype] elseif filetype =~# '^.\{-}\..\+$' " Unfortunately the handling of "sub-filetypes" isn't " consistent. Some use MAJOR.MINOR, others use MINOR.MAJOR. let alt_filetype = s:GetFiletype(filetype) " if alt_filetype == filetype " let alt_filetype = s:GetFiletype(filetype, 1) " if alt_filetype == a:filetype " let alt_filetype = s:GetFiletype(filetype, 0) " endif " endif Tlibtrace 'tcomment', 2, alt_filetype return [1, alt_filetype] else 1 0.000003 Tlibtrace 'tcomment', 3, '' 1 0.000003 return [0, ''] endif FUNCTION 61_get_cmd_part() Called 108 times Total time: 0.105186 Self time: 0.006824 count total (s) self (s) 108 0.001370 0.000580 let l:save_pos = vimtex#pos#get_cursor() 108 0.004229 0.000581 call vimtex#pos#set_cursor(a:start_pos) 108 0.067357 0.000727 let l:open = vimtex#delim#get_next('delim_tex', 'open') 108 0.005545 0.000726 call vimtex#pos#set_cursor(l:save_pos) " " Ensure that the delimiter " 1) exists, " 2) is of the right type, " 3) and is the next non-whitespace character. " 108 0.004537 0.001670 if empty(l:open) || l:open.match !=# a:part || strlen(substitute( s:text_between(a:start_pos, l:open), ' ', '', 'g')) != 0 36 0.000063 return {} endif 72 0.017438 0.000589 let l:close = vimtex#delim#get_matching(l:open) 72 0.000194 if empty(l:close) return {} endif 72 0.003514 0.000755 return { 'open' : l:open, 'close' : l:close, 'text' : s:text_between(l:open, l:close),} FUNCTION 92_AddModeExtra() Called 5 times Total time: 0.000149 Self time: 0.000149 count total (s) self (s) " TLogVAR a:comment_mode, a:extra 5 0.000013 if a:beg == a:end let extra = substitute(a:extra, '\C[B]', '', 'g') else 5 0.000056 let extra = substitute(a:extra, '\C[IR]', '', 'g') 5 0.000008 endif 5 0.000018 if empty(extra) 5 0.000013 return a:comment_mode else let comment_mode = a:comment_mode if extra =~# 'B' let comment_mode = substitute(comment_mode, '\c[gir]', '', 'g') endif if extra =~# '[IR]' let comment_mode = substitute(comment_mode, '\c[gb]', '', 'g') endif if extra =~# '[BLIRK]' && comment_mode =~# 'G' let comment_mode = substitute(comment_mode, '\c[G]', '', 'g') endif let rv = substitute(comment_mode, '\c['. extra .']', '', 'g') . extra " TLogVAR a:comment_mode, a:extra, comment_mode, extra, rv return rv endif FUNCTION vimtex#util#in_syntax() Called 2 times Total time: 0.000654 Self time: 0.000654 count total (s) self (s) " Usage: vimtex#util#in_syntax(name, [line, col]) " Get position and correct it if necessary 2 0.000013 let l:pos = a:0 > 0 ? [a:1, a:2] : [line('.'), col('.')] 2 0.000006 if mode() ==# 'i' let l:pos[1] -= 1 endif 2 0.000015 call map(l:pos, 'max([v:val, 1])') " Check syntax at position 2 0.000607 return match(map(synstack(l:pos[0], l:pos[1]), "synIDattr(v:val, 'name')"), '^' . a:name) >= 0 FUNCTION tcomment#GetCommentDef() Called 1 time Total time: 0.000016 Self time: 0.000016 count total (s) self (s) 1 0.000006 if exists('b:tcomment_def_'. a:name) return b:tcomment_def_{a:name} else 1 0.000006 return get(s:definitions, a:name, a:0 >= 1 ? a:1 : '') endif FUNCTION 92_StartPosRx() Called 1 time Total time: 0.000046 Self time: 0.000015 count total (s) self (s) " TLogVAR a:comment_mode, a:line, a:col " if a:comment_mode =~# 'I' " return s:StartLineRx(a:line) . s:StartColRx(a:comment_mode, a:col) " else 1 0.000040 0.000009 let rv = s:StartColRx(a:comment_mode, a:col) " endif " TLogVAR rv 1 0.000002 return rv FUNCTION tcomment#Comment() Called 1 time Total time: 0.217512 Self time: 0.007903 count total (s) self (s) 1 0.000043 0.000013 let comment_mode0 = s:AddModeExtra((a:0 >= 1 ? a:1 : 'G'), g:tcommentModeExtra, a:beg, a:end) 1 0.000003 let comment_mode = comment_mode0 1 0.000005 let comment_anyway = a:0 >= 2 ? (a:2 ==# '!') : 0 " TLogVAR a:beg, a:end, comment_mode, comment_anyway, a:000 " save the cursor position 1 0.000004 if exists('w:tcomment_pos') 1 0.000006 let s:current_pos = copy(w:tcomment_pos) 1 0.000001 else let s:current_pos = getpos('.') endif " echom "DBG current_pos=" string(s:current_pos) 1 0.000006 let cursor_pos = getpos("'>") " TLogVAR cursor_pos 1 0.000003 let s:cursor_pos = [] 1 0.000006 if comment_mode =~# 'i' let blnum = line("'<") if blnum == line("'>") if virtcol('.') <= indent(blnum) let i_mode = 'G' else let i_mode = 'I' endif else let i_mode = 'G' endif let comment_mode = substitute(comment_mode, '\Ci', i_mode, 'g') " TLogVAR 1, comment_mode endif 1 0.000337 0.000021 let [lbeg, cbeg, lend, cend] = s:GetStartEnd(a:beg, a:end, comment_mode) " TLogVAR lbeg, cbeg, lend, cend, virtcol('$') 1 0.000006 if comment_mode ==? 'I' && comment_mode0 =~# 'i' && lbeg == lend && cend >= virtcol('$') - 1 let comment_mode = substitute(comment_mode, '\CI', cbeg <= 1 ? 'G' : 'R', 'g') " TLogVAR comment_mode endif 1 0.000029 0.000015 let mode_extra = s:GetTempOption('mode_extra', '') " TLogVAR mode_extra 1 0.000004 if !empty(mode_extra) let comment_mode = s:AddModeExtra(comment_mode, mode_extra, lbeg, lend) " TLogVAR "mode_extra", comment_mode unlet s:temp_options.mode_extra endif " get the correct commentstring 1 0.000005 let cdef = copy(g:tcommentOptions) " TLogVAR 1, cdef 1 0.000005 if exists('b:tcommentOptions') let cdef = extend(cdef, copy(b:tcommentOptions)) " TLogVAR 2, cdef endif 1 0.000004 if a:0 >= 3 && type(a:3) == 4 call extend(cdef, a:3) " TLogVAR 3, cdef else 1 0.001922 0.000018 let cdef0 = s:GetCommentDefinition(lbeg, lend, comment_mode) " TLogVAR 4.1, cdef, cdef0 1 0.000005 call extend(cdef, cdef0) " TLogVAR 4.2, cdef 1 0.000002 let ax = 3 1 0.000005 if a:0 >= 3 && !empty(a:3) && stridx(a:3, '=') == -1 let ax = 4 let cdef.begin = a:3 if a:0 >= 4 && !empty(a:4) && stridx(a:4, '=') == -1 let ax = 5 let cdef.end = a:4 endif endif " TLogVAR ax, a:0, a:000 1 0.000002 if a:0 >= ax " let cdef = extend(cdef, s:ParseArgs(lbeg, lend, comment_mode, a:000[ax - 1 : -1])) let cdef = s:ExtendCDef(lbeg, lend, comment_mode, cdef, s:ParseArgs(lbeg, lend, comment_mode, a:000[ax - 1 : -1])) " TLogVAR 5, cdef endif 1 0.000009 if !empty(get(cdef, 'begin', '')) || !empty(get(cdef, 'end', '')) let cdef.commentstring = s:EncodeCommentPart(get(cdef, 'begin', '')) . '%s' . s:EncodeCommentPart(get(cdef, 'end', '')) endif 1 0.000003 let comment_mode = cdef.mode " TLogVAR 2, comment_mode 1 0.000001 endif 1 0.000003 if empty(comment_mode) echohl WarningMsg echom 'TComment: Comment mode is not supported for the current filetype:' string(comment_mode) echohl NONE return endif 1 0.000004 if exists('s:temp_options') let cdef = s:ExtendCDef(lbeg, lend, comment_mode, cdef, s:temp_options) " TLogVAR 6, cdef " echom "DBG s:temp_options" string(s:temp_options) unlet s:temp_options endif " TLogVAR 7, cdef 1 0.000004 if has_key(cdef, 'whitespace') call s:SetWhitespaceMode(cdef) endif 1 0.000018 if !empty(filter(['count', 'cbeg', 'cend', 'cmid'], 'has_key(cdef, v:val)')) call s:RepeatCommentstring(cdef) endif " echom "DBG" string(a:000) 1 0.000068 0.000014 let cms0 = s:BlockGetCommentRx(cdef) " TLogVAR cms0 "" make whitespace optional; this conflicts with comments that require some "" whitespace 1 0.000014 let cmt_check = substitute(cms0, '\([ ]\)', '\1\\?', 'g') "" turn commentstring into a search pattern " TLogVAR cmt_check 1 0.000072 0.000014 let cmt_check = s:Printf1(cmt_check, '\(\_.\{-}\)') " TLogVAR cdef, cmt_check 1 0.000002 let s:cdef = cdef " set comment_mode " TLogVAR comment_mode 1 0.000632 0.000021 let [lbeg, lend, uncomment] = s:CommentDef(lbeg, lend, cmt_check, comment_mode, cbeg, cend) " TLogVAR lbeg, lend, cbeg, cend, uncomment, comment_mode, comment_anyway 1 0.000002 if uncomment 1 0.000006 if comment_mode =~# 'C' || comment_anyway let comment_do = 'c' else 1 0.000003 let comment_do = 'u' 1 0.000001 endif 1 0.000001 else if comment_mode =~# 'U' let comment_do = 'u' elseif comment_mode =~# 'K' let comment_do = 'k' else let comment_do = 'c' endif endif " TLogVAR comment_anyway, comment_mode, mode_extra, comment_do " " echom "DBG" string(s:cdef) 1 0.000002 if comment_do ==# 'c' && comment_mode !~# 'I' let cbeg = get(s:cdef, 'col', cbeg) endif " TLogVAR cbeg " go " TLogVAR comment_mode 1 0.000003 if comment_mode =~# 'B' " We want a comment block call s:CommentBlock(lbeg, lend, cbeg, cend, comment_mode, comment_do, cmt_check, s:cdef) else " We want commented lines " final search pattern for uncommenting 1 0.000005 let cmt_check = '\V\^\(\s\{-}\)'. cmt_check .'\$' " let cmt_check = escape(cmt_check, '"/\') " final pattern for commenting 1 0.000032 0.000014 let cmt_replace = s:GetCommentReplace(s:cdef, cms0) " TLogVAR cmt_replace " TLogVAR comment_mode, lbeg, cbeg, lend, cend 1 0.000003 let s:processline_lnum = lbeg 1 0.000048 0.000011 let end_rx = s:EndPosRx(comment_mode, lend, cend) 1 0.000005 let postfix_rx = end_rx ==# '\$' ? '' : '\.\*\$' 1 0.000060 0.000014 let prefix_rx = '\^\.\{-}' . s:StartPosRx(comment_mode, lbeg, cbeg) 1 0.000009 let comment_rx = '\V' . '\('. prefix_rx . '\)' .'\(' .'\(\_.\{-}\)' . end_rx .'\)' .'\(' . postfix_rx . '\)' " TLogVAR comment_rx, prefix_rx, end_rx, postfix_rx " let @x = comment_rx " DBG 11 0.000020 for lnum in range(lbeg, lend) 10 0.000034 let line0 = getline(lnum) " TLogVAR line0 10 0.000266 let lmatch = matchlist(line0, comment_rx) " TLogVAR lmatch 10 0.000038 if empty(lmatch) && g:tcomment#blank_lines >= 2 let lline0 = s:Strdisplaywidth(line0) " TLogVAR lline0, cbeg if lline0 < cbeg let line0 = line0 . repeat(' ', cbeg - lline0) let lmatch = [line0, line0, '', '', ''] " TLogVAR 'padded', line0, lmatch endif endif 10 0.000022 if !empty(lmatch) 10 0.002215 0.000151 let [part1, ok] = s:ProcessLine(comment_do, lmatch[2], cmt_check, cmt_replace) " TLogVAR part1, ok 10 0.000013 if ok 10 0.000058 let line1 = lmatch[1] . part1 . lmatch[4] 10 0.000023 if comment_do ==# 'u' 10 0.000021 if g:tcomment#rstrip_on_uncomment > 0 10 0.000062 if g:tcomment#rstrip_on_uncomment == 2 || line1 !~# '\S' let line1 = substitute(line1, '\s\+$', '', '') endif 10 0.000007 endif 10 0.000007 endif " TLogVAR line1 10 0.210808 0.006351 call setline(lnum, line1) 10 0.000011 endif 10 0.000008 endif 10 0.000008 endfor 1 0.000002 if comment_do ==# 'u' 1 0.000005 let postprocess_uncomment = get(cdef, 'postprocess_uncomment', '') 1 0.000003 if !empty(postprocess_uncomment) exec printf(postprocess_uncomment, lbeg, lend) endif 1 0.000000 endif 1 0.000000 endif " reposition cursor " TLogVAR 3, comment_mode " echom "DBG s:cursor_pos" string(s:cursor_pos) 1 0.000002 if !empty(s:cursor_pos) let cursor_pos = s:cursor_pos endif 1 0.000004 if comment_mode =~# '>' call setpos('.', cursor_pos) if comment_mode !~? 'i' && comment_mode =~# '>>' norm! l^ endif elseif comment_mode =~# '#' call setpos('.', cursor_pos) if exists('w:tcomment_pos') let w:tcomment_pos = cursor_pos endif else 1 0.000004 call setpos('.', s:current_pos) 1 0.000001 endif 1 0.000004 unlet! s:cursor_pos s:current_pos s:cdef FUNCTION 92_GuessCustomCommentString() Called 1 time Total time: 0.000346 Self time: 0.000158 count total (s) self (s) " TLogVAR a:ft, a:comment_mode, a:000 1 0.000004 let comment_mode = a:comment_mode 1 0.000043 0.000013 let custom_comment = tcomment#TypeExists(a:ft) 1 0.000037 0.000011 let custom_comment_mode = tcomment#TypeExists(a:ft, comment_mode) 1 0.000006 let supported_comment_mode = !empty(custom_comment_mode) ? comment_mode : '' " TLogVAR custom_comment, custom_comment_mode 1 0.000004 let default = a:0 >= 1 ? a:1 : '' 1 0.000010 let default_cdef = a:0 >= 2 ? a:2 : {} 1 0.000007 let default_supports_comment_mode = get(default_cdef, 'comment_mode', custom_comment_mode) " TLogVAR default, default_supports_comment_mode 1 0.000010 if comment_mode =~# '[ILB]' && !empty(custom_comment_mode) let def = tcomment#GetCommentDef(custom_comment_mode) " TLogVAR 1, def elseif !empty(custom_comment) 1 0.000030 0.000014 let def = tcomment#GetCommentDef(custom_comment) 1 0.000128 0.000012 let comment_mode = s:GuessCommentMode(comment_mode, supported_comment_mode) " TLogVAR 3, def, comment_mode 1 0.000003 elseif !empty(default) if empty(default_cdef) let def = {'commentstring': default} else let def = default_cdef endif let comment_mode = s:GuessCommentMode(comment_mode, default_supports_comment_mode) " TLogVAR 4, def, comment_mode else let def = {} let comment_mode = s:GuessCommentMode(comment_mode, '') " TLogVAR 5, def, comment_mode endif 1 0.000004 let cdef = copy(def) 1 0.000004 if !has_key(cdef, 'mode') 1 0.000004 let cdef.mode = comment_mode 1 0.000001 endif 1 0.000003 let cdef.filetype = a:ft " TLogVAR cdef 1 0.000001 return cdef FUNCTION 10() Called 4 times Total time: 0.000097 Self time: 0.000097 count total (s) self (s) 4 0.000011 if s:delayed_show.show_timer 4 0.000015 call timer_stop(s:delayed_show.show_timer) 4 0.000003 endif 4 0.000004 if s:delayed_show.hide_timer call timer_stop(s:delayed_show.hide_timer) call s:delayed_show.hide_cb(s:delayed_show.hide_timer) endif 4 0.000021 let s:delayed_show.show_timer = timer_start(g:matchparen_show_delay, s:delayed_show.show_cb) 4 0.000017 let s:delayed_show.show_pos = getpos('.') FUNCTION 11() Called 2 times Total time: 0.000659 Self time: 0.000183 count total (s) self (s) 2 0.000058 if s:delayed_show.show_pos == getpos('.') 2 0.000545 0.000069 call s:Highlight_Matching_Pair() " If something was highlighted, start a timer to hide it. 2 0.000013 if exists('w:paren_hl_on') && w:paren_hl_on if s:delayed_show.hide_timer call timer_stop(s:delayed_show.hide_timer) endif let s:delayed_show.hide_timer = timer_start(g:matchparen_hide_delay, s:delayed_show.hide_cb) endif 2 0.000002 endif FUNCTION 92_BlockGetCommentString() Called 1 time Total time: 0.000022 Self time: 0.000022 count total (s) self (s) 1 0.000004 if has_key(a:cdef, 'middle') return a:cdef.commentstring else 1 0.000014 return matchstr(a:cdef.commentstring, '^.\{-}\ze\(\n\|$\)') endif FUNCTION 115() Called 156 times Total time: 0.062177 Self time: 0.062177 count total (s) self (s) 156 0.059747 let l:env = matchstr(a:line, self.re.name) 156 0.000606 if !empty(l:env) && self.validate(l:env) if a:line =~# self.re.start if a:line !~# '\\end' return 'a1' endif elseif a:line =~# self.re.end if a:line !~# '\\begin' return 's1' endif endif endif FUNCTION 92_GuessCommentMode() Called 3 times Total time: 0.000358 Self time: 0.000276 count total (s) self (s) " TLogVAR a:comment_mode, a:supported_comment_modes 3 0.000040 let special = substitute(a:comment_mode, '\c[^ukc]', '', 'g') 3 0.000015 let cmode = tolower(a:comment_mode) 3 0.000038 let ccmodes = split(tolower(a:supported_comment_modes), '\zs') 3 0.000032 let ccmodes = filter(ccmodes, 'stridx(cmode, v:val) != -1') 3 0.000035 let guess = substitute(a:comment_mode, '\w\+', 'G', 'g') " TLogVAR ccmodes, guess 3 0.000020 if a:comment_mode =~# '[BR]' let rv = !empty(ccmodes) ? a:comment_mode : guess elseif a:comment_mode =~# '[I]' let rv = !empty(ccmodes) ? a:comment_mode : '' else 3 0.000008 let rv = guess 3 0.000003 endif 3 0.000110 0.000028 return s:AddModeExtra(rv, special, 0, 1) FUNCTION vimtex#util#in_comment() Called 2 times Total time: 0.000676 Self time: 0.000022 count total (s) self (s) 2 0.000675 0.000021 return call('vimtex#util#in_syntax', ['texComment'] + a:000) FUNCTION 123() Called 1038 times Total time: 0.020759 Self time: 0.020759 count total (s) self (s) 1038 0.012133 if a:line =~# self.re.start 360 0.000726 let self.opened = 1 360 0.000469 return 'a1' elseif self.opened && a:line =~# self.re.end 340 0.000677 let self.opened = 0 340 0.000426 return 's1' endif FUNCTION 124() Called 36 times Total time: 0.125733 Self time: 0.002158 count total (s) self (s) 36 0.000427 let l:col = strlen(matchstr(a:line, '^\s*')) + 1 36 0.125266 0.001691 return matchstr(a:line, self.re.text) . '[...]{' . vimtex#cmd#get_at(v:foldstart, l:col).args[0].text . '}' FUNCTION 125() Called 258 times Total time: 0.004528 Self time: 0.004528 count total (s) self (s) 258 0.001953 if a:line =~# self.re.start 60 0.000126 let s:self.opened = 1 60 0.000071 return 'a1' elseif a:line =~# self.re.end 42 0.000087 let s:self.opened = 0 42 0.000056 return 's1' endif FUNCTION 126() Called 4 times Total time: 0.000079 Self time: 0.000079 count total (s) self (s) 4 0.000076 return a:line =~# self.re.parser1 ? ' ' . matchstr(a:line, self.re.parser1 . '\s*\zs.*') : ' ' . matchstr(a:line, self.re.parser2) FUNCTION 128() Called 258 times Total time: 0.008217 Self time: 0.003833 count total (s) self (s) 258 0.005540 0.001156 call self.refresh() " Fold chapters and sections 258 0.000919 for [l:part, l:level] in self.folds if a:line =~# l:part return '>' . l:level endif endfor FUNCTION 92_Strdisplaywidth() Called 2 times Total time: 0.000013 Self time: 0.000013 count total (s) self (s) 2 0.000012 return call('strdisplaywidth', a:000) FUNCTION 131() Called 258 times Total time: 0.004384 Self time: 0.004384 count total (s) self (s) " " Parse current buffer to find which sections to fold and their levels. The " patterns are predefined to optimize the folding. " " We ignore top level parts such as \frontmatter, \appendix, \part, and " similar, unless there are at least two such commands in a document. " " Only refresh if file has been changed 258 0.002516 let l:time = getftime(expand('%')) 258 0.000784 if l:time == self.time | return | endif let self.time = l:time " Initialize let self.folds = [] let level = 0 let buffer = getline(1,'$') " Parse part commands (frontmatter, appendix, etc) " Note: We want a minimum of two top level parts let lines = filter(copy(buffer), 'v:val =~ ''' . self.re.parts . '''') if len(lines) >= 2 let level += 1 call insert(self.folds, [self.re.parts, level]) endif " Parse section commands (part, chapter, [sub...]section) let lines = filter(copy(buffer), 'v:val =~ ''' . self.re.sections . '''') for part in self.sections let partpattern = '^\s*\%(\\\|% Fake\)' . part . ':\?\>' for line in lines if line =~# partpattern let level += 1 call insert(self.folds, [partpattern, level]) break endif endfor endfor FUNCTION 133() Called 258 times Total time: 0.004700 Self time: 0.004700 count total (s) self (s) 258 0.002692 if a:line =~# self.re.start let self.opened = 1 return 'a1' elseif self.opened && a:line =~# self.re.end let self.opened = 0 return 's1' endif FUNCTION 136() Called 156 times Total time: 0.059738 Self time: 0.059738 count total (s) self (s) 156 0.059113 if a:line =~# self.re.start let self.opened = 1 return 'a1' endif FUNCTION vimtex#delim#get_next() Called 108 times Total time: 0.066630 Self time: 0.001964 count total (s) self (s) 108 0.066584 0.001918 return s:get_delim(extend({ 'direction' : 'next', 'type' : a:type, 'side' : a:side,}, get(a:, '1', {}))) FUNCTION 13_Highlight_Matching_Pair() Called 2 times Total time: 0.000476 Self time: 0.000476 count total (s) self (s) " Remove any previous match. 2 0.000020 if exists('w:paren_hl_on') && w:paren_hl_on silent! call matchdelete(3) let w:paren_hl_on = 0 endif " Avoid that we remove the popup menu. " Return when there are no colors (looks like the cursor jumps). 2 0.000035 if pumvisible() || (&t_Co < 8 && !has("gui_running")) return endif " Get the character under the cursor and check if it's in 'matchpairs'. 2 0.000016 let c_lnum = line('.') 2 0.000014 let c_col = col('.') 2 0.000006 let before = 0 2 0.000013 let text = getline(c_lnum) 2 0.000086 let matches = matchlist(text, '\(.\)\=\%'.c_col.'c\(.\=\)') 2 0.000013 if empty(matches) let [c_before, c] = ['', ''] else 2 0.000024 let [c_before, c] = matches[1:2] 2 0.000005 endif 2 0.000103 let plist = split(&matchpairs, '.\zs[:,]') 2 0.000015 let i = index(plist, c) 2 0.000005 if i < 0 " not found, in Insert mode try character before the cursor 2 0.000010 if c_col > 1 && (mode() == 'i' || mode() == 'R') let before = strlen(c_before) let c = c_before let i = index(plist, c) endif 2 0.000004 if i < 0 " not found, nothing to do 2 0.000004 return endif endif " Figure out the arguments for searchpairpos(). if i % 2 == 0 let s_flags = 'nW' let c2 = plist[i + 1] else let s_flags = 'nbW' let c2 = c let c = plist[i - 1] endif if c == '[' let c = '\[' let c2 = '\]' endif " Find the match. When it was just before the cursor move it there for a " moment. if before > 0 let has_getcurpos = exists("*getcurpos") if has_getcurpos " getcurpos() is more efficient but doesn't exist before 7.4.313. let save_cursor = getcurpos() else let save_cursor = winsaveview() endif call cursor(c_lnum, c_col - before) endif " Build an expression that detects whether the current cursor position is in " certain syntax types (string, comment, etc.), for use as searchpairpos()'s " skip argument. " We match "escape" for special items, such as lispEscapeSpecial. let s_skip = '!empty(filter(map(synstack(line("."), col(".")), ''synIDattr(v:val, "name")''), ' . '''v:val =~? "string\\|character\\|singlequote\\|escape\\|comment"''))' " If executing the expression determines that the cursor is currently in " one of the syntax types, then we want searchpairpos() to find the pair " within those syntax types (i.e., not skip). Otherwise, the cursor is " outside of the syntax types and s_skip should keep its value so we skip any " matching pair inside the syntax types. execute 'if' s_skip '| let s_skip = 0 | endif' " Limit the search to lines visible in the window. let stoplinebottom = line('w$') let stoplinetop = line('w0') if i % 2 == 0 let stopline = stoplinebottom else let stopline = stoplinetop endif " Limit the search time to 300 msec to avoid a hang on very long lines. " This fails when a timeout is not supported. if mode() == 'i' || mode() == 'R' let timeout = exists("b:matchparen_insert_timeout") ? b:matchparen_insert_timeout : g:matchparen_insert_timeout else let timeout = exists("b:matchparen_timeout") ? b:matchparen_timeout : g:matchparen_timeout endif try let [m_lnum, m_col] = searchpairpos(c, '', c2, s_flags, s_skip, stopline, timeout) catch /E118/ " Can't use the timeout, restrict the stopline a bit more to avoid taking " a long time on closed folds and long lines. " The "viewable" variables give a range in which we can scroll while " keeping the cursor at the same position. " adjustedScrolloff accounts for very large numbers of scrolloff. let adjustedScrolloff = min([&scrolloff, (line('w$') - line('w0')) / 2]) let bottom_viewable = min([line('$'), c_lnum + &lines - adjustedScrolloff - 2]) let top_viewable = max([1, c_lnum-&lines+adjustedScrolloff + 2]) " one of these stoplines will be adjusted below, but the current values are " minimal boundaries within the current window if i % 2 == 0 if has("byte_offset") && has("syntax_items") && &smc > 0 let stopbyte = min([line2byte("$"), line2byte(".") + col(".") + &smc * 2]) let stopline = min([bottom_viewable, byte2line(stopbyte)]) else let stopline = min([bottom_viewable, c_lnum + 100]) endif let stoplinebottom = stopline else if has("byte_offset") && has("syntax_items") && &smc > 0 let stopbyte = max([1, line2byte(".") + col(".") - &smc * 2]) let stopline = max([top_viewable, byte2line(stopbyte)]) else let stopline = max([top_viewable, c_lnum - 100]) endif let stoplinetop = stopline endif let [m_lnum, m_col] = searchpairpos(c, '', c2, s_flags, s_skip, stopline) endtry if before > 0 if has_getcurpos call setpos('.', save_cursor) else call winrestview(save_cursor) endif endif " If a match is found setup match highlighting. if m_lnum > 0 && m_lnum >= stoplinetop && m_lnum <= stoplinebottom if exists('*matchaddpos') call matchaddpos('MatchParen', [[c_lnum, c_col - before], [m_lnum, m_col]], 10, 3) else exe '3match MatchParen /\(\%' . c_lnum . 'l\%' . (c_col - before) . 'c\)\|\(\%' . m_lnum . 'l\%' . m_col . 'c\)/' endif let w:paren_hl_on = 1 endif FUNCTION 140() Called 338 times Total time: 0.011372 Self time: 0.011372 count total (s) self (s) 338 0.007335 if a:line =~# self.re.start 40 0.000095 let self.opened += 1 40 0.000057 return 'a1' elseif self.opened > 0 && a:line =~# self.re.end 40 0.000090 let self.opened -= 1 40 0.000062 return 's1' endif FUNCTION 143() Called 1038 times Total time: 0.032087 Self time: 0.032087 count total (s) self (s) 1038 0.023336 if a:line =~# self.re.start let self.opened = 1 return 'a1' elseif self.opened && a:line =~# self.re.end let self.opened = 0 return 's1' endif FUNCTION vimtex#delim#get_matching() Called 72 times Total time: 0.016849 Self time: 0.007118 count total (s) self (s) 72 0.000507 if empty(a:delim) || !has_key(a:delim, 'lnum') | return {} | endif " " Get the matching position " 72 0.000968 0.000418 let l:save_pos = vimtex#pos#get_cursor() 72 0.002779 0.000419 call vimtex#pos#set_cursor(a:delim) 72 0.004433 0.000689 let [l:match, l:lnum, l:cnum] = a:delim.get_matching() 72 0.003502 0.000425 call vimtex#pos#set_cursor(l:save_pos) " " Create the match result " 72 0.000589 let l:matching = deepcopy(a:delim) 72 0.000180 let l:matching.lnum = l:lnum 72 0.000134 let l:matching.cnum = l:cnum 72 0.000185 let l:matching.match = l:match 72 0.000170 let l:matching.corr = a:delim.match 72 0.000228 let l:matching.side = a:delim.is_open ? 'close' : 'open' 72 0.000169 let l:matching.is_open = !a:delim.is_open 72 0.000228 let l:matching.re.corr = a:delim.re.this 72 0.000192 let l:matching.re.this = a:delim.re.corr 72 0.000191 if l:matching.type ==# 'delim' 72 0.000167 let l:matching.corr_delim = a:delim.delim 72 0.000197 let l:matching.corr_mod = a:delim.mod 72 0.000186 let l:matching.delim = a:delim.corr_delim 72 0.000156 let l:matching.mod = a:delim.corr_mod 72 0.000247 elseif l:matching.type ==# 'env' && has_key(l:matching, 'name') if l:matching.is_open let l:matching.env_cmd = vimtex#cmd#get_at(l:lnum, l:cnum) else unlet l:matching.env_cmd endif endif 72 0.000125 return l:matching FUNCTION vimtex#pos#set_cursor() Called 577 times Total time: 0.023308 Self time: 0.004634 count total (s) self (s) 577 0.023003 0.004329 call cursor(s:parse_args(a:000)) FUNCTION 92_EndColRx() Called 2 times Total time: 0.000059 Self time: 0.000046 count total (s) self (s) " TLogVAR a:comment_mode, a:lnum, a:pos 2 0.000009 let line = getline(a:lnum) 2 0.000034 0.000021 let cend = s:Strdisplaywidth(line) " TLogVAR cend 2 0.000005 if a:pos == 0 || a:pos >= cend 2 0.000002 return '\$' else if a:comment_mode =~? 'i' && a:comment_mode =~# 'o' let mod = '>' else let mod = '' endif " TLogVAR &selection, mod return '\%'. mod . a:pos .'v' endif FUNCTION 92_Printf1() Called 1 time Total time: 0.000058 Self time: 0.000037 count total (s) self (s) 1 0.000033 0.000012 let n = s:Count(a:fmt, '%\@61_get_cmd() Called 36 times Total time: 0.112141 Self time: 0.004331 count total (s) self (s) 36 0.001596 0.000343 let [lnum, cnum, match] = s:get_cmd_name(a:direction ==# 'next') 36 0.000139 if lnum == 0 | return {} | endif 36 0.000419 let res = { 'name' : match, 'pos_start' : { 'lnum' : lnum, 'cnum' : cnum }, 'pos_end' : { 'lnum' : lnum, 'cnum' : cnum + strlen(match) - 1 }, 'args' : [],} " Environments always start with environment name and allows option " afterwords 36 0.000086 if res.name ==# '\begin' let arg = s:get_cmd_part('{', res.pos_end) call add(res.args, arg) let res.pos_end.lnum = arg.close.lnum let res.pos_end.cnum = arg.close.cnum endif " Get options 36 0.037942 0.000291 let res.opt = s:get_cmd_part('[', res.pos_end) 36 0.000117 if !empty(res.opt) 36 0.000086 let res.pos_end.lnum = res.opt.close.lnum 36 0.000069 let res.pos_end.cnum = res.opt.close.cnum 36 0.000035 endif " Get arguments 36 0.041840 0.000289 let arg = s:get_cmd_part('{', res.pos_end) 72 0.000200 while !empty(arg) 36 0.000122 call add(res.args, arg) 36 0.000091 let res.pos_end.lnum = arg.close.lnum 36 0.000071 let res.pos_end.cnum = arg.close.cnum 36 0.026456 0.000472 let arg = s:get_cmd_part('{', res.pos_end) 36 0.000048 endwhile " Include entire cmd text 36 0.001708 0.000337 let res.text = s:text_between(res.pos_start, res.pos_end, 1) 36 0.000053 return res FUNCTION 64_parser_delim_get_regexp() Called 216 times Total time: 0.009055 Self time: 0.009055 count total (s) self (s) 216 0.000675 let l:type = a:0 > 0 ? a:1 : 'delim_all' " First check for unmatched math delimiter 216 0.000484 if a:delim ==# '.' return g:vimtex#delim#re.delim_math[a:side ? 'open' : 'close'] endif " Next check normal delimiters 216 0.005283 let l:index = index(map(copy(g:vimtex#delim#lists[l:type].name), 'v:val[' . a:side . ']'), a:delim) 216 0.001191 return l:index >= 0 ? g:vimtex#delim#lists[l:type].re[l:index][a:side] : '' FUNCTION 92_Count() Called 11 times Total time: 0.000224 Self time: 0.000224 count total (s) self (s) 11 0.000217 return len(split(a:string, a:rx, 1)) - 1 FUNCTION 190() Called 2 times Total time: 0.000035 Self time: 0.000035 count total (s) self (s) 2 0.000016 silent! call matchdelete(w:vimtex_match_id1) 2 0.000010 silent! call matchdelete(w:vimtex_match_id2) 2 0.000004 unlet! w:vimtex_match_id1 2 0.000003 unlet! w:vimtex_match_id2 FUNCTION 191() Called 2 times Total time: 0.001331 Self time: 0.000044 count total (s) self (s) 2 0.000044 0.000009 call self.clear() 2 0.000692 0.000016 if vimtex#util#in_comment() | return | endif 1 0.000587 0.000011 let l:current = vimtex#delim#get_current('all', 'both') 1 0.000004 if empty(l:current) | return | endif let l:corresponding = vimtex#delim#get_matching(l:current) if empty(l:corresponding) | return | endif let [l:open, l:close] = l:current.is_open ? [l:current, l:corresponding] : [l:corresponding, l:current] let w:vimtex_match_id1 = matchadd('MatchParen', '\%' . l:open.lnum . 'l\%' . l:open.cnum . 'c' . l:open.re.this) let w:vimtex_match_id2 = matchadd('MatchParen', '\%' . l:close.lnum . 'l\%' . l:close.cnum . 'c' . l:close.re.this) FUNCTION vimtex#fold#text() Called 40 times Total time: 0.130173 Self time: 0.004361 count total (s) self (s) 40 0.000218 let l:line = getline(v:foldstart) 40 0.000455 let l:level = v:foldlevel > 1 ? repeat('-', v:foldlevel-2) . g:vimtex_fold_levelmarker : '' 96 0.000241 for l:type in b:vimtex.fold_types_ordered 96 0.002174 if l:line =~# l:type.re.start 40 0.126167 0.000355 let l:text = l:type.text(l:line, l:level) 80 0.000220 if !empty(l:text) | return l:text | endif endif 56 0.000051 endfor FUNCTION 64_parser_delim() Called 108 times Total time: 0.032610 Self time: 0.015845 count total (s) self (s) 108 0.000305 let result = {} 108 0.000309 let result.type = 'delim' 108 0.002463 let result.side = a:match =~# g:vimtex#delim#re.delim_all.open ? 'open' : 'close' 108 0.000403 let result.is_open = result.side ==# 'open' 108 0.000782 let result.get_matching = function('s:get_matching_delim') " " Find corresponding delimiter and the regexps " 108 0.001829 if a:match =~# '^' . g:vimtex#delim#re.mods.both let m1 = matchstr(a:match, '^' . g:vimtex#delim#re.mods.both) let d1 = substitute(strpart(a:match, len(m1)), '^\s*', '', '') let s1 = !result.is_open let re1 = s:parser_delim_get_regexp(m1, s1, 'mods') . '\s*' . s:parser_delim_get_regexp(d1, s1, 'delim_math') let m2 = s:parser_delim_get_corr(m1, 'mods') let d2 = s:parser_delim_get_corr(d1, 'delim_math') let s2 = result.is_open let re2 = s:parser_delim_get_regexp(m2, s2, 'mods') . '\s*' . (m1 =~# '\\\%(left\|right\)' ? '\%(' . s:parser_delim_get_regexp(d2, s2, 'delim_math') . '\|\.\)' : s:parser_delim_get_regexp(d2, s2, 'delim_math')) else 108 0.000225 let d1 = a:match 108 0.000157 let m1 = '' 108 0.005579 0.000855 let re1 = s:parser_delim_get_regexp(a:match, !result.is_open) 108 0.008497 0.000787 let d2 = s:parser_delim_get_corr(a:match) 108 0.000195 let m2 = '' 108 0.005134 0.000803 let re2 = s:parser_delim_get_regexp(d2, result.is_open) 108 0.000128 endif 108 0.000259 let result.delim = d1 108 0.000226 let result.mod = m1 108 0.000345 let result.corr = m2 . d2 108 0.000266 let result.corr_delim = d2 108 0.000248 let result.corr_mod = m2 108 0.000767 let result.re = { 'this' : re1, 'corr' : re2, 'open' : result.is_open ? re1 : re2, 'close' : result.is_open ? re2 : re1,} 108 0.000161 return result FUNCTION 92_CommentDef() Called 1 time Total time: 0.000611 Self time: 0.000520 count total (s) self (s) " TLogVAR a:beg, a:end, a:checkRx, a:comment_mode, a:cbeg, a:cend 1 0.000003 let beg = a:beg 1 0.000002 let end = a:end 1 0.000006 if a:comment_mode =~# 'U' let uncomment = 1 elseif a:comment_mode =~# '[CK]' let uncomment = 0 else 1 0.000004 if get(s:cdef, 'mixedindent', 1) 1 0.000046 0.000016 let mdrx = '\V'. s:StartColRx(a:comment_mode, a:cbeg) .'\s\*' 1 0.000008 let cbeg1 = a:comment_mode =~? 'i' ? a:cbeg : a:cbeg + 1 1 0.000039 0.000011 let mdrx .= s:StartColRx(a:comment_mode, cbeg1, 0) .'\s\*' 1 0.000001 else let mdrx = '\V'. s:StartColRx(a:comment_mode, a:cbeg) .'\s\*' endif 1 0.000048 0.000015 let mdrx .= a:checkRx .'\s\*'. s:EndColRx(a:comment_mode, a:end, 0) " let mdrx = '\V'. s:StartPosRx(a:comment_mode, beg, a:cbeg) .'\s\*'. a:checkRx .'\s\*'. s:EndPosRx(a:comment_mode, end, 0) " TLogVAR mdrx 1 0.000004 let line = getline(beg) 1 0.000002 if a:cbeg != 0 && a:cend != 0 let line = strpart(line, 0, a:cend - 1) endif 1 0.000035 let uncomment = (line =~ mdrx) " TLogVAR 1, uncomment, line 1 0.000004 let n = beg + 1 1 0.000005 if a:comment_mode =~# 'G' 1 0.000002 if uncomment 10 0.000019 while n <= end 9 0.000058 if getline(n) =~# '\S' 9 0.000140 if !(getline(n) =~ mdrx) let uncomment = 0 " TLogVAR 2, uncomment break endif 9 0.000008 endif 9 0.000027 let n = n + 1 9 0.000013 endwh 1 0.000001 endif 1 0.000002 elseif a:comment_mode =~# 'B' let t = @t try silent exec 'norm! '. beg.'G1|v'.end.'G$"ty' if &selection ==# 'inclusive' && @t =~# '\n$' && len(@t) > 1 let @t = @t[0 : -2] endif " TLogVAR @t, mdrx let uncomment = (@t =~ mdrx) " TLogVAR 3, uncomment if !uncomment && a:comment_mode =~? 'o' let mdrx1 = substitute(mdrx, '\\$$', '\\n\\$', '') " TLogVAR mdrx1 if @t =~ mdrx1 let uncomment = 1 " TLogVAR 4, uncomment endif endif finally let @t = t endtry endif 1 0.000001 endif " TLogVAR 5, beg, end, uncomment 1 0.000003 return [beg, end, uncomment] FUNCTION tcomment#Operator() Called 1 time Total time: 0.217835 Self time: 0.000286 count total (s) self (s) 1 0.000004 let type = a:type 1 0.000004 let comment_mode = a:0 >= 1 ? a:1 : '' 1 0.000002 let bang = a:0 >= 2 ? a:2 : '' " TLogVAR type, comment_mode, bang 1 0.000005 if !exists('w:tcomment_pos') 1 0.000007 let w:tcomment_pos = getpos('.') 1 0.000001 endif 1 0.000005 let sel_save = &selection 1 0.000011 set selection=inclusive 1 0.000005 let reg_save = @@ 1 0.000001 try 1 0.000003 if type ==# 'line' 1 0.000028 silent exe "normal! '[V']" 1 0.000003 let comment_mode1 = 'G' 1 0.000002 elseif type ==# 'block' silent exe "normal! `[\`]" let comment_mode1 = 'I' elseif type ==# 'char' silent exe 'normal! `[v`]' let comment_mode1 = 'I' else silent exe 'normal! `[v`]' let comment_mode1 = 'i' endif 1 0.000004 if empty(comment_mode) 1 0.000004 let comment_mode = comment_mode1 1 0.000001 endif 1 0.000005 let lbeg = line("'[") 1 0.000004 let lend = line("']") 1 0.000005 let cend = virtcol("']") 1 0.000003 if type ==# 'char' if lbeg == lend && cend >= virtcol('$') - 1 let comment_mode = 'R' elseif g:tcomment#ignore_char_type && lbeg != lend silent exe "normal! '[V']" let cend = virtcol("']") let comment_mode = 'G' let type = 'line' endif endif 1 0.000004 let cbeg = virtcol("'[") " TLogVAR comment_mode, comment_mode1, lbeg, lend, cbeg, cend, virtcol('$') " TLogVAR comment_mode " echom "DBG tcomment#Operator" lbeg virtcol("'[") virtcol("'<") lend virtcol("']") virtcol("'>") 1 0.000017 norm!  1 0.000058 0.000021 let comment_mode = s:AddModeExtra(comment_mode, g:tcommentOpModeExtra, lbeg, lend) " TLogVAR comment_mode, type " if type =~ 'line\|block' || g:tcomment#ignore_char_type " if comment_mode =~# '[R]' " call tcomment#Comment([lbeg, cbeg], [lend, cend], comment_mode.'o', bang) " elseif type =~ 'line\|block' || g:tcomment#ignore_char_type 1 0.000012 if type =~# '\%(line\|block\)' 1 0.217540 0.000028 call tcomment#Comment(lbeg, lend, comment_mode.'o', bang) 1 0.000001 else call tcomment#Comment([lbeg, cbeg], [lend, cend], comment_mode.'o', bang) endif 1 0.000002 finally 1 0.000008 let &selection = sel_save 1 0.000005 let @@ = reg_save " TLogVAR g:tcommentOpModeExtra 1 0.000005 if g:tcommentOpModeExtra !~# '[#>]' 1 0.000004 if exists('w:tcomment_pos') " TLogVAR w:tcomment_pos 1 0.000005 if w:tcomment_pos != getpos('.') call setpos('.', w:tcomment_pos) endif 1 0.000002 unlet! w:tcomment_pos 1 0.000001 else echohl WarningMsg echom "TComment: w:tcomment_pos wasn't set. Please report this to the plugin author" echohl NONE endif 1 0.000001 endif 1 0.000001 endtry FUNCTION 64_get_matching_delim() Called 72 times Total time: 0.003744 Self time: 0.003744 count total (s) self (s) 72 0.000855 let [re, flags, stopline] = self.is_open ? [self.re.close, 'nW', line('.') + s:stopline] : [self.re.open, 'bnW', max([line('.') - s:stopline, 1])] 72 0.001642 let [lnum, cnum] = searchpairpos(self.re.open, '', self.re.close, flags, '', stopline) 72 0.000870 let match = matchstr(getline(lnum), '^' . re, cnum-1) 72 0.000219 return [match, lnum, cnum] FUNCTION vimtex#fold#level() Called 2604 times Total time: 0.421835 Self time: 0.217044 count total (s) self (s) 2604 0.008627 let l:line = getline(a:lnum) " Filter out lines that do not start any folds (optimization) 2604 0.146847 if l:line !~# b:vimtex.fold_re | return '=' | endif " Never fold \begin|end{document} 1040 0.007197 if l:line =~# '^\s*\\\%(begin\|end\){document}' 2 0.000003 return 0 endif 3656 0.005871 for l:type in b:vimtex.fold_types_ordered 3500 0.225241 0.020450 let l:value = l:type.level(l:line, a:lnum) 4382 0.011754 if !empty(l:value) | return l:value | endif 2618 0.002067 endfor " Return foldlevel of previous line 156 0.000188 return '=' FUNCTION 92_GetCommentDefinition() Called 1 time Total time: 0.001904 Self time: 0.000166 count total (s) self (s) 1 0.000004 let ft = a:0 >= 1 ? a:1 : '' 1 0.000006 Tlibtrace 'tcomment', a:beg, a:end, a:comment_mode, ft 1 0.000004 if !empty(ft) let cdef = s:GuessCustomCommentString(ft, a:comment_mode) else 1 0.000005 let cdef = {'mode': a:comment_mode} 1 0.000001 endif " TLogVAR cdef 1 0.000005 let cms = get(cdef, 'commentstring', '') 1 0.000004 if empty(cms) 1 0.000352 0.000016 let filetype = s:GetFiletype(ft) 1 0.000003 Tlibtrace 'tcomment', filetype 1 0.000005 if exists('b:commentstring') let cms = b:commentstring " TLogVAR 1, cms return s:GuessCustomCommentString(filetype, a:comment_mode, cms) elseif exists('b:commentStart') && !empty(b:commentStart) let cms = s:EncodeCommentPart(b:commentStart) .' %s' " TLogVAR 2, cms if exists('b:commentEnd') && !empty(b:commentEnd) let cms = cms .' '. s:EncodeCommentPart(b:commentEnd) endif return s:GuessCustomCommentString(filetype, a:comment_mode, cms) else 1 0.000241 0.000018 let [use_guess_ft, alt_filetype] = s:AltFiletype(ft, cdef) 1 0.000003 Tlibtrace 'tcomment', use_guess_ft, alt_filetype 1 0.000002 if use_guess_ft return s:GuessFileType(a:beg, a:end, a:comment_mode, filetype, alt_filetype) else 1 0.000851 0.000018 let guess_cdef = s:GuessVimOptionsCommentString(a:comment_mode) " TLogVAR guess_cdef 1 0.000366 0.000020 return s:GuessCustomCommentString(filetype, a:comment_mode, guess_cdef.commentstring, guess_cdef) endif endif let cdef.commentstring = cms endif return cdef FUNCTION 92_EndPosRx() Called 1 time Total time: 0.000037 Self time: 0.000011 count total (s) self (s) " TLogVAR a:comment_mode, a:lnum, a:col " if a:comment_mode =~# 'I' " return s:EndLineRx(a:lnum) . s:EndColRx(a:col) " else 1 0.000034 0.000008 return s:EndColRx(a:comment_mode, a:lnum, a:col) " endif FUNCTION vimtex#pos#val() Called 72 times Total time: 0.003125 Self time: 0.001069 count total (s) self (s) 72 0.002639 0.000583 let [l:lnum, l:cnum; l:rest] = s:parse_args(a:000) 72 0.000376 return 100000*l:lnum + min([l:cnum, 90000]) FUNCTION 92_GetCommentReplace() Called 1 time Total time: 0.000018 Self time: 0.000018 count total (s) self (s) 1 0.000005 if has_key(a:cdef, 'commentstring_rx') let rs = s:BlockGetCommentString(a:cdef) else 1 0.000002 let rs = a:cms0 1 0.000001 endif 1 0.000002 return rs " return escape(rs, '"/') FUNCTION 92_GetFiletype() Called 2 times Total time: 0.000480 Self time: 0.000480 count total (s) self (s) 2 0.000013 let ft = a:0 >= 1 && !empty(a:1) ? a:1 : &filetype 2 0.000009 let poss = a:0 >= 2 ? a:2 : [-1, 1, 0] 6 0.000011 for pos in poss 4 0.000013 Tlibtrace 'tcomment', ft, pos 4 0.000009 if pos == -1 2 0.000004 let rv = ft 2 0.000004 else 2 0.000026 let fts = split(ft, '^\@!\.') " TLogVAR fts " let ft = substitute(ft, '\..*$', '', '') 2 0.000010 let rv = get(fts, pos, ft) " TLogVAR fts, rv 2 0.000002 endif 4 0.000109 let fts_rx = '^\%('. join(map(keys(g:tcomment#filetype_map), 'escape(v:val, ''\'')'), '\|') .'\)$' 4 0.000013 Tlibtrace 'tcomment', fts_rx 4 0.000045 if rv =~ fts_rx for [ft_rx, ftrv] in items(g:tcomment#filetype_map) " TLogVAR ft_rx, ftrv if rv =~ ft_rx let rv = substitute(rv, ft_rx, ftrv, '') Tlibtrace 'tcomment', ft_rx, rv Tlibtrace 'tcomment', rv return rv endif endfor endif 4 0.000004 endfor 2 0.000006 Tlibtrace 'tcomment', ft 2 0.000004 return ft FUNCTION 92_GetTempOption() Called 1 time Total time: 0.000014 Self time: 0.000014 count total (s) self (s) 1 0.000006 if exists('s:temp_options') && has_key(s:temp_options, a:name) return s:temp_options[a:name] else 1 0.000003 return a:default endif FUNCTION tcomment#TypeExists() Called 2 times Total time: 0.000056 Self time: 0.000056 count total (s) self (s) 2 0.000008 let comment_mode = a:0 >= 1 ? a:1 : '' 2 0.000005 let name = a:name 2 0.000011 if comment_mode =~? 'b' let name .= '_block' elseif comment_mode =~? 'i' let name .= '_inline' endif 2 0.000011 return has_key(s:definitions, name) ? name : '' FUNCTION 92_BlockGetCommentRx() Called 1 time Total time: 0.000054 Self time: 0.000032 count total (s) self (s) 1 0.000005 if has_key(a:cdef, 'commentstring_rx') return a:cdef.commentstring_rx else 1 0.000037 0.000015 let cms0 = s:BlockGetCommentString(a:cdef) 1 0.000005 let cms0 = escape(cms0, '\') 1 0.000002 return cms0 endif FUNCTION vimtex#pos#get_cursor() Called 361 times Total time: 0.002679 Self time: 0.002679 count total (s) self (s) 361 0.002466 return exists('*getcurpos') ? getcurpos() : getpos('.') FUNCTION 64_parser_delim_get_corr() Called 108 times Total time: 0.007710 Self time: 0.007710 count total (s) self (s) 108 0.000320 let l:type = a:0 > 0 ? a:1 : 'delim_all' 576 0.001282 for l:pair in g:vimtex#delim#lists[l:type].name 576 0.001759 if a:delim ==# l:pair[0] 108 0.000254 return l:pair[1] elseif a:delim ==# l:pair[1] return l:pair[0] endif 468 0.000416 endfor FUNCTION tcomment#MaybeReuseOptions() Called 1 time Total time: 0.000046 Self time: 0.000046 count total (s) self (s) 1 0.000014 if exists('s:options_cache') && get(s:options_cache, 'name', '') == a:name if exists('s:temp_options') let s:temp_options = extend(deepcopy(s:options_cache.options), s:temp_options) let s:options_cache = {'name': a:name, 'options': s:temp_options} else let s:temp_options = deepcopy(s:options_cache.options) endif elseif exists('s:temp_options') let s:options_cache = {'name': a:name, 'options': s:temp_options} endif FUNCTION 92_GuessVimOptionsCommentString() Called 1 time Total time: 0.000833 Self time: 0.000139 count total (s) self (s) " TLogVAR a:comment_mode 1 0.000004 let commentstring = &commentstring " let valid_f = (match(substitute(commentstring, '%\@61_text_between() Called 184 times Total time: 0.006997 Self time: 0.006997 count total (s) self (s) 184 0.001086 let [l1, c1] = [a:p1.lnum, a:p1.cnum - (a:0 > 0)] 184 0.000889 let [l2, c2] = [a:p2.lnum, a:p2.cnum - (a:0 <= 0)] 184 0.000783 let lines = getline(l1, l2) 184 0.000480 if !empty(lines) 184 0.001060 let lines[0] = strpart(lines[0], c1) 184 0.001300 let lines[-1] = strpart(lines[-1], 0, l1 == l2 ? c2 - c1 : c2) 184 0.000208 endif 184 0.000735 return join(lines, '') FUNCTION 61_get_cmd_name() Called 36 times Total time: 0.001253 Self time: 0.001253 count total (s) self (s) 36 0.000597 let [l:lnum, l:cnum] = searchpos('\v\\\a+\*?', a:next ? 'nW' : 'cbnW') 36 0.000505 let l:match = matchstr(getline(l:lnum), '^\v\\\a*\*?', l:cnum-1) 36 0.000114 return [l:lnum, l:cnum, l:match] FUNCTION 92_StartColRx() Called 3 times Total time: 0.000089 Self time: 0.000089 count total (s) self (s) 3 0.000017 let mixedindent = a:0 >= 1 ? a:1 : get(s:cdef, 'mixedindent', 1) " TLogVAR a:comment_mode, a:col, mixedindent 3 0.000020 if a:comment_mode =~# '[IR]' let col = mixedindent ? a:col - 1 : a:col let c0 = 1 else 3 0.000007 let col = a:col 3 0.000006 let c0 = 2 3 0.000003 endif " TLogVAR col, c0, mixedindent 3 0.000005 if col < c0 3 0.000005 return '\^' elseif mixedindent return '\%>'. col .'v' else return '\%'. col .'v' endif FUNCTION 92_ProcessLine() Called 10 times Total time: 0.002064 Self time: 0.001717 count total (s) self (s) " TLogVAR a:comment_do, a:match, a:checkRx, a:replace 10 0.000011 try 10 0.000028 if !(g:tcomment#blank_lines > 0 || a:match =~# '\S') return [a:match, 0] endif 10 0.000022 if a:comment_do ==# 'k' if a:match =~ a:checkRx return ['', 0] endif endif 10 0.000018 if a:comment_do ==# 'u' 10 0.000179 let m = matchlist(a:match, a:checkRx) 10 0.000026 if !empty(m) 10 0.000300 0.000097 for irx in range(2, s:Count(a:checkRx, '\\\@' let s:cursor_pos = getpos('.') let s:cursor_pos[2] += len(rv) elseif s:cdef.mode =~# '#' if empty(s:cursor_pos) || s:current_pos[1] == s:processline_lnum let prefix = matchstr(a:replace, '^.*%\@92_UnreplaceInLine() Called 10 times Total time: 0.000144 Self time: 0.000144 count total (s) self (s) 10 0.000036 if has_key(s:cdef, 'replacements') let replacements = s:cdef.replacements return s:DoReplacements(a:text, values(replacements), keys(replacements)) else 10 0.000018 return a:text endif FUNCTION 68_parse_args() Called 685 times Total time: 0.021826 Self time: 0.020730 count total (s) self (s) " " The arguments should be in one of the following forms (when unpacked): " " [lnum, cnum] " [bufnum, lnum, cnum, ...] " {'lnum' : lnum, 'cnum' : cnum} " 685 0.002038 if len(a:args) > 1 36 0.000157 return s:parse_args([a:args]) elseif len(a:args) == 1 649 0.003422 if type(a:args[0]) == type({}) 216 0.001512 return [get(a:args[0], 'lnum'), get(a:args[0], 'cnum')] else 433 0.001542 if len(a:args[0]) == 2 36 0.000109 return a:args[0] else 397 0.001424 return a:args[0][1:] endif endif else return a:args endif FUNCTION 92_GetStartEnd() Called 1 time Total time: 0.000316 Self time: 0.000316 count total (s) self (s) " TLogVAR a:beg, a:end, a:comment_mode 1 0.000005 if type(a:beg) == 3 let [lbeg, cbeg] = a:beg let [lend, cend] = a:end else 1 0.000003 let lbeg = a:beg 1 0.000002 let lend = a:end 1 0.000003 let comment_mode = a:comment_mode " TLogVAR comment_mode 1 0.000005 if comment_mode =~# 'R' let cbeg = virtcol('.') let cend = virtcol('$') let comment_mode = substitute(comment_mode, '\CR', 'G', 'g') " TLogVAR 'R', cbeg, cend, comment_mode elseif comment_mode =~# 'I' let cbeg = virtcol("'<") if cbeg == 0 let cbeg = virtcol('.') endif let cend = virtcol("'>") if cend < virtcol('$') && (comment_mode =~# 'o' || &selection ==# 'inclusive') let cend += 1 " TLogVAR cend, virtcol('$') endif " TLogVAR 'I', cbeg, cend, comment_mode else 1 0.000002 let cbeg = -1 1 0.000002 let cend = 0 11 0.000022 for lnum in range(a:beg, a:end) 10 0.000072 if getline(lnum) =~# '\S' 10 0.000035 let indentwidth = indent(lnum) " TLogVAR cbeg, lnum, indentwidth, getline(lnum) 10 0.000026 if cbeg == -1 || indentwidth < cbeg 1 0.000002 let cbeg = indentwidth 1 0.000002 endif 10 0.000008 endif 10 0.000009 endfor 1 0.000002 if cbeg == -1 let cbeg = 0 endif 1 0.000001 endif 1 0.000001 endif " TLogVAR lbeg, cbeg, lend, cend 1 0.000003 if lend < lbeg || (lend == lbeg && cend > 0 && cend < cbeg) return [lend, cend, lbeg, cbeg] else 1 0.000004 return [lbeg, cbeg, lend, cend] endif FUNCTION vimtex#cmd#get_at() Called 36 times Total time: 0.123575 Self time: 0.001076 count total (s) self (s) 36 0.000492 0.000220 let l:pos_saved = vimtex#pos#get_cursor() 36 0.002088 0.000247 call call('vimtex#pos#set_cursor', a:000) 36 0.119188 0.000289 let l:cmd = vimtex#cmd#get_current() 36 0.001690 0.000203 call vimtex#pos#set_cursor(l:pos_saved) 36 0.000066 return l:cmd FUNCTION vimtex#cmd#get_current() Called 36 times Total time: 0.118899 Self time: 0.001927 count total (s) self (s) 36 0.000441 0.000182 let l:save_pos = vimtex#pos#get_cursor() 36 0.001972 0.000264 let l:pos_val_cursor = vimtex#pos#val(l:save_pos) 36 0.000075 let l:depth = 3 36 0.000075 while l:depth > 0 36 0.000078 let l:depth -= 1 36 0.112407 0.000266 let l:cmd = s:get_cmd('prev') 36 0.000158 if empty(l:cmd) | break | endif 36 0.001683 0.000266 let l:pos_val = vimtex#pos#val(l:cmd.pos_end) 36 0.000093 if l:pos_val >= l:pos_val_cursor 36 0.001643 0.000196 call vimtex#pos#set_cursor(l:save_pos) 36 0.000090 return l:cmd else call vimtex#pos#set_cursor(vimtex#pos#prev(l:cmd.pos_start)) endif endwhile call vimtex#pos#set_cursor(l:save_pos) return {} FUNCTION 135() Called 156 times Total time: 0.060951 Self time: 0.001213 count total (s) self (s) 156 0.060867 0.001129 return self.opened ? self.fold_closed(a:line, a:lnum) : self.fold_opened(a:line, a:lnum) FUNCTION tcomment#ResetOption() Called 1 time Total time: 0.000008 Self time: 0.000008 count total (s) self (s) 1 0.000006 unlet! s:temp_options s:options_cache FUNCTIONS SORTED ON TOTAL TIME count total (s) self (s) function 2604 0.421835 0.217044 vimtex#fold#level() 1 0.217931 0.000050 TCommentOpFunc_gc() 1 0.217835 0.000286 tcomment#Operator() 1 0.217512 0.007903 tcomment#Comment() 40 0.130173 0.004361 vimtex#fold#text() 36 0.125733 0.002158 124() 36 0.123575 0.001076 vimtex#cmd#get_at() 36 0.118899 0.001927 vimtex#cmd#get_current() 36 0.112141 0.004331 61_get_cmd() 108 0.105186 0.006824 61_get_cmd_part() 108 0.066630 0.001964 vimtex#delim#get_next() 109 0.065222 0.027175 64_get_delim() 156 0.062177 115() 156 0.060951 0.001213 135() 156 0.059738 136() 108 0.032610 0.015845 64_parser_delim() 1038 0.032087 143() 577 0.023308 0.004634 vimtex#pos#set_cursor() 685 0.021826 0.020730 68_parse_args() 1038 0.020759 123() FUNCTIONS SORTED ON SELF TIME count total (s) self (s) function 2604 0.421835 0.217044 vimtex#fold#level() 156 0.062177 115() 156 0.059738 136() 1038 0.032087 143() 109 0.065222 0.027175 64_get_delim() 1038 0.020759 123() 685 0.021826 0.020730 68_parse_args() 108 0.032610 0.015845 64_parser_delim() 338 0.011372 140() 216 0.009055 64_parser_delim_get_regexp() 1 0.217512 0.007903 tcomment#Comment() 108 0.007710 64_parser_delim_get_corr() 72 0.016849 0.007118 vimtex#delim#get_matching() 184 0.006997 61_text_between() 108 0.105186 0.006824 61_get_cmd_part() 258 0.004700 133() 577 0.023308 0.004634 vimtex#pos#set_cursor() 258 0.004528 125() 258 0.004384 131() 40 0.130173 0.004361 vimtex#fold#text() ```
Do you have any clue what the problem is?
kiryph commented 7 years ago

The plugin FastFold resolves this issue which is apparently a problem of vim itself. :h vimtex-folding mentions already FastFold. However, IMHO it is not obvious that a plugin such as vim-commentary is affected by this (most likely due to insert mode updates of folds).

Unfortunately, the plugin vim-fold-cycle which I use for my customized mappings of zC, zo, and zc (see here https://github.com/arecarn/vim-fold-cycle/issues/6#issuecomment-294370249) does not support FastFold. Another solution could be to set temporarily foldmethod to manual for vim-commentary mappings and return afterwards to the old value. I am not sure how to do this. Related question is https://stackoverflow.com/questions/21280457/stop-vim-from-dynamically-updating-folds

Note that neovim does not have this issue (most likely due to following change https://github.com/neovim/neovim/commit/9d4fcec7c6b65ef04fd4416b014e96f33b1f708a). There is also a pending pull request for vim (https://github.com/vim/vim/pull/1045). However, I cannot confirm that it helps in this circumstance by following steps:

  1. modify homebrew formula to point to Shougos pull request with brew edit vim; change head "https://github.com/vim/vim.git" to head "https://github.com/Shougo/vim.git", :branch => 'fold'
  2. reinstall vim with brew reinstall vim --HEAD

Profiling returns:

FUNCTIONS SORTED ON TOTAL TIME
count  total (s)   self (s)  function
    1  14.207625   0.059748  <SNR>27_go()
11540  14.147601   1.397272  vimtex#fold#level()
 3955   5.983754   5.983048  123()
 3835   5.753792   0.027972  143()
 3835   5.725820             144()
 4965   0.273035   0.193778  136()
 6395   0.210174             151()
 5765   0.186866             148()
 6275   0.128953             131()
 4965   0.124612             133()
 4965   0.089143             141()
 4965   0.079257             139()
    1   0.000960             <SNR>28_UpdateCommentString()
  160   0.000706             129()
   10   0.000500   0.000289  LightlineFilename()
   10   0.000421   0.000362  LightlineMode()
    1   0.000320   0.000112  <SNR>100_cursormoved_delayed_cb()
   15   0.000247             LightlineFileencoding()
   10   0.000246             LightlineFugitive()
   10   0.000208             <SNR>27_strip_white_space()

FUNCTIONS SORTED ON SELF TIME
count  total (s)   self (s)  function
 3955   5.983754   5.983048  123()
 3835              5.725820  144()
11540  14.147601   1.397272  vimtex#fold#level()
 6395              0.210174  151()
 4965   0.273035   0.193778  136()
 5765              0.186866  148()
 6275              0.128953  131()
 4965              0.124612  133()
 4965              0.089143  141()
 4965              0.079257  139()
    1  14.207625   0.059748  <SNR>27_go()
 3835   5.753792   0.027972  143()
    1              0.000960  <SNR>28_UpdateCommentString()
  160              0.000706  129()
   10   0.000421   0.000362  LightlineMode()
   10   0.000500   0.000289  LightlineFilename()
   15              0.000247  LightlineFileencoding()
   10              0.000246  LightlineFugitive()
   10              0.000208  <SNR>27_strip_white_space()
   15              0.000194  LightlineFiletype()
lervag commented 7 years ago

I do not know that much about why or why not vim-commentary do not support FastFold, and I can not immediately give a solution for your issue with it. However, I can confirm that expression based folding is slow, and that there is unfortunately little I can do about it. I've tried to make the folding function as efficient as possible, and I am not surprised if I could make it even faster. However, I do not see how at the moment. Pull requests or suggestions are very welcome!

In the meantime, I heartily recommend that you either use FastFold, or that you use the manual folding and explicitly use zx to refresh, see :h vimtex_fold_manual.

kiryph commented 7 years ago

Yes, it is an issue of commentary or actually vim. With my last profiling test there were 11540 invocations of foldexpr. It does not make sense to optimize vimtex foldexpr as long as there are unnecessary calls to it. If it then vimtex#foldexpr is too slow it can be reported to you and one can start thinking about it.

This means for me either:

  1. switching to neovim,
  2. using vim with fastfold and stopping to use vim-fold-cycle,
  3. using vim with manually refreshed folds, or
  4. convincing Bram Moolenaar to fix these unnecessary calls to foldexpr.

Thanks anyhow.

lervag commented 7 years ago

If it helps, I've switched to neovim and I'm happy I did. I have, so far, had no issues with it, and it feels smoother and "cleaner".

In any case, thanks for your input on the issue. I hope Bram will fix the unnecessary calls to foldexpr, as I think that would help a lot of Vim users in general.