blob: 119a66c1b8a4e92b032016c9398d2724837eb9e4 [file] [log] [blame]
Bram Moolenaar7db25fe2018-05-13 00:02:36 +02001" Vim indent file for TeX
2" Language: TeX
3" Maintainer: Christian Brabandt <cb@256bit.org>
4" Previous Maintainer: YiChao Zhou <broken.zhou AT gmail.com>
5" Latest Revision: 2017-05-03
6" Version: 0.9.3
7" Repository: https://github.com/chrisbra/vim-tex-indent
8" Documention: :h ft-tex-indent
9" Created: Sat, 16 Feb 2002 16:50:19 +0100
Bram Moolenaar328da0d2016-03-04 22:22:32 +010010" Please email me if you found something I can do. Comments, bug report and
11" feature request are welcome.
Bram Moolenaar61d35bd2012-03-28 20:51:51 +020012
Bram Moolenaar7db25fe2018-05-13 00:02:36 +020013" Last Update: {{{1
Bram Moolenaar61d35bd2012-03-28 20:51:51 +020014" 25th Sep 2002, by LH :
Bram Moolenaarf1568ec2011-12-14 21:17:39 +010015" (*) better support for the option
16" (*) use some regex instead of several '||'.
17" Oct 9th, 2003, by JT:
18" (*) don't change indentation of lines starting with '%'
Bram Moolenaar61d35bd2012-03-28 20:51:51 +020019" 2005/06/15, Moshe Kaminsky <kaminsky AT math.huji.ac.il>
Bram Moolenaarf1568ec2011-12-14 21:17:39 +010020" (*) New variables:
21" g:tex_items, g:tex_itemize_env, g:tex_noindent_env
Bram Moolenaar7db25fe2018-05-13 00:02:36 +020022" 2011/3/6, by Zhou YiChao <broken.zhou AT gmail.com>
Bram Moolenaarf1568ec2011-12-14 21:17:39 +010023" (*) Don't change indentation of lines starting with '%'
24" I don't see any code with '%' and it doesn't work properly
25" so I add some code.
26" (*) New features: Add smartindent-like indent for "{}" and "[]".
27" (*) New variables: g:tex_indent_brace
Bram Moolenaar7db25fe2018-05-13 00:02:36 +020028" 2011/9/25, by Zhou Yichao <broken.zhou AT gmail.com>
Bram Moolenaarf1568ec2011-12-14 21:17:39 +010029" (*) Bug fix: smartindent-like indent for "[]"
30" (*) New features: Align with "&".
Bram Moolenaar61d35bd2012-03-28 20:51:51 +020031" (*) New variable: g:tex_indent_and.
Bram Moolenaar7db25fe2018-05-13 00:02:36 +020032" 2011/10/23 by Zhou Yichao <broken.zhou AT gmail.com>
Bram Moolenaarf1568ec2011-12-14 21:17:39 +010033" (*) Bug fix: improve the smartindent-like indent for "{}" and
34" "[]".
Bram Moolenaar7db25fe2018-05-13 00:02:36 +020035" 2012/02/27 by Zhou Yichao <broken.zhou AT gmail.com>
Bram Moolenaar61d35bd2012-03-28 20:51:51 +020036" (*) Bug fix: support default folding marker.
37" (*) Indent with "&" is not very handy. Make it not enable by
38" default.
Bram Moolenaar7db25fe2018-05-13 00:02:36 +020039" 2012/03/06 by Zhou Yichao <broken.zhou AT gmail.com>
Bram Moolenaar61d35bd2012-03-28 20:51:51 +020040" (*) Modify "&" behavior and make it default again. Now "&"
41" won't align when there are more then one "&" in the previous
42" line.
43" (*) Add indent "\left(" and "\right)"
44" (*) Trust user when in "verbatim" and "lstlisting"
Bram Moolenaar7db25fe2018-05-13 00:02:36 +020045" 2012/03/11 by Zhou Yichao <broken.zhou AT gmail.com>
Bram Moolenaar61d35bd2012-03-28 20:51:51 +020046" (*) Modify "&" so that only indent when current line start with
Bram Moolenaar328da0d2016-03-04 22:22:32 +010047" "&".
Bram Moolenaar7db25fe2018-05-13 00:02:36 +020048" 2012/03/12 by Zhou Yichao <broken.zhou AT gmail.com>
Bram Moolenaar61d35bd2012-03-28 20:51:51 +020049" (*) Modify indentkeys.
Bram Moolenaar7db25fe2018-05-13 00:02:36 +020050" 2012/03/18 by Zhou Yichao <broken.zhou AT gmail.com>
Bram Moolenaar61d35bd2012-03-28 20:51:51 +020051" (*) Add &cpo
Bram Moolenaar7db25fe2018-05-13 00:02:36 +020052" 2013/05/02 by Zhou Yichao <broken.zhou AT gmail.com>
Bram Moolenaarad3b3662013-05-17 18:14:19 +020053" (*) Fix problem about GetTeXIndent checker. Thank Albert Netymk
54" for reporting this.
Bram Moolenaar7db25fe2018-05-13 00:02:36 +020055" 2014/06/23 by Zhou Yichao <broken.zhou AT gmail.com>
Bram Moolenaar328da0d2016-03-04 22:22:32 +010056" (*) Remove the feature g:tex_indent_and because it is buggy.
57" (*) If there is not any obvious indentation hints, we do not
58" alert our user's current indentation.
59" (*) g:tex_indent_brace now only works if the open brace is the
60" last character of that line.
Bram Moolenaar7db25fe2018-05-13 00:02:36 +020061" 2014/08/03 by Zhou Yichao <broken.zhou AT gmail.com>
Bram Moolenaar328da0d2016-03-04 22:22:32 +010062" (*) Indent current line if last line has larger indentation
Bram Moolenaar7db25fe2018-05-13 00:02:36 +020063" 2014/08/09 by Zhou Yichao <broken.zhou AT gmail.com>
64" (*) Add missing return value for s:GetEndIndentation(...)
65" 2017/05/02: new maintainer Christian Brabandt
66" 2017/05/02: use shiftwidth() function
67" 2017/05/02: do not add indent when environment starts and ends
68" at previous line
69" 2017/05/03: release 0.9.3 submitted for inclusion with Vim
Bram Moolenaar328da0d2016-03-04 22:22:32 +010070"
Bram Moolenaar61d35bd2012-03-28 20:51:51 +020071" }}}
Bram Moolenaar7db25fe2018-05-13 00:02:36 +020072" Only define the function once {{{1
Bram Moolenaarf1568ec2011-12-14 21:17:39 +010073if exists("b:did_indent")
74 finish
75endif
Bram Moolenaar61d35bd2012-03-28 20:51:51 +020076
77let s:cpo_save = &cpo
78set cpo&vim
Bram Moolenaar7db25fe2018-05-13 00:02:36 +020079" Define global variable {{{1
Bram Moolenaarf1568ec2011-12-14 21:17:39 +010080let b:did_indent = 1
81
Bram Moolenaarf1568ec2011-12-14 21:17:39 +010082if !exists("g:tex_indent_items")
83 let g:tex_indent_items = 1
84endif
85if !exists("g:tex_indent_brace")
86 let g:tex_indent_brace = 1
87endif
Bram Moolenaar328da0d2016-03-04 22:22:32 +010088if !exists("g:tex_max_scan_line")
89 let g:tex_max_scan_line = 60
Bram Moolenaarf1568ec2011-12-14 21:17:39 +010090endif
91if g:tex_indent_items
92 if !exists("g:tex_itemize_env")
93 let g:tex_itemize_env = 'itemize\|description\|enumerate\|thebibliography'
94 endif
95 if !exists('g:tex_items')
Bram Moolenaar7db25fe2018-05-13 00:02:36 +020096 let g:tex_items = '\\bibitem\|\\item'
Bram Moolenaarf1568ec2011-12-14 21:17:39 +010097 endif
98else
99 let g:tex_items = ''
100endif
101
Bram Moolenaar61d35bd2012-03-28 20:51:51 +0200102if !exists("g:tex_noindent_env")
103 let g:tex_noindent_env = 'document\|verbatim\|lstlisting'
104endif "}}}
Bram Moolenaar7db25fe2018-05-13 00:02:36 +0200105" VIM Setting " {{{1
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100106setlocal autoindent
107setlocal nosmartindent
108setlocal indentexpr=GetTeXIndent()
Bram Moolenaar61d35bd2012-03-28 20:51:51 +0200109setlocal indentkeys&
110exec 'setlocal indentkeys+=[,(,{,),},],\&' . substitute(g:tex_items, '^\|\(\\|\)', ',=', 'g')
111let g:tex_items = '^\s*' . substitute(g:tex_items, '^\(\^\\s\*\)*', '', '')
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100112
Bram Moolenaar7db25fe2018-05-13 00:02:36 +0200113let b:undo_indent = 'setlocal indentexpr< indentkeys< smartindent< autoindent<'
114" }}}
115function! GetTeXIndent() " {{{1
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100116 " Find a non-blank line above the current line.
117 let lnum = prevnonblank(v:lnum - 1)
Bram Moolenaar328da0d2016-03-04 22:22:32 +0100118 let cnum = v:lnum
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100119
120 " Comment line is not what we need.
121 while lnum != 0 && getline(lnum) =~ '^\s*%'
122 let lnum = prevnonblank(lnum - 1)
123 endwhile
124
125 " At the start of the file use zero indent.
126 if lnum == 0
Bram Moolenaar7db25fe2018-05-13 00:02:36 +0200127 return 0
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100128 endif
129
Bram Moolenaar328da0d2016-03-04 22:22:32 +0100130 let line = substitute(getline(lnum), '\s*%.*', '','g') " last line
131 let cline = substitute(getline(v:lnum), '\s*%.*', '', 'g') " current line
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100132
Bram Moolenaar61d35bd2012-03-28 20:51:51 +0200133 " We are in verbatim, so do what our user what.
134 if synIDattr(synID(v:lnum, indent(v:lnum), 1), "name") == "texZone"
135 if empty(cline)
136 return indent(lnum)
137 else
138 return indent(v:lnum)
139 end
140 endif
Bram Moolenaar7db25fe2018-05-13 00:02:36 +0200141
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100142 if lnum == 0
Bram Moolenaar7db25fe2018-05-13 00:02:36 +0200143 return 0
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100144 endif
145
146 let ind = indent(lnum)
Bram Moolenaar328da0d2016-03-04 22:22:32 +0100147 let stay = 1
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100148
149 " New code for comment: retain the indent of current line
150 if cline =~ '^\s*%'
151 return indent(v:lnum)
152 endif
153
Bram Moolenaar7db25fe2018-05-13 00:02:36 +0200154 " Add a 'shiftwidth' after beginning of environments
155 " But don't do it for g:tex_noindent_env or when it also ends at the
156 " previous line.
157 if line =~ '\\begin{.*}' && line !~ '\\end{.*}' && line !~ g:tex_noindent_env
158 let ind = ind + shiftwidth()
159 let stay = 0
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100160
161 if g:tex_indent_items
162 " Add another sw for item-environments
163 if line =~ g:tex_itemize_env
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200164 let ind = ind + shiftwidth()
Bram Moolenaar328da0d2016-03-04 22:22:32 +0100165 let stay = 0
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100166 endif
167 endif
168 endif
169
Bram Moolenaar328da0d2016-03-04 22:22:32 +0100170 if cline =~ '\\end{.*}'
171 let retn = s:GetEndIndentation(v:lnum)
172 if retn != -1
173 return retn
174 endif
175 end
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100176 " Subtract a 'shiftwidth' when an environment ends
Bram Moolenaar328da0d2016-03-04 22:22:32 +0100177 if cline =~ '\\end{.*}'
178 \ && cline !~ g:tex_noindent_env
179 \ && cline !~ '\\begin{.*}.*\\end{.*}'
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100180 if g:tex_indent_items
181 " Remove another sw for item-environments
182 if cline =~ g:tex_itemize_env
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200183 let ind = ind - shiftwidth()
Bram Moolenaar328da0d2016-03-04 22:22:32 +0100184 let stay = 0
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100185 endif
186 endif
187
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200188 let ind = ind - shiftwidth()
Bram Moolenaar328da0d2016-03-04 22:22:32 +0100189 let stay = 0
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100190 endif
191
192 if g:tex_indent_brace
Bram Moolenaar7db25fe2018-05-13 00:02:36 +0200193 let char = line[strlen(line)-1]
194 if char == '[' || char == '{'
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200195 let ind += shiftwidth()
Bram Moolenaar328da0d2016-03-04 22:22:32 +0100196 let stay = 0
197 endif
198
Bram Moolenaar7db25fe2018-05-13 00:02:36 +0200199 let cind = indent(v:lnum)
200 let char = cline[cind]
201 if (char == ']' || char == '}') &&
202 \ s:CheckPairedIsLastCharacter(v:lnum, cind)
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200203 let ind -= shiftwidth()
Bram Moolenaar328da0d2016-03-04 22:22:32 +0100204 let stay = 0
205 endif
206
Bram Moolenaar7db25fe2018-05-13 00:02:36 +0200207 for i in range(indent(lnum)+1, strlen(line)-1)
208 let char = line[i]
209 if char == ']' || char == '}'
210 if s:CheckPairedIsLastCharacter(lnum, i)
211 let ind -= shiftwidth()
212 let stay = 0
Bram Moolenaar328da0d2016-03-04 22:22:32 +0100213 endif
Bram Moolenaar7db25fe2018-05-13 00:02:36 +0200214 endif
215 endfor
Bram Moolenaar61d35bd2012-03-28 20:51:51 +0200216 endif
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100217
218 " Special treatment for 'item'
219 " ----------------------------
220
221 if g:tex_indent_items
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100222 " '\item' or '\bibitem' itself:
223 if cline =~ g:tex_items
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200224 let ind = ind - shiftwidth()
Bram Moolenaar328da0d2016-03-04 22:22:32 +0100225 let stay = 0
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100226 endif
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100227 " lines following to '\item' are intented once again:
228 if line =~ g:tex_items
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200229 let ind = ind + shiftwidth()
Bram Moolenaar328da0d2016-03-04 22:22:32 +0100230 let stay = 0
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100231 endif
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100232 endif
233
Bram Moolenaar7db25fe2018-05-13 00:02:36 +0200234 if stay
235 " If there is no obvious indentation hint, we trust our user.
Bram Moolenaar328da0d2016-03-04 22:22:32 +0100236 if empty(cline)
237 return ind
238 else
239 return max([indent(v:lnum), s:GetLastBeginIndentation(v:lnum)])
240 endif
241 else
242 return ind
243 endif
244endfunction "}}}
Bram Moolenaar7db25fe2018-05-13 00:02:36 +0200245function! s:GetLastBeginIndentation(lnum) " {{{1
Bram Moolenaar328da0d2016-03-04 22:22:32 +0100246 let matchend = 1
247 for lnum in range(a:lnum-1, max([a:lnum - g:tex_max_scan_line, 1]), -1)
248 let line = getline(lnum)
249 if line =~ '\\end{.*}'
250 let matchend += 1
251 endif
252 if line =~ '\\begin{.*}'
253 let matchend -= 1
254 endif
255 if matchend == 0
Bram Moolenaarb4d6c3e2017-05-27 16:45:17 +0200256 if line =~ g:tex_itemize_env
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200257 return indent(lnum) + 2 * shiftwidth()
Bram Moolenaarb4d6c3e2017-05-27 16:45:17 +0200258 endif
Bram Moolenaar7db25fe2018-05-13 00:02:36 +0200259 if line =~ g:tex_noindent_env
260 return indent(lnum)
261 endif
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200262 return indent(lnum) + shiftwidth()
Bram Moolenaar328da0d2016-03-04 22:22:32 +0100263 endif
264 endfor
265 return -1
266endfunction
267
Bram Moolenaar7db25fe2018-05-13 00:02:36 +0200268function! s:GetEndIndentation(lnum) " {{{1
Bram Moolenaar328da0d2016-03-04 22:22:32 +0100269 if getline(a:lnum) =~ '\\begin{.*}.*\\end{.*}'
270 return -1
271 endif
272
273 let min_indent = 100
274 let matchend = 1
275 for lnum in range(a:lnum-1, max([a:lnum-g:tex_max_scan_line, 1]), -1)
276 let line = getline(lnum)
277 if line =~ '\\end{.*}'
278 let matchend += 1
279 endif
280 if line =~ '\\begin{.*}'
281 let matchend -= 1
282 endif
283 if matchend == 0
284 return indent(lnum)
285 endif
286 if !empty(line)
287 let min_indent = min([min_indent, indent(lnum)])
288 endif
289 endfor
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200290 return min_indent - shiftwidth()
Bram Moolenaar328da0d2016-03-04 22:22:32 +0100291endfunction
292
293" Most of the code is from matchparen.vim
294function! s:CheckPairedIsLastCharacter(lnum, col) "{{{
Bram Moolenaar7db25fe2018-05-13 00:02:36 +0200295 " Get the character under the cursor and check if it's in 'matchpairs'.
Bram Moolenaar328da0d2016-03-04 22:22:32 +0100296 let c_lnum = a:lnum
297 let c_col = a:col+1
298
299
Bram Moolenaar7db25fe2018-05-13 00:02:36 +0200300 let c = getline(c_lnum)[c_col-1]
Bram Moolenaar328da0d2016-03-04 22:22:32 +0100301 let plist = split(&matchpairs, '.\zs[:,]')
302 let i = index(plist, c)
303 if i < 0
304 return 0
305 endif
306
307 " Figure out the arguments for searchpairpos().
308 if i % 2 == 0
309 let s_flags = 'nW'
310 let c2 = plist[i + 1]
311 else
312 let s_flags = 'nbW'
313 let c2 = c
314 let c = plist[i - 1]
315 endif
316 if c == '['
317 let c = '\['
318 let c2 = '\]'
319 endif
320
321 " Find the match. When it was just before the cursor move it there for a
322 " moment.
323 let save_cursor = winsaveview()
324 call cursor(c_lnum, c_col)
325
326 " When not in a string or comment ignore matches inside them.
327 " We match "escape" for special items, such as lispEscapeSpecial.
328 let s_skip ='synIDattr(synID(line("."), col("."), 0), "name") ' .
329 \ '=~? "string\\|character\\|singlequote\\|escape\\|comment"'
330 execute 'if' s_skip '| let s_skip = 0 | endif'
331
332 let stopline = max([0, c_lnum - g:tex_max_scan_line])
333
334 " Limit the search time to 300 msec to avoid a hang on very long lines.
335 " This fails when a timeout is not supported.
336 try
337 let [m_lnum, m_col] = searchpairpos(c, '', c2, s_flags, s_skip, stopline, 100)
338 catch /E118/
339 endtry
340
341 call winrestview(save_cursor)
342
343 if m_lnum > 0
344 let line = getline(m_lnum)
345 return strlen(line) == m_col
346 endif
347
348 return 0
Bram Moolenaar7db25fe2018-05-13 00:02:36 +0200349endfunction
350" Reset cpo setting {{{1
Bram Moolenaarb6b046b2011-12-30 13:11:27 +0100351let &cpo = s:cpo_save
352unlet s:cpo_save
353
Bram Moolenaarf1568ec2011-12-14 21:17:39 +0100354" vim: set sw=4 textwidth=80: