tex.vim (13848B)
1 " Vim indent file 2 " Language: LaTeX 3 " Maintainer: Yichao Zhou <broken.zhou AT gmail.com> 4 " Created: Sat, 16 Feb 2002 16:50:19 +0100 5 " Version: 1.0.0 6 " Please email me if you found something I can do. Comments, bug report and 7 " feature request are welcome. 8 9 " Last Update: {{{ 10 " 25th Sep 2002, by LH : 11 " (*) better support for the option 12 " (*) use some regex instead of several '||'. 13 " Oct 9th, 2003, by JT: 14 " (*) don't change indentation of lines starting with '%' 15 " 2005/06/15, Moshe Kaminsky <kaminsky AT math.huji.ac.il> 16 " (*) New variables: 17 " g:tex_items, g:tex_itemize_env, g:tex_noindent_env 18 " 2011/3/6, by Yichao Zhou <broken.zhou AT gmail.com> 19 " (*) Don't change indentation of lines starting with '%' 20 " I don't see any code with '%' and it doesn't work properly 21 " so I add some code. 22 " (*) New features: Add smartindent-like indent for "{}" and "[]". 23 " (*) New variables: g:tex_indent_brace 24 " 2011/9/25, by Yichao Zhou <broken.zhou AT gmail.com> 25 " (*) Bug fix: smartindent-like indent for "[]" 26 " (*) New features: Align with "&". 27 " (*) New variable: g:tex_indent_and. 28 " 2011/10/23 by Yichao Zhou <broken.zhou AT gmail.com> 29 " (*) Bug fix: improve the smartindent-like indent for "{}" and 30 " "[]". 31 " 2012/02/27 by Yichao Zhou <broken.zhou AT gmail.com> 32 " (*) Bug fix: support default folding marker. 33 " (*) Indent with "&" is not very handy. Make it not enable by 34 " default. 35 " 2012/03/06 by Yichao Zhou <broken.zhou AT gmail.com> 36 " (*) Modify "&" behavior and make it default again. Now "&" 37 " won't align when there are more then one "&" in the previous 38 " line. 39 " (*) Add indent "\left(" and "\right)" 40 " (*) Trust user when in "verbatim" and "lstlisting" 41 " 2012/03/11 by Yichao Zhou <broken.zhou AT gmail.com> 42 " (*) Modify "&" so that only indent when current line start with 43 " "&". 44 " 2012/03/12 by Yichao Zhou <broken.zhou AT gmail.com> 45 " (*) Modify indentkeys. 46 " 2012/03/18 by Yichao Zhou <broken.zhou AT gmail.com> 47 " (*) Add &cpo 48 " 2013/05/02 by Yichao Zhou <broken.zhou AT gmail.com> 49 " (*) Fix problem about GetTeXIndent checker. Thank Albert Netymk 50 " for reporting this. 51 " 2014/06/23 by Yichao Zhou <broken.zhou AT gmail.com> 52 " (*) Remove the feature g:tex_indent_and because it is buggy. 53 " (*) If there is not any obvious indentation hints, we do not 54 " alert our user's current indentation. 55 " (*) g:tex_indent_brace now only works if the open brace is the 56 " last character of that line. 57 " 2014/08/03 by Yichao Zhou <broken.zhou AT gmail.com> 58 " (*) Indent current line if last line has larger indentation 59 " 2016/11/08 by Yichao Zhou <broken.zhou AT gmail.com> 60 " (*) Fix problems for \[ and \]. Thanks Bruno for reporting. 61 " 2017/04/30 by Yichao Zhou <broken.zhou AT gmail.com> 62 " (*) Fix a bug between g:tex_noindent_env and g:tex_indent_items 63 " Now g:tex_noindent_env='document\|verbatim\|itemize' (Emacs 64 " style) is supported. Thanks Miles Wheeler for reporting. 65 " 2018/02/07 by Yichao Zhou <broken.zhou AT gmail.com> 66 " (*) Make indentation more smart in the normal mode 67 " 2020/04/26 by Yichao Zhou <broken.zhou AT gmail.com> 68 " (*) Fix a bug related to \[ & \]. Thanks Manuel Boni for 69 " reporting. 70 " 2023/08/28 by Vim Project 71 " (*) Set b:undo_indent. 72 " }}} 73 74 " Document: {{{ 75 " 76 " For proper latex experience, please put 77 " let g:tex_flavor = "latex" 78 " into your vimrc. 79 " 80 " * g:tex_indent_brace 81 " 82 " If this variable is unset or non-zero, it will use smartindent-like style 83 " for "{}" and "[]". Now this only works if the open brace is the last 84 " character of that line. 85 " 86 " % Example 1 87 " \usetikzlibrary{ 88 " external 89 " } 90 " 91 " % Example 2 92 " \tikzexternalize[ 93 " prefix=tikz] 94 " 95 " * g:tex_indent_items 96 " 97 " If this variable is set, item-environments are indented like Emacs does 98 " it, i.e., continuation lines are indented with a shiftwidth. 99 " 100 " set unset 101 " ------------------------------------------------------ 102 " \begin{itemize} \begin{itemize} 103 " \item blablabla \item blablabla 104 " bla bla bla bla bla bla 105 " \item blablabla \item blablabla 106 " bla bla bla bla bla bla 107 " \end{itemize} \end{itemize} 108 " 109 " 110 " * g:tex_items 111 " 112 " A list of tokens to be considered as commands for the beginning of an item 113 " command. The tokens should be separated with '\|'. The initial '\' should 114 " be escaped. The default is '\\bibitem\|\\item'. 115 " 116 " * g:tex_itemize_env 117 " 118 " A list of environment names, separated with '\|', where the items (item 119 " commands matching g:tex_items) may appear. The default is 120 " 'itemize\|description\|enumerate\|thebibliography'. 121 " 122 " * g:tex_noindent_env 123 " 124 " A list of environment names. separated with '\|', where no indentation is 125 " required. The default is 'document\|verbatim'. 126 " }}} 127 128 " Only define the function once 129 if exists("b:did_indent") 130 finish 131 endif 132 133 let s:cpo_save = &cpo 134 set cpo&vim 135 136 " Define global variable {{{ 137 138 let b:did_indent = 1 139 140 if !exists("g:tex_indent_items") 141 let g:tex_indent_items = 1 142 endif 143 if !exists("g:tex_indent_brace") 144 let g:tex_indent_brace = 1 145 endif 146 if !exists("g:tex_max_scan_line") 147 let g:tex_max_scan_line = 60 148 endif 149 if g:tex_indent_items 150 if !exists("g:tex_itemize_env") 151 let g:tex_itemize_env = 'itemize\|description\|enumerate\|thebibliography' 152 endif 153 if !exists('g:tex_items') 154 let g:tex_items = '\\bibitem\|\\item' 155 endif 156 else 157 let g:tex_items = '' 158 endif 159 160 if !exists("g:tex_noindent_env") 161 let g:tex_noindent_env = 'document\|verbatim\|lstlisting' 162 endif "}}} 163 164 " VIM Setting " {{{ 165 setlocal autoindent 166 setlocal nosmartindent 167 setlocal indentexpr=GetTeXIndent() 168 setlocal indentkeys& 169 exec 'setlocal indentkeys+=[,(,{,),},],\&' . substitute(g:tex_items, '^\|\(\\|\)', ',=', 'g') 170 let g:tex_items = '^\s*' . substitute(g:tex_items, '^\(\^\\s\*\)*', '', '') 171 let b:undo_indent = "setlocal autoindent< indentexpr< indentkeys< smartindent<" 172 " }}} 173 174 function! GetTeXIndent() " {{{ 175 " Find a non-blank line above the current line. 176 let lnum = prevnonblank(v:lnum - 1) 177 let cnum = v:lnum 178 179 " Comment line is not what we need. 180 while lnum != 0 && getline(lnum) =~ '^\s*%' 181 let lnum = prevnonblank(lnum - 1) 182 endwhile 183 184 " At the start of the file use zero indent. 185 if lnum == 0 186 return 0 187 endif 188 189 let line = substitute(getline(lnum), '\s*%.*', '','g') " last line 190 let cline = substitute(getline(v:lnum), '\s*%.*', '', 'g') " current line 191 192 let ccol = 1 193 while cline[ccol] =~ '\s' 194 let ccol += 1 195 endwhile 196 197 " We are in verbatim, so do what our user what. 198 if synIDattr(synID(v:lnum, ccol, 1), "name") == "texZone" 199 if empty(cline) 200 return indent(lnum) 201 else 202 return indent(v:lnum) 203 endif 204 endif 205 206 if lnum == 0 207 return 0 208 endif 209 210 let ind = indent(lnum) 211 let stay = 1 212 213 " New code for comment: retain the indent of current line 214 if cline =~ '^\s*%' 215 return indent(v:lnum) 216 endif 217 218 " Add a 'shiftwidth' after beginning of environments. 219 " Don't add it for \begin{document} and \begin{verbatim} 220 " if line =~ '^\s*\\begin{\(.*\)}' && line !~ 'verbatim' 221 " LH modification : \begin does not always start a line 222 " ZYC modification : \end after \begin won't cause wrong indent anymore 223 if line =~ '\\begin{.*}' 224 if line !~ g:tex_noindent_env 225 let ind = ind + shiftwidth() 226 let stay = 0 227 endif 228 229 if g:tex_indent_items 230 " Add another sw for item-environments 231 if line =~ g:tex_itemize_env 232 let ind = ind + shiftwidth() 233 let stay = 0 234 endif 235 endif 236 endif 237 238 if cline =~ '\\end{.*}' 239 let retn = s:GetEndIndentation(v:lnum) 240 if retn != -1 241 return retn 242 endif 243 end 244 " Subtract a 'shiftwidth' when an environment ends 245 if cline =~ '\\end{.*}' 246 \ && cline !~ g:tex_noindent_env 247 \ && cline !~ '\\begin{.*}.*\\end{.*}' 248 if g:tex_indent_items 249 " Remove another sw for item-environments 250 if cline =~ g:tex_itemize_env 251 let ind = ind - shiftwidth() 252 let stay = 0 253 endif 254 endif 255 256 let ind = ind - shiftwidth() 257 let stay = 0 258 endif 259 260 if g:tex_indent_brace 261 if line =~ '[[{]$' 262 let ind += shiftwidth() 263 let stay = 0 264 endif 265 266 if cline =~ '^\s*\\\?[\]}]' && s:CheckPairedIsLastCharacter(v:lnum, ccol) 267 let ind -= shiftwidth() 268 let stay = 0 269 endif 270 271 if line !~ '^\s*\\\?[\]}]' 272 for i in range(1, strlen(line)-1) 273 let char = line[i] 274 if char == ']' || char == '}' 275 if s:CheckPairedIsLastCharacter(lnum, i) 276 let ind -= shiftwidth() 277 let stay = 0 278 endif 279 endif 280 endfor 281 endif 282 endif 283 284 " Special treatment for 'item' 285 " ---------------------------- 286 287 if g:tex_indent_items 288 " '\item' or '\bibitem' itself: 289 if cline =~ g:tex_items 290 let ind = ind - shiftwidth() 291 let stay = 0 292 endif 293 " lines following to '\item' are indented once again: 294 if line =~ g:tex_items 295 let ind = ind + shiftwidth() 296 let stay = 0 297 endif 298 endif 299 300 if stay && mode() == 'i' 301 " If there is no obvious indentation hint, and indentation is triggered 302 " in insert mode, we trust our user. 303 if empty(cline) 304 return ind 305 else 306 return max([indent(v:lnum), s:GetLastBeginIndentation(v:lnum)]) 307 endif 308 else 309 return ind 310 endif 311 endfunction "}}} 312 313 function! s:GetLastBeginIndentation(lnum) " {{{ 314 let matchend = 1 315 for lnum in range(a:lnum-1, max([a:lnum - g:tex_max_scan_line, 1]), -1) 316 let line = getline(lnum) 317 if line =~ '\\end{.*}' 318 let matchend += 1 319 endif 320 if line =~ '\\begin{.*}' 321 let matchend -= 1 322 endif 323 if matchend == 0 324 if line =~ g:tex_noindent_env 325 return indent(lnum) 326 endif 327 if line =~ g:tex_itemize_env 328 return indent(lnum) + 2 * shiftwidth() 329 endif 330 return indent(lnum) + shiftwidth() 331 endif 332 endfor 333 return -1 334 endfunction 335 336 function! s:GetEndIndentation(lnum) " {{{ 337 if getline(a:lnum) =~ '\\begin{.*}.*\\end{.*}' 338 return -1 339 endif 340 341 let min_indent = 100 342 let matchend = 1 343 for lnum in range(a:lnum-1, max([a:lnum-g:tex_max_scan_line, 1]), -1) 344 let line = getline(lnum) 345 if line =~ '\\end{.*}' 346 let matchend += 1 347 endif 348 if line =~ '\\begin{.*}' 349 let matchend -= 1 350 endif 351 if matchend == 0 352 return indent(lnum) 353 endif 354 if !empty(line) 355 let min_indent = min([min_indent, indent(lnum)]) 356 endif 357 endfor 358 return min_indent - shiftwidth() 359 endfunction 360 361 " Most of the code is from matchparen.vim 362 function! s:CheckPairedIsLastCharacter(lnum, col) "{{{ 363 let c_lnum = a:lnum 364 let c_col = a:col+1 365 366 let line = getline(c_lnum) 367 if line[c_col-1] == '\' 368 let c_col = c_col + 1 369 endif 370 let c = line[c_col-1] 371 372 let plist = split(&matchpairs, '.\zs[:,]') 373 let i = index(plist, c) 374 if i < 0 375 return 0 376 endif 377 378 " Figure out the arguments for searchpairpos(). 379 if i % 2 == 0 380 let s_flags = 'nW' 381 let c2 = plist[i + 1] 382 else 383 let s_flags = 'nbW' 384 let c2 = c 385 let c = plist[i - 1] 386 endif 387 if c == '[' 388 let c = '\[' 389 let c2 = '\]' 390 endif 391 392 " Find the match. When it was just before the cursor move it there for a 393 " moment. 394 let save_cursor = winsaveview() 395 call cursor(c_lnum, c_col) 396 397 " When not in a string or comment ignore matches inside them. 398 " We match "escape" for special items, such as lispEscapeSpecial. 399 let s_skip ='synIDattr(synID(line("."), col("."), 0), "name") ' . 400 \ '=~? "string\\|character\\|singlequote\\|escape\\|comment"' 401 execute 'if' s_skip '| let s_skip = 0 | endif' 402 403 let stopline = max([0, c_lnum - g:tex_max_scan_line]) 404 405 " Limit the search time to 300 msec to avoid a hang on very long lines. 406 " This fails when a timeout is not supported. 407 try 408 let [m_lnum, m_col] = searchpairpos(c, '', c2, s_flags, s_skip, stopline, 100) 409 catch /E118/ 410 endtry 411 412 call winrestview(save_cursor) 413 414 if m_lnum > 0 415 let line = getline(m_lnum) 416 return strlen(line) == m_col 417 endif 418 419 return 0 420 endfunction "}}} 421 422 let &cpo = s:cpo_save 423 unlet s:cpo_save 424 425 " vim: set sw=4 textwidth=80: