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