blob: 83523df71308f827c4d0ce8681faa8e97c0bcfd7 [file] [log] [blame]
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00001" Vim indent file generic utility functions
2" Language: * (various)
3" Maintainer: Dave Silvia <dsilvia@mchsi.com>
4" Date: 6/30/2004
5
6" SUMMARY: To use GenericIndent, indent/<your_filename>.vim would have the
7" following general format:
8"
9" if exists("b:did_indent") | finish | endif
10" let b:did_indent = 1
11" runtime indent/GenericIndent.vim
12" let b:indentStmts=''
13" let b:dedentStmts=''
14" let b:allStmts=''
15" setlocal indentexpr=GenericIndent()
16" setlocal indentkeys=<your_keys>
17" call GenericIndentStmts(<your_stmts>)
18" call GenericDedentStmts(<your_stmts>)
19" call GenericAllStmts()
20"
21" END SUMMARY:
22
23" NOTE: b:indentStmts, b:dedentStmts, and b:allStmts need to be initialized
24" to '' before callin the functions because 'indent.vim' explicitly
25" 'unlet's b:did_indent. This means that the lists will compound if
26" you change back and forth between buffers. This is true as of
27" version 6.3, 6/23/2004.
28"
29" NOTE: By default, GenericIndent is case sensitive.
30" let b:case_insensitive=1 if you want to ignore case, e.g. DOS batch files
31
32" The function 'GenericIndent' is data driven and handles most all cases of
33" indent checking if you first set up the data. To use this function follow
34" the example below (taken from the file indent/MuPAD_source.vim)
35"
36" Before you start, source this file in indent/<your_script>.vim to have it
37" define functions for your use.
38"
39"runtime indent/GenericIndent.vim
40"
41" The data is in 5 sets:
42"
43" First, set the data set 'indentexpr' to GenericIndent().
44"
45"setlocal indentexpr=GenericIndent()
46"
47" Second, set the data set 'indentkeys' to the keywords/expressions that need
48" to be checked for 'indenting' _as_ they typed.
49"
50"setlocal indentkeys==end_proc,=else,=then,=elif,=end_if,=end_case,=until,=end_repeat,=end_domain,=end_for,=end_while,=end,o,O
51"
52" NOTE: 'o,O' at the end of the previous line says you wish to be called
53" whenever a newline is placed in the buffer. This allows the previous line
54" to be checked for indentation parameters.
55"
56" Third, set the data set 'b:indentStmts' to the keywords/expressions that, when
57" they are on a line _when_ you _press_ the _<Enter>_ key,
58" you wish to have the next line indented.
59"
60"call GenericIndentStmts('begin,if,then,else,elif,case,repeat,until,domain,do')
61"
62" Fourth, set the data set 'b:dedentStmts' to the keywords/expressions that, when
63" they are on a line you are currently typing, you wish to have that line
64" 'dedented' (having already been indented because of the previous line's
65" indentation).
66"
67"call GenericDedentStmts('end_proc,then,else,elif,end_if,end_case,until,end_repeat,end_domain,end_for,end_while,end')
68"
69" Fifth, set the data set 'b:allStmts' to the concatenation of the third and
70" fourth data sets, used for checking when more than one keyword/expression
71" is on a line.
72"
73"call GenericAllStmts()
74"
75" NOTE: GenericIndentStmts uses two variables: 'b:indentStmtOpen' and
76" 'b:indentStmtClose' which default to '\<' and '\>' respectively. You can
77" set (let) these to any value you wish before calling GenericIndentStmts with
78" your list. Similarly, GenericDedentStmts uses 'b:dedentStmtOpen' and
79" 'b:dedentStmtClose'.
80"
81" NOTE: Patterns may be used in the lists passed to Generic[In|De]dentStmts
82" since each element in the list is copied verbatim.
83"
84" Optionally, you can set the DEBUGGING flag within your script to have the
85" debugging messages output. See below for description. This can also be set
86" (let) from the command line within your editing buffer.
87"
88"let b:DEBUGGING=1
89"
90" See:
91" :h runtime
92" :set runtimepath ?
93" to familiarize yourself with how this works and where you should have this
94" file and your file(s) installed.
95"
96" For help with setting 'indentkeys' see:
97" :h indentkeys
98" Also, for some good examples see 'indent/sh.vim' and 'indent/vim.vim' as
99" well as files for other languages you may be familiar with.
100"
101"
102" Alternatively, if you'd rather specify yourself, you can enter
103" 'b:indentStmts', 'b:dedentStmts', and 'b:allStmts' 'literally':
104"
105"let b:indentStmts='\<begin\>\|\<if\>\|\<then\>\|\<else\>\|\<elif\>\|\<case\>\|\<repeat\>\|\<until\>\|\<domain\>\|\<do\>'
106"let b:dedentStmts='\<end_proc\>\|\<else\>\|\<elif\>\|\<end_if\>\|\<end_case\>\|\<until\>\|\<end_repeat\>\|\<end_domain\>\|\<end_for\>\|\<end_while\>\|\<end\>'
107"let b:allStmts=b:indentStmts.'\|'.b:dedentStmts
108"
109" This is only useful if you have particularly different parameters for
110" matching each statement.
111
112" RECAP: From indent/MuPAD_source.vim
113"
114"if exists("b:did_indent") | finish | endif
115"
116"let b:did_indent = 1
117"
118"runtime indent/GenericIndent.vim
119"
120"setlocal indentexpr=GenericIndent()
121"setlocal indentkeys==end_proc,=then,=else,=elif,=end_if,=end_case,=until,=end_repeat,=end_domain,=end_for,=end_while,=end,o,O
122"call GenericIndentStmts('begin,if,then,else,elif,case,repeat,until,domain,do')
123"call GenericDedentStmts('end_proc,then,else,elif,end_if,end_case,until,end_repeat,end_domain,end_for,end_while,end')
124"call GenericAllStmts()
125"
126" END RECAP:
127
128let s:hit=0
129let s:lastVlnum=0
130let s:myScriptName=expand("<sfile>:t")
131
132if exists("*GenericIndent")
133 finish
134endif
135
136function GenericAllStmts()
137 let b:allStmts=b:indentStmts.'\|'.b:dedentStmts
138 call DebugGenericIndent(expand("<sfile>").": "."b:indentStmts: ".b:indentStmts.", b:dedentStmts: ".b:dedentStmts.", b:allStmts: ".b:allStmts)
139endfunction
140
141function GenericIndentStmts(stmts)
142 let Stmts=a:stmts
143 let Comma=match(Stmts,',')
144 if Comma == -1 || Comma == strlen(Stmts)-1
145 echoerr "Must supply a comma separated list of at least 2 entries."
146 echoerr "Supplied list: <".Stmts.">"
147 return
148 endif
149
150 if !exists("b:indentStmtOpen")
151 let b:indentStmtOpen='\<'
152 endif
153 if !exists("b:indentStmtClose")
154 let b:indentStmtClose='\>'
155 endif
156 if !exists("b:indentStmts")
157 let b:indentStmts=''
158 endif
159 if b:indentStmts != ''
160 let b:indentStmts=b:indentStmts.'\|'
161 endif
162 call DebugGenericIndent(expand("<sfile>").": "."b:indentStmtOpen: ".b:indentStmtOpen.", b:indentStmtClose: ".b:indentStmtClose.", b:indentStmts: ".b:indentStmts.", Stmts: ".Stmts)
163 let stmtEntryBegin=0
164 let stmtEntryEnd=Comma
165 let stmtEntry=strpart(Stmts,stmtEntryBegin,stmtEntryEnd-stmtEntryBegin)
166 let Stmts=strpart(Stmts,Comma+1)
167 let Comma=match(Stmts,',')
168 let b:indentStmts=b:indentStmts.b:indentStmtOpen.stmtEntry.b:indentStmtClose
169 while Comma != -1
170 let stmtEntryEnd=Comma
171 let stmtEntry=strpart(Stmts,stmtEntryBegin,stmtEntryEnd-stmtEntryBegin)
172 let Stmts=strpart(Stmts,Comma+1)
173 let Comma=match(Stmts,',')
174 let b:indentStmts=b:indentStmts.'\|'.b:indentStmtOpen.stmtEntry.b:indentStmtClose
175 endwhile
176 let stmtEntry=Stmts
177 let b:indentStmts=b:indentStmts.'\|'.b:indentStmtOpen.stmtEntry.b:indentStmtClose
178endfunction
179
180function GenericDedentStmts(stmts)
181 let Stmts=a:stmts
182 let Comma=match(Stmts,',')
183 if Comma == -1 || Comma == strlen(Stmts)-1
184 echoerr "Must supply a comma separated list of at least 2 entries."
185 echoerr "Supplied list: <".Stmts.">"
186 return
187 endif
188
189 if !exists("b:dedentStmtOpen")
190 let b:dedentStmtOpen='\<'
191 endif
192 if !exists("b:dedentStmtClose")
193 let b:dedentStmtClose='\>'
194 endif
195 if !exists("b:dedentStmts")
196 let b:dedentStmts=''
197 endif
198 if b:dedentStmts != ''
199 let b:dedentStmts=b:dedentStmts.'\|'
200 endif
201 call DebugGenericIndent(expand("<sfile>").": "."b:dedentStmtOpen: ".b:dedentStmtOpen.", b:dedentStmtClose: ".b:dedentStmtClose.", b:dedentStmts: ".b:dedentStmts.", Stmts: ".Stmts)
202 let stmtEntryBegin=0
203 let stmtEntryEnd=Comma
204 let stmtEntry=strpart(Stmts,stmtEntryBegin,stmtEntryEnd-stmtEntryBegin)
205 let Stmts=strpart(Stmts,Comma+1)
206 let Comma=match(Stmts,',')
207 let b:dedentStmts=b:dedentStmts.b:dedentStmtOpen.stmtEntry.b:dedentStmtClose
208 while Comma != -1
209 let stmtEntryEnd=Comma
210 let stmtEntry=strpart(Stmts,stmtEntryBegin,stmtEntryEnd-stmtEntryBegin)
211 let Stmts=strpart(Stmts,Comma+1)
212 let Comma=match(Stmts,',')
213 let b:dedentStmts=b:dedentStmts.'\|'.b:dedentStmtOpen.stmtEntry.b:dedentStmtClose
214 endwhile
215 let stmtEntry=Stmts
216 let b:dedentStmts=b:dedentStmts.'\|'.b:dedentStmtOpen.stmtEntry.b:dedentStmtClose
217endfunction
218
219" Debugging function. Displays messages in the command area which can be
220" reviewed using ':messages'. To turn it on use ':let b:DEBUGGING=1'. Once
221" on, turn off by using ':let b:DEBUGGING=0. If you don't want it at all and
222" feel it's slowing down your editing (you must have an _awfully_ slow
223" machine!;-> ), you can just comment out the calls to it from 'GenericIndent'
224" below. No need to remove the function or the calls, tho', as you never can
225" tell when they might come in handy!;-)
226function DebugGenericIndent(msg)
227 if exists("b:DEBUGGING") && b:DEBUGGING
228 echomsg '['.s:hit.']'.s:myScriptName."::".a:msg
229 endif
230endfunction
231
232function GenericIndent()
233 " save ignore case option. Have to set noignorecase for the match
234 " functions to do their job the way we want them to!
235 " NOTE: if you add a return to this function be sure you do
236 " if IgnoreCase | set ignorecase | endif
237 " before returning. You can just cut and paste from here.
238 let IgnoreCase=&ignorecase
239 " let b:case_insensitive=1 if you want to ignore case, e.g. DOS batch files
240 if !exists("b:case_insensitive")
241 set noignorecase
242 endif
243 " this is used to let DebugGenericIndent display which invocation of the
244 " function goes with which messages.
245 let s:hit=s:hit+1
246 let lnum=v:lnum
247 let cline=getline(lnum)
248 let lnum=prevnonblank(lnum)
249 if lnum==0 | if IgnoreCase | set ignorecase | endif | return 0 | endif
250 let pline=getline(lnum)
251 let ndnt=indent(lnum)
252 if !exists("b:allStmts")
253 call GenericAllStmts()
254 endif
255
256 call DebugGenericIndent(expand("<sfile>").": "."cline=<".cline.">, pline=<".pline.">, lnum=".lnum.", v:lnum=".v:lnum.", ndnt=".ndnt)
257 if lnum==v:lnum
258 " current line, only check dedent
259 "
260 " just dedented this line, don't need to do it again.
261 " another dedentStmts was added or an end%[_*] was completed.
262 if s:lastVlnum==v:lnum
263 if IgnoreCase | set ignorecase | endif
264 return ndnt
265 endif
266 let s:lastVlnum=v:lnum
267 call DebugGenericIndent(expand("<sfile>").": "."Checking dedent")
268 let srcStr=cline
269 let dedentKeyBegin=match(srcStr,b:dedentStmts)
270 if dedentKeyBegin != -1
271 let dedentKeyEnd=matchend(srcStr,b:dedentStmts)
272 let dedentKeyStr=strpart(srcStr,dedentKeyBegin,dedentKeyEnd-dedentKeyBegin)
273 "only dedent if it's the beginning of the line
274 if match(srcStr,'^\s*\<'.dedentKeyStr.'\>') != -1
275 call DebugGenericIndent(expand("<sfile>").": "."It's the beginning of the line, dedent")
276 let ndnt=ndnt-&shiftwidth
277 endif
278 endif
279 call DebugGenericIndent(expand("<sfile>").": "."dedent - returning ndnt=".ndnt)
280 else
281 " previous line, only check indent
282 call DebugGenericIndent(expand("<sfile>").": "."Checking indent")
283 let srcStr=pline
284 let indentKeyBegin=match(srcStr,b:indentStmts)
285 if indentKeyBegin != -1
286 " only indent if it's the last indentStmts in the line
287 let allKeyBegin=match(srcStr,b:allStmts)
288 let allKeyEnd=matchend(srcStr,b:allStmts)
289 let allKeyStr=strpart(srcStr,allKeyBegin,allKeyEnd-allKeyBegin)
290 let srcStr=strpart(srcStr,allKeyEnd)
291 let allKeyBegin=match(srcStr,b:allStmts)
292 if allKeyBegin != -1
293 " not the end of the line, check what is and only indent if
294 " it's an indentStmts
295 call DebugGenericIndent(expand("<sfile>").": "."Multiple words in line, checking if last is indent")
296 while allKeyBegin != -1
297 let allKeyEnd=matchend(srcStr,b:allStmts)
298 let allKeyStr=strpart(srcStr,allKeyBegin,allKeyEnd-allKeyBegin)
299 let srcStr=strpart(srcStr,allKeyEnd)
300 let allKeyBegin=match(srcStr,b:allStmts)
301 endwhile
302 if match(b:indentStmts,allKeyStr) != -1
303 call DebugGenericIndent(expand("<sfile>").": "."Last word in line is indent")
304 let ndnt=ndnt+&shiftwidth
305 endif
306 else
307 " it's the last indentStmts in the line, go ahead and indent
308 let ndnt=ndnt+&shiftwidth
309 endif
310 endif
311 call DebugGenericIndent(expand("<sfile>").": "."indent - returning ndnt=".ndnt)
312 endif
313 if IgnoreCase | set ignorecase | endif
314 return ndnt
315endfunction
316
317
318" TODO: I'm open!
319"
320" BUGS: You tell me! Probably. I just haven't found one yet or haven't been
321" told about one.
322"