Merged from the latest developing branch.
[MacVim.git] / runtime / indent / cobol.vim
blob8267a4c91fe1b995cf1379cf731bccd55e35d825
1 " Vim indent file
2 " Language:     cobol
3 " Author:       Tim Pope <vimNOSPAM@tpope.info>
4 " $Id: cobol.vim,v 1.7 2008/07/13 17:35:58 vimboss Exp $
6 if exists("b:did_indent")
7     finish
8 endif
9 let b:did_indent = 1
11 setlocal expandtab
12 setlocal indentexpr=GetCobolIndent(v:lnum)
13 setlocal indentkeys&
14 setlocal indentkeys+=0<*>,0/,0$,0=01,=~division,=~section,0=~end,0=~then,0=~else,0=~when,*<Return>,.
16 " Only define the function once.
17 if exists("*GetCobolIndent")
18     finish
19 endif
21 let s:skip = 'getline(".") =~ "^.\\{6\\}[*/$-]\\|\"[^\"]*\""'
23 function! 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
35 endfunction
37 function! s:stripped(lnum)
38     return substitute(strpart(getline(a:lnum),0,72),'^\s*','','')
39 endfunction
41 function! 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
62 endfunction
64 function! 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
216 endfunction