| " Vim indent file |
| " Language: cobol |
| " Author: Tim Pope <vimNOSPAM@tpope.info> |
| " $Id: cobol.vim,v 1.1 2007/05/05 18:08:19 vimboss Exp $ |
| |
| if exists("b:did_indent") |
| finish |
| endif |
| let b:did_indent = 1 |
| |
| setlocal expandtab |
| setlocal indentexpr=GetCobolIndent(v:lnum) |
| setlocal indentkeys& |
| setlocal indentkeys+=0<*>,0/,0$,0=01,=~division,=~section,0=~end,0=~then,0=~else,0=~when,*<Return>,. |
| |
| " Only define the function once. |
| if exists("*GetCobolIndent") |
| finish |
| endif |
| |
| let s:skip = 'getline(".") =~ "^.\\{6\\}[*/$-]\\|\"[^\"]*\""' |
| |
| function! s:prevgood(lnum) |
| " Find a non-blank line above the current line. |
| " Skip over comments. |
| let lnum = a:lnum |
| while lnum > 0 |
| let lnum = prevnonblank(lnum - 1) |
| let line = getline(lnum) |
| if line !~? '^\s*[*/$-]' && line !~? '^.\{6\}[*/$CD-]' |
| break |
| endif |
| endwhile |
| return lnum |
| endfunction |
| |
| function! s:stripped(lnum) |
| return substitute(strpart(getline(a:lnum),0,72),'^\s*','','') |
| endfunction |
| |
| function! s:optionalblock(lnum,ind,blocks,clauses) |
| let ind = a:ind |
| let clauses = '\c\<\%(\<NOT\s\+\)\@<!\%(NOT\s\+\)\=\%('.a:clauses.'\)' |
| let begin = '\c-\@<!\<\%('.a:blocks.'\)\>' |
| let beginfull = begin.'\ze.*\%(\n\%(\s*\%([*/$-].*\)\=\n\)*\)\=\s*\%('.clauses.'\)' |
| let end = '\c\<end-\%('.a:blocks.'\)\>\|\%(\.\%( \|$\)\)\@=' |
| let cline = s:stripped(a:lnum) |
| let line = s:stripped(s:prevgood(a:lnum)) |
| if cline =~? clauses "&& line !~? '^search\>' |
| call cursor(a:lnum,1) |
| let lastclause = searchpair(beginfull,clauses,end,'bWr',s:skip) |
| if getline(lastclause) =~? clauses && s:stripped(lastclause) !~? '^'.begin |
| let ind = indent(lastclause) |
| elseif lastclause > 0 |
| let ind = indent(lastclause) + &sw |
| "let ind = ind + &sw |
| endif |
| elseif line =~? clauses && cline !~? end |
| let ind = ind + &sw |
| endif |
| return ind |
| endfunction |
| |
| function! GetCobolIndent(lnum) abort |
| let minshft = 6 |
| let ashft = minshft + 1 |
| let bshft = ashft + 4 |
| " (Obsolete) numbered lines |
| if getline(a:lnum) =~? '^\s*\d\{6\}\%($\|[ */$CD-]\)' |
| return 0 |
| endif |
| let cline = s:stripped(a:lnum) |
| " Comments, etc. must start in the 7th column |
| if cline =~? '^[*/$-]' |
| return minshft |
| elseif cline =~# '^[CD]' && indent(a:lnum) == minshft |
| return minshft |
| endif |
| " Divisions, sections, and file descriptions start in area A |
| if cline =~? '\<\(DIVISION\|SECTION\)\%($\|\.\)' || cline =~? '^[FS]D\>' |
| return ashft |
| endif |
| " Fields |
| if cline =~? '^0*\(1\|77\)\>' |
| return ashft |
| endif |
| if cline =~? '^\d\+\>' |
| let cnum = matchstr(cline,'^\d\+\>') |
| let default = 0 |
| let step = -1 |
| while step < 2 |
| let lnum = a:lnum |
| while lnum > 0 && lnum < line('$') && lnum > a:lnum - 500 && lnum < a:lnum + 500 |
| let lnum = step > 0 ? nextnonblank(lnum + step) : prevnonblank(lnum + step) |
| let line = getline(lnum) |
| let lindent = indent(lnum) |
| if line =~? '^\s*\d\+\>' |
| let num = matchstr(line,'^\s*\zs\d\+\>') |
| if 0+cnum == num |
| return lindent |
| elseif 0+cnum > num && default < lindent + &sw |
| let default = lindent + &sw |
| endif |
| elseif lindent < bshft && lindent >= ashft |
| break |
| endif |
| endwhile |
| let step = step + 2 |
| endwhile |
| return default ? default : bshft |
| endif |
| let lnum = s:prevgood(a:lnum) |
| " Hit the start of the file, use "zero" indent. |
| if lnum == 0 |
| return ashft |
| endif |
| " Initial spaces are ignored |
| let line = s:stripped(lnum) |
| let ind = indent(lnum) |
| " Paragraphs. There may be some false positives. |
| if cline =~? '^\(\a[A-Z0-9-]*[A-Z0-9]\|\d[A-Z0-9-]*\a\)\.' "\s*$' |
| if cline !~? '^EXIT\s*\.' && line =~? '\.\s*$' |
| return ashft |
| endif |
| endif |
| " Paragraphs in the identification division. |
| "if cline =~? '^\(PROGRAM-ID\|AUTHOR\|INSTALLATION\|' . |
| "\ 'DATE-WRITTEN\|DATE-COMPILED\|SECURITY\)\>' |
| "return ashft |
| "endif |
| if line =~? '\.$' |
| " XXX |
| return bshft |
| endif |
| if line =~? '^PERFORM\>' |
| let perfline = substitute(line, '\c^PERFORM\s*', "", "") |
| if perfline =~? '^\%(\k\+\s\+TIMES\)\=\s*$' |
| let ind = ind + &sw |
| elseif perfline =~? '^\%(WITH\s\+TEST\|VARYING\|UNTIL\)\>.*[^.]$' |
| let ind = ind + &sw |
| endif |
| endif |
| if line =~? '^\%(IF\|THEN\|ELSE\|READ\|EVALUATE\|SEARCH\|SELECT\)\>' |
| let ind = ind + &sw |
| endif |
| let ind = s:optionalblock(a:lnum,ind,'ADD\|COMPUTE\|DIVIDE\|MULTIPLY\|SUBTRACT','ON\s\+SIZE\s\+ERROR') |
| let ind = s:optionalblock(a:lnum,ind,'STRING\|UNSTRING\|ACCEPT\|DISPLAY\|CALL','ON\s\+OVERFLOW\|ON\s\+EXCEPTION') |
| if cline !~? '^AT\s\+END\>' || line !~? '^SEARCH\>' |
| 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') |
| endif |
| if cline =~? '^WHEN\>' |
| call cursor(a:lnum,1) |
| " We also search for READ so that contained AT ENDs are skipped |
| let lastclause = searchpair('\c-\@<!\<\%(SEARCH\|EVALUATE\|READ\)\>','\c\<\%(WHEN\|AT\s\+END\)\>','\c\<END-\%(SEARCH\|EVALUATE\|READ\)\>','bW',s:skip) |
| let g:foo = s:stripped(lastclause) |
| if s:stripped(lastclause) =~? '\c\<\%(WHEN\|AT\s\+END\)\>' |
| "&& s:stripped(lastclause) !~? '^\%(SEARCH\|EVALUATE\|READ\)\>' |
| let ind = indent(lastclause) |
| elseif lastclause > 0 |
| let ind = indent(lastclause) + &sw |
| endif |
| elseif line =~? '^WHEN\>' |
| let ind = ind + &sw |
| endif |
| "I'm not sure why I had this |
| "if line =~? '^ELSE\>-\@!' && line !~? '\.$' |
| "let ind = indent(s:prevgood(lnum)) |
| "endif |
| if cline =~? '^\(END\)\>-\@!' |
| " On lines with just END, 'guess' a simple shift left |
| let ind = ind - &sw |
| elseif cline =~? '^\(END-IF\|THEN\|ELSE\)\>-\@!' |
| call cursor(a:lnum,indent(a:lnum)) |
| let match = searchpair('\c-\@<!\<IF\>','\c-\@<!\%(THEN\|ELSE\)\>','\c-\@<!\<END-IF\>\zs','bnW',s:skip) |
| if match > 0 |
| let ind = indent(match) |
| endif |
| elseif cline =~? '^END-[A-Z]' |
| let beginword = matchstr(cline,'\c\<END-\zs[A-Z0-9-]\+') |
| let endword = 'END-'.beginword |
| let first = 0 |
| let suffix = '.*\%(\n\%(\%(\s*\|.\{6\}\)[*/].*\n\)*\)\=\s*' |
| if beginword =~? '^\%(ADD\|COMPUTE\|DIVIDE\|MULTIPLY\|SUBTRACT\)$' |
| let beginword = beginword . suffix . '\<\%(NOT\s\+\)\=ON\s\+SIZE\s\+ERROR' |
| let g:beginword = beginword |
| let first = 1 |
| elseif beginword =~? '^\%(STRING\|UNSTRING\)$' |
| let beginword = beginword . suffix . '\<\%(NOT\s\+\)\=ON\s\+OVERFLOW' |
| let first = 1 |
| elseif beginword =~? '^\%(ACCEPT\|DISPLAY\)$' |
| let beginword = beginword . suffix . '\<\%(NOT\s\+\)\=ON\s\+EXCEPTION' |
| let first = 1 |
| elseif beginword ==? 'CALL' |
| let beginword = beginword . suffix . '\<\%(NOT\s\+\)\=ON\s\+\%(EXCEPTION\|OVERFLOW\)' |
| let first = 1 |
| elseif beginword =~? '^\%(DELETE\|REWRITE\|START\|READ\|WRITE\)$' |
| let first = 1 |
| let beginword = beginword . suffix . '\<\%(NOT\s\+\)\=\(INVALID\s\+KEY' |
| if beginword =~? '^READ' |
| let first = 0 |
| let beginword = beginword . '\|AT\s\+END\|NO\s\+DATA' |
| elseif beginword =~? '^WRITE' |
| let beginword = beginword . '\|AT\s\+END-OF-PAGE' |
| endif |
| let beginword = beginword . '\)' |
| endif |
| call cursor(a:lnum,indent(a:lnum)) |
| let match = searchpair('\c-\@<!\<'.beginword.'\>','','\c\<'.endword.'\>\zs','bnW'.(first? 'r' : ''),s:skip) |
| if match > 0 |
| let ind = indent(match) |
| elseif cline =~? '^\(END-\(READ\|EVALUATE\|SEARCH\|PERFORM\)\)\>' |
| let ind = ind - &sw |
| endif |
| endif |
| return ind < bshft ? bshft : ind |
| endfunction |