blob: 01f72126027cfbc55fafb264216465cd6066b748 [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 Moolenaar6e649222021-10-04 21:32:54 +010010" Ankit Jain 24.09.2021 add b:undo_indent (request by tpope)
Bram Moolenaarc81e5e72007-05-05 18:24:42 +000011
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
Bram Moolenaar6e649222021-10-04 21:32:54 +010022let b:undo_indent = "setlocal expandtab< indentexpr< indentkeys<"
23
Bram Moolenaarc81e5e72007-05-05 18:24:42 +000024" 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
Bram Moolenaar3ec574f2017-06-13 18:12:01 +020063 let ind = indent(lastclause) + shiftwidth()
64 "let ind = ind + shiftwidth()
Bram Moolenaarc81e5e72007-05-05 18:24:42 +000065 endif
66 elseif line =~? clauses && cline !~? end
Bram Moolenaar3ec574f2017-06-13 18:12:01 +020067 let ind = ind + shiftwidth()
Bram Moolenaarc81e5e72007-05-05 18:24:42 +000068 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
Bram Moolenaar63b74a82019-03-24 15:09:13 +010077 " #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-]\)'
Bram Moolenaarc81e5e72007-05-05 18:24:42 +000080 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
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200111 elseif 0+cnum > num && default < lindent + shiftwidth()
112 let default = lindent + shiftwidth()
Bram Moolenaarc81e5e72007-05-05 18:24:42 +0000113 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*$'
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200148 let ind = ind + shiftwidth()
Bram Moolenaarc81e5e72007-05-05 18:24:42 +0000149 elseif perfline =~? '^\%(WITH\s\+TEST\|VARYING\|UNTIL\)\>.*[^.]$'
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200150 let ind = ind + shiftwidth()
Bram Moolenaarc81e5e72007-05-05 18:24:42 +0000151 endif
152 endif
153 if line =~? '^\%(IF\|THEN\|ELSE\|READ\|EVALUATE\|SEARCH\|SELECT\)\>'
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200154 let ind = ind + shiftwidth()
Bram Moolenaarc81e5e72007-05-05 18:24:42 +0000155 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
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200170 let ind = indent(lastclause) + shiftwidth()
Bram Moolenaarc81e5e72007-05-05 18:24:42 +0000171 endif
172 elseif line =~? '^WHEN\>'
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200173 let ind = ind + shiftwidth()
Bram Moolenaarc81e5e72007-05-05 18:24:42 +0000174 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
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200181 let ind = ind - shiftwidth()
Bram Moolenaarc81e5e72007-05-05 18:24:42 +0000182 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\)\)\>'
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200222 let ind = ind - shiftwidth()
Bram Moolenaarc81e5e72007-05-05 18:24:42 +0000223 endif
224 endif
225 return ind < bshft ? bshft : ind
226endfunction