blob: 590a729df4c16a6912fbae3dc0e2ddd2efa57018 [file] [log] [blame]
Bram Moolenaarc81e5e72007-05-05 18:24:42 +00001" Vim indent file
2" Language: cobol
Bram Moolenaar63b74a82019-03-24 15:09:13 +01003" Maintainer: Ankit Jain <ajatkj@yahoo.co.in>
4" (formerly Tim Pope <vimNOSPAM@tpope.info>)
Bram Moolenaar5c736222010-01-06 20:54:52 +01005" $Id: cobol.vim,v 1.1 2007/05/05 18:08:19 vimboss Exp $
Bram Moolenaar63b74a82019-03-24 15:09:13 +01006" 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
Bram Moolenaarc81e5e72007-05-05 18:24:42 +000010
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
Bram Moolenaar3ec574f2017-06-13 18:12:01 +020060 let ind = indent(lastclause) + shiftwidth()
61 "let ind = ind + shiftwidth()
Bram Moolenaarc81e5e72007-05-05 18:24:42 +000062 endif
63 elseif line =~? clauses && cline !~? end
Bram Moolenaar3ec574f2017-06-13 18:12:01 +020064 let ind = ind + shiftwidth()
Bram Moolenaarc81e5e72007-05-05 18:24:42 +000065 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
Bram Moolenaar63b74a82019-03-24 15:09:13 +010074 " #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-]\)'
Bram Moolenaarc81e5e72007-05-05 18:24:42 +000077 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
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200108 elseif 0+cnum > num && default < lindent + shiftwidth()
109 let default = lindent + shiftwidth()
Bram Moolenaarc81e5e72007-05-05 18:24:42 +0000110 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*$'
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200145 let ind = ind + shiftwidth()
Bram Moolenaarc81e5e72007-05-05 18:24:42 +0000146 elseif perfline =~? '^\%(WITH\s\+TEST\|VARYING\|UNTIL\)\>.*[^.]$'
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200147 let ind = ind + shiftwidth()
Bram Moolenaarc81e5e72007-05-05 18:24:42 +0000148 endif
149 endif
150 if line =~? '^\%(IF\|THEN\|ELSE\|READ\|EVALUATE\|SEARCH\|SELECT\)\>'
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200151 let ind = ind + shiftwidth()
Bram Moolenaarc81e5e72007-05-05 18:24:42 +0000152 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
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200167 let ind = indent(lastclause) + shiftwidth()
Bram Moolenaarc81e5e72007-05-05 18:24:42 +0000168 endif
169 elseif line =~? '^WHEN\>'
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200170 let ind = ind + shiftwidth()
Bram Moolenaarc81e5e72007-05-05 18:24:42 +0000171 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
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200178 let ind = ind - shiftwidth()
Bram Moolenaarc81e5e72007-05-05 18:24:42 +0000179 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\)\)\>'
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200219 let ind = ind - shiftwidth()
Bram Moolenaarc81e5e72007-05-05 18:24:42 +0000220 endif
221 endif
222 return ind < bshft ? bshft : ind
223endfunction