1" 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" 323