1" Vim indent file 2" Language: cobol 3" Author: Tim Pope <vimNOSPAM@tpope.info> 4" $Id: cobol.vim,v 1.1 2007/05/05 18:08:19 vimboss Exp $ 5 6if exists("b:did_indent") 7 finish 8endif 9let b:did_indent = 1 10 11setlocal expandtab 12setlocal indentexpr=GetCobolIndent(v:lnum) 13setlocal indentkeys& 14setlocal indentkeys+=0<*>,0/,0$,0=01,=~division,=~section,0=~end,0=~then,0=~else,0=~when,*<Return>,. 15 16" Only define the function once. 17if exists("*GetCobolIndent") 18 finish 19endif 20 21let s:skip = 'getline(".") =~ "^.\\{6\\}[*/$-]\\|\"[^\"]*\""' 22 23function! s:prevgood(lnum) 24 " Find a non-blank line above the current line. 25 " Skip over comments. 26 let lnum = a:lnum 27 while lnum > 0 28 let lnum = prevnonblank(lnum - 1) 29 let line = getline(lnum) 30 if line !~? '^\s*[*/$-]' && line !~? '^.\{6\}[*/$CD-]' 31 break 32 endif 33 endwhile 34 return lnum 35endfunction 36 37function! s:stripped(lnum) 38 return substitute(strpart(getline(a:lnum),0,72),'^\s*','','') 39endfunction 40 41function! s:optionalblock(lnum,ind,blocks,clauses) 42 let ind = a:ind 43 let clauses = '\c\<\%(\<NOT\s\+\)\@<!\%(NOT\s\+\)\=\%('.a:clauses.'\)' 44 let begin = '\c-\@<!\<\%('.a:blocks.'\)\>' 45 let beginfull = begin.'\ze.*\%(\n\%(\s*\%([*/$-].*\)\=\n\)*\)\=\s*\%('.clauses.'\)' 46 let end = '\c\<end-\%('.a:blocks.'\)\>\|\%(\.\%( \|$\)\)\@=' 47 let cline = s:stripped(a:lnum) 48 let line = s:stripped(s:prevgood(a:lnum)) 49 if cline =~? clauses "&& line !~? '^search\>' 50 call cursor(a:lnum,1) 51 let lastclause = searchpair(beginfull,clauses,end,'bWr',s:skip) 52 if getline(lastclause) =~? clauses && s:stripped(lastclause) !~? '^'.begin 53 let ind = indent(lastclause) 54 elseif lastclause > 0 55 let ind = indent(lastclause) + &sw 56 "let ind = ind + &sw 57 endif 58 elseif line =~? clauses && cline !~? end 59 let ind = ind + &sw 60 endif 61 return ind 62endfunction 63 64function! GetCobolIndent(lnum) abort 65 let minshft = 6 66 let ashft = minshft + 1 67 let bshft = ashft + 4 68 " (Obsolete) numbered lines 69 if getline(a:lnum) =~? '^\s*\d\{6\}\%($\|[ */$CD-]\)' 70 return 0 71 endif 72 let cline = s:stripped(a:lnum) 73 " Comments, etc. must start in the 7th column 74 if cline =~? '^[*/$-]' 75 return minshft 76 elseif cline =~# '^[CD]' && indent(a:lnum) == minshft 77 return minshft 78 endif 79 " Divisions, sections, and file descriptions start in area A 80 if cline =~? '\<\(DIVISION\|SECTION\)\%($\|\.\)' || cline =~? '^[FS]D\>' 81 return ashft 82 endif 83 " Fields 84 if cline =~? '^0*\(1\|77\)\>' 85 return ashft 86 endif 87 if cline =~? '^\d\+\>' 88 let cnum = matchstr(cline,'^\d\+\>') 89 let default = 0 90 let step = -1 91 while step < 2 92 let lnum = a:lnum 93 while lnum > 0 && lnum < line('$') && lnum > a:lnum - 500 && lnum < a:lnum + 500 94 let lnum = step > 0 ? nextnonblank(lnum + step) : prevnonblank(lnum + step) 95 let line = getline(lnum) 96 let lindent = indent(lnum) 97 if line =~? '^\s*\d\+\>' 98 let num = matchstr(line,'^\s*\zs\d\+\>') 99 if 0+cnum == num 100 return lindent 101 elseif 0+cnum > num && default < lindent + &sw 102 let default = lindent + &sw 103 endif 104 elseif lindent < bshft && lindent >= ashft 105 break 106 endif 107 endwhile 108 let step = step + 2 109 endwhile 110 return default ? default : bshft 111 endif 112 let lnum = s:prevgood(a:lnum) 113 " Hit the start of the file, use "zero" indent. 114 if lnum == 0 115 return ashft 116 endif 117 " Initial spaces are ignored 118 let line = s:stripped(lnum) 119 let ind = indent(lnum) 120 " Paragraphs. There may be some false positives. 121 if cline =~? '^\(\a[A-Z0-9-]*[A-Z0-9]\|\d[A-Z0-9-]*\a\)\.' "\s*$' 122 if cline !~? '^EXIT\s*\.' && line =~? '\.\s*$' 123 return ashft 124 endif 125 endif 126 " Paragraphs in the identification division. 127 "if cline =~? '^\(PROGRAM-ID\|AUTHOR\|INSTALLATION\|' . 128 "\ 'DATE-WRITTEN\|DATE-COMPILED\|SECURITY\)\>' 129 "return ashft 130 "endif 131 if line =~? '\.$' 132 " XXX 133 return bshft 134 endif 135 if line =~? '^PERFORM\>' 136 let perfline = substitute(line, '\c^PERFORM\s*', "", "") 137 if perfline =~? '^\%(\k\+\s\+TIMES\)\=\s*$' 138 let ind = ind + &sw 139 elseif perfline =~? '^\%(WITH\s\+TEST\|VARYING\|UNTIL\)\>.*[^.]$' 140 let ind = ind + &sw 141 endif 142 endif 143 if line =~? '^\%(IF\|THEN\|ELSE\|READ\|EVALUATE\|SEARCH\|SELECT\)\>' 144 let ind = ind + &sw 145 endif 146 let ind = s:optionalblock(a:lnum,ind,'ADD\|COMPUTE\|DIVIDE\|MULTIPLY\|SUBTRACT','ON\s\+SIZE\s\+ERROR') 147 let ind = s:optionalblock(a:lnum,ind,'STRING\|UNSTRING\|ACCEPT\|DISPLAY\|CALL','ON\s\+OVERFLOW\|ON\s\+EXCEPTION') 148 if cline !~? '^AT\s\+END\>' || line !~? '^SEARCH\>' 149 let ind = s:optionalblock(a:lnum,ind,'DELETE\|REWRITE\|START\|WRITE\|READ','INVALID\s\+KEY\|AT\s\+END\|NO\s\+DATA\|AT\s\+END-OF-PAGE') 150 endif 151 if cline =~? '^WHEN\>' 152 call cursor(a:lnum,1) 153 " We also search for READ so that contained AT ENDs are skipped 154 let lastclause = searchpair('\c-\@<!\<\%(SEARCH\|EVALUATE\|READ\)\>','\c\<\%(WHEN\|AT\s\+END\)\>','\c\<END-\%(SEARCH\|EVALUATE\|READ\)\>','bW',s:skip) 155 let g:foo = s:stripped(lastclause) 156 if s:stripped(lastclause) =~? '\c\<\%(WHEN\|AT\s\+END\)\>' 157 "&& s:stripped(lastclause) !~? '^\%(SEARCH\|EVALUATE\|READ\)\>' 158 let ind = indent(lastclause) 159 elseif lastclause > 0 160 let ind = indent(lastclause) + &sw 161 endif 162 elseif line =~? '^WHEN\>' 163 let ind = ind + &sw 164 endif 165 "I'm not sure why I had this 166 "if line =~? '^ELSE\>-\@!' && line !~? '\.$' 167 "let ind = indent(s:prevgood(lnum)) 168 "endif 169 if cline =~? '^\(END\)\>-\@!' 170 " On lines with just END, 'guess' a simple shift left 171 let ind = ind - &sw 172 elseif cline =~? '^\(END-IF\|THEN\|ELSE\)\>-\@!' 173 call cursor(a:lnum,indent(a:lnum)) 174 let match = searchpair('\c-\@<!\<IF\>','\c-\@<!\%(THEN\|ELSE\)\>','\c-\@<!\<END-IF\>\zs','bnW',s:skip) 175 if match > 0 176 let ind = indent(match) 177 endif 178 elseif cline =~? '^END-[A-Z]' 179 let beginword = matchstr(cline,'\c\<END-\zs[A-Z0-9-]\+') 180 let endword = 'END-'.beginword 181 let first = 0 182 let suffix = '.*\%(\n\%(\%(\s*\|.\{6\}\)[*/].*\n\)*\)\=\s*' 183 if beginword =~? '^\%(ADD\|COMPUTE\|DIVIDE\|MULTIPLY\|SUBTRACT\)$' 184 let beginword = beginword . suffix . '\<\%(NOT\s\+\)\=ON\s\+SIZE\s\+ERROR' 185 let g:beginword = beginword 186 let first = 1 187 elseif beginword =~? '^\%(STRING\|UNSTRING\)$' 188 let beginword = beginword . suffix . '\<\%(NOT\s\+\)\=ON\s\+OVERFLOW' 189 let first = 1 190 elseif beginword =~? '^\%(ACCEPT\|DISPLAY\)$' 191 let beginword = beginword . suffix . '\<\%(NOT\s\+\)\=ON\s\+EXCEPTION' 192 let first = 1 193 elseif beginword ==? 'CALL' 194 let beginword = beginword . suffix . '\<\%(NOT\s\+\)\=ON\s\+\%(EXCEPTION\|OVERFLOW\)' 195 let first = 1 196 elseif beginword =~? '^\%(DELETE\|REWRITE\|START\|READ\|WRITE\)$' 197 let first = 1 198 let beginword = beginword . suffix . '\<\%(NOT\s\+\)\=\(INVALID\s\+KEY' 199 if beginword =~? '^READ' 200 let first = 0 201 let beginword = beginword . '\|AT\s\+END\|NO\s\+DATA' 202 elseif beginword =~? '^WRITE' 203 let beginword = beginword . '\|AT\s\+END-OF-PAGE' 204 endif 205 let beginword = beginword . '\)' 206 endif 207 call cursor(a:lnum,indent(a:lnum)) 208 let match = searchpair('\c-\@<!\<'.beginword.'\>','','\c\<'.endword.'\>\zs','bnW'.(first? 'r' : ''),s:skip) 209 if match > 0 210 let ind = indent(match) 211 elseif cline =~? '^\(END-\(READ\|EVALUATE\|SEARCH\|PERFORM\)\)\>' 212 let ind = ind - &sw 213 endif 214 endif 215 return ind < bshft ? bshft : ind 216endfunction 217