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