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" 71" }}} 72 73" Document: {{{ 74" 75" For proper latex experience, please put 76" let g:tex_flavor = "latex" 77" into your vimrc. 78" 79" * g:tex_indent_brace 80" 81" If this variable is unset or non-zero, it will use smartindent-like style 82" for "{}" and "[]". Now this only works if the open brace is the last 83" character of that line. 84" 85" % Example 1 86" \usetikzlibrary{ 87" external 88" } 89" 90" % Example 2 91" \tikzexternalize[ 92" prefix=tikz] 93" 94" * g:tex_indent_items 95" 96" If this variable is set, item-environments are indented like Emacs does 97" it, i.e., continuation lines are indented with a shiftwidth. 98" 99" set unset 100" ------------------------------------------------------ 101" \begin{itemize} \begin{itemize} 102" \item blablabla \item blablabla 103" bla bla bla bla bla bla 104" \item blablabla \item blablabla 105" bla bla bla bla bla bla 106" \end{itemize} \end{itemize} 107" 108" 109" * g:tex_items 110" 111" A list of tokens to be considered as commands for the beginning of an item 112" command. The tokens should be separated with '\|'. The initial '\' should 113" be escaped. The default is '\\bibitem\|\\item'. 114" 115" * g:tex_itemize_env 116" 117" A list of environment names, separated with '\|', where the items (item 118" commands matching g:tex_items) may appear. The default is 119" 'itemize\|description\|enumerate\|thebibliography'. 120" 121" * g:tex_noindent_env 122" 123" A list of environment names. separated with '\|', where no indentation is 124" required. The default is 'document\|verbatim'. 125" }}} 126 127" Only define the function once 128if exists("b:did_indent") 129 finish 130endif 131 132let s:cpo_save = &cpo 133set cpo&vim 134 135" Define global variable {{{ 136 137let b:did_indent = 1 138 139if !exists("g:tex_indent_items") 140 let g:tex_indent_items = 1 141endif 142if !exists("g:tex_indent_brace") 143 let g:tex_indent_brace = 1 144endif 145if !exists("g:tex_max_scan_line") 146 let g:tex_max_scan_line = 60 147endif 148if g:tex_indent_items 149 if !exists("g:tex_itemize_env") 150 let g:tex_itemize_env = 'itemize\|description\|enumerate\|thebibliography' 151 endif 152 if !exists('g:tex_items') 153 let g:tex_items = '\\bibitem\|\\item' 154 endif 155else 156 let g:tex_items = '' 157endif 158 159if !exists("g:tex_noindent_env") 160 let g:tex_noindent_env = 'document\|verbatim\|lstlisting' 161endif "}}} 162 163" VIM Setting " {{{ 164setlocal autoindent 165setlocal nosmartindent 166setlocal indentexpr=GetTeXIndent() 167setlocal indentkeys& 168exec 'setlocal indentkeys+=[,(,{,),},],\&' . substitute(g:tex_items, '^\|\(\\|\)', ',=', 'g') 169let g:tex_items = '^\s*' . substitute(g:tex_items, '^\(\^\\s\*\)*', '', '') 170" }}} 171 172function! GetTeXIndent() " {{{ 173 " Find a non-blank line above the current line. 174 let lnum = prevnonblank(v:lnum - 1) 175 let cnum = v:lnum 176 177 " Comment line is not what we need. 178 while lnum != 0 && getline(lnum) =~ '^\s*%' 179 let lnum = prevnonblank(lnum - 1) 180 endwhile 181 182 " At the start of the file use zero indent. 183 if lnum == 0 184 return 0 185 endif 186 187 let line = substitute(getline(lnum), '\s*%.*', '','g') " last line 188 let cline = substitute(getline(v:lnum), '\s*%.*', '', 'g') " current line 189 190 let ccol = 1 191 while cline[ccol] =~ '\s' 192 let ccol += 1 193 endwhile 194 195 " We are in verbatim, so do what our user what. 196 if synIDattr(synID(v:lnum, ccol, 1), "name") == "texZone" 197 if empty(cline) 198 return indent(lnum) 199 else 200 return indent(v:lnum) 201 endif 202 endif 203 204 if lnum == 0 205 return 0 206 endif 207 208 let ind = indent(lnum) 209 let stay = 1 210 211 " New code for comment: retain the indent of current line 212 if cline =~ '^\s*%' 213 return indent(v:lnum) 214 endif 215 216 " Add a 'shiftwidth' after beginning of environments. 217 " Don't add it for \begin{document} and \begin{verbatim} 218 " if line =~ '^\s*\\begin{\(.*\)}' && line !~ 'verbatim' 219 " LH modification : \begin does not always start a line 220 " ZYC modification : \end after \begin won't cause wrong indent anymore 221 if line =~ '\\begin{.*}' 222 if line !~ g:tex_noindent_env 223 let ind = ind + shiftwidth() 224 let stay = 0 225 endif 226 227 if g:tex_indent_items 228 " Add another sw for item-environments 229 if line =~ g:tex_itemize_env 230 let ind = ind + shiftwidth() 231 let stay = 0 232 endif 233 endif 234 endif 235 236 if cline =~ '\\end{.*}' 237 let retn = s:GetEndIndentation(v:lnum) 238 if retn != -1 239 return retn 240 endif 241 end 242 " Subtract a 'shiftwidth' when an environment ends 243 if cline =~ '\\end{.*}' 244 \ && cline !~ g:tex_noindent_env 245 \ && cline !~ '\\begin{.*}.*\\end{.*}' 246 if g:tex_indent_items 247 " Remove another sw for item-environments 248 if cline =~ g:tex_itemize_env 249 let ind = ind - shiftwidth() 250 let stay = 0 251 endif 252 endif 253 254 let ind = ind - shiftwidth() 255 let stay = 0 256 endif 257 258 if g:tex_indent_brace 259 if line =~ '[[{]$' 260 let ind += shiftwidth() 261 let stay = 0 262 endif 263 264 if cline =~ '^\s*\\\?[\]}]' && s:CheckPairedIsLastCharacter(v:lnum, ccol) 265 let ind -= shiftwidth() 266 let stay = 0 267 endif 268 269 if line !~ '^\s*\\\?[\]}]' 270 for i in range(1, strlen(line)-1) 271 let char = line[i] 272 if char == ']' || char == '}' 273 if s:CheckPairedIsLastCharacter(lnum, i) 274 let ind -= shiftwidth() 275 let stay = 0 276 endif 277 endif 278 endfor 279 endif 280 endif 281 282 " Special treatment for 'item' 283 " ---------------------------- 284 285 if g:tex_indent_items 286 " '\item' or '\bibitem' itself: 287 if cline =~ g:tex_items 288 let ind = ind - shiftwidth() 289 let stay = 0 290 endif 291 " lines following to '\item' are indented once again: 292 if line =~ g:tex_items 293 let ind = ind + shiftwidth() 294 let stay = 0 295 endif 296 endif 297 298 if stay && mode() == 'i' 299 " If there is no obvious indentation hint, and indentation is triggered 300 " in insert mode, we trust our user. 301 if empty(cline) 302 return ind 303 else 304 return max([indent(v:lnum), s:GetLastBeginIndentation(v:lnum)]) 305 endif 306 else 307 return ind 308 endif 309endfunction "}}} 310 311function! s:GetLastBeginIndentation(lnum) " {{{ 312 let matchend = 1 313 for lnum in range(a:lnum-1, max([a:lnum - g:tex_max_scan_line, 1]), -1) 314 let line = getline(lnum) 315 if line =~ '\\end{.*}' 316 let matchend += 1 317 endif 318 if line =~ '\\begin{.*}' 319 let matchend -= 1 320 endif 321 if matchend == 0 322 if line =~ g:tex_noindent_env 323 return indent(lnum) 324 endif 325 if line =~ g:tex_itemize_env 326 return indent(lnum) + 2 * shiftwidth() 327 endif 328 return indent(lnum) + shiftwidth() 329 endif 330 endfor 331 return -1 332endfunction 333 334function! s:GetEndIndentation(lnum) " {{{ 335 if getline(a:lnum) =~ '\\begin{.*}.*\\end{.*}' 336 return -1 337 endif 338 339 let min_indent = 100 340 let matchend = 1 341 for lnum in range(a:lnum-1, max([a:lnum-g:tex_max_scan_line, 1]), -1) 342 let line = getline(lnum) 343 if line =~ '\\end{.*}' 344 let matchend += 1 345 endif 346 if line =~ '\\begin{.*}' 347 let matchend -= 1 348 endif 349 if matchend == 0 350 return indent(lnum) 351 endif 352 if !empty(line) 353 let min_indent = min([min_indent, indent(lnum)]) 354 endif 355 endfor 356 return min_indent - shiftwidth() 357endfunction 358 359" Most of the code is from matchparen.vim 360function! s:CheckPairedIsLastCharacter(lnum, col) "{{{ 361 let c_lnum = a:lnum 362 let c_col = a:col+1 363 364 let line = getline(c_lnum) 365 if line[c_col-1] == '\' 366 let c_col = c_col + 1 367 endif 368 let c = line[c_col-1] 369 370 let plist = split(&matchpairs, '.\zs[:,]') 371 let i = index(plist, c) 372 if i < 0 373 return 0 374 endif 375 376 " Figure out the arguments for searchpairpos(). 377 if i % 2 == 0 378 let s_flags = 'nW' 379 let c2 = plist[i + 1] 380 else 381 let s_flags = 'nbW' 382 let c2 = c 383 let c = plist[i - 1] 384 endif 385 if c == '[' 386 let c = '\[' 387 let c2 = '\]' 388 endif 389 390 " Find the match. When it was just before the cursor move it there for a 391 " moment. 392 let save_cursor = winsaveview() 393 call cursor(c_lnum, c_col) 394 395 " When not in a string or comment ignore matches inside them. 396 " We match "escape" for special items, such as lispEscapeSpecial. 397 let s_skip ='synIDattr(synID(line("."), col("."), 0), "name") ' . 398 \ '=~? "string\\|character\\|singlequote\\|escape\\|comment"' 399 execute 'if' s_skip '| let s_skip = 0 | endif' 400 401 let stopline = max([0, c_lnum - g:tex_max_scan_line]) 402 403 " Limit the search time to 300 msec to avoid a hang on very long lines. 404 " This fails when a timeout is not supported. 405 try 406 let [m_lnum, m_col] = searchpairpos(c, '', c2, s_flags, s_skip, stopline, 100) 407 catch /E118/ 408 endtry 409 410 call winrestview(save_cursor) 411 412 if m_lnum > 0 413 let line = getline(m_lnum) 414 return strlen(line) == m_col 415 endif 416 417 return 0 418endfunction "}}} 419 420let &cpo = s:cpo_save 421unlet s:cpo_save 422 423" vim: set sw=4 textwidth=80: 424