blob: b1fa00b8cc39069767284e6eaab3c1e09a32fd2e [file] [log] [blame]
Christian Brabandt6efb1982023-08-10 05:44:25 +02001" ABB Rapid Command indent file for Vim
2" Language: ABB Rapid Command
3" Maintainer: Patrick Meiser-Knosowski <knosowski@graeffrobotics.de>
4" Version: 2.2.7
5" Last Change: 12. May 2023
6" Credits: Based on indent/vim.vim
7"
8" Suggestions of improvement are very welcome. Please email me!
9"
10" Known bugs: ../doc/rapid.txt
11"
12" TODO
13" * indent wrapped lines which do not end with an ; or special key word,
14" maybe this is a better idea, but then () and [] has to be changed as
15" well
16"
17
18if exists("g:rapidNoSpaceIndent")
19 if !exists("g:rapidSpaceIndent")
20 let g:rapidSpaceIndent = !g:rapidNoSpaceIndent
21 endif
22 unlet g:rapidNoSpaceIndent
23endif
24
25" Only load this indent file when no other was loaded.
26if exists("b:did_indent") || get(g:,'rapidNoIndent',0)
27 finish
28endif
29let b:did_indent = 1
30
31setlocal nolisp
32setlocal nosmartindent
33setlocal autoindent
34setlocal indentexpr=GetRapidIndent()
35if get(g:,'rapidNewStyleIndent',0)
36 setlocal indentkeys=!^F,o,O,0=~endmodule,0=~error,0=~undo,0=~backward,0=~endproc,0=~endrecord,0=~endtrap,0=~endfunc,0=~else,0=~endif,0=~endtest,0=~endfor,0=~endwhile,:,<[>,<]>,<(>,<)>
37else
38 setlocal indentkeys=!^F,o,O,0=~endmodule,0=~error,0=~undo,0=~backward,0=~endproc,0=~endrecord,0=~endtrap,0=~endfunc,0=~else,0=~endif,0=~endtest,0=~endfor,0=~endwhile,:
39endif
40let b:undo_indent="setlocal lisp< si< ai< inde< indk<"
41
42if get(g:,'rapidSpaceIndent',1)
43 " Use spaces for indention, 2 is enough.
44 " More or even tabs wastes space on the teach pendant.
45 setlocal softtabstop=2
46 setlocal shiftwidth=2
47 setlocal expandtab
48 setlocal shiftround
49 let b:undo_indent = b:undo_indent." sts< sw< et< sr<"
50endif
51
52" Only define the function once.
53if exists("*GetRapidIndent")
54 finish
55endif
56
57let s:keepcpo= &cpo
58set cpo&vim
59
60function GetRapidIndent()
61 let ignorecase_save = &ignorecase
62 try
63 let &ignorecase = 0
64 return s:GetRapidIndentIntern()
65 finally
66 let &ignorecase = ignorecase_save
67 endtry
68endfunction
69
70function s:GetRapidIndentIntern() abort
71
72 let l:currentLineNum = v:lnum
73 let l:currentLine = getline(l:currentLineNum)
74
75 if l:currentLine =~ '^!' && !get(g:,'rapidCommentIndent',0)
76 " If current line is ! line comment, do not change indent
Viktor Szépedbf749b2023-10-16 09:53:37 +020077 " This may be useful if code is commented out at the first column.
Christian Brabandt6efb1982023-08-10 05:44:25 +020078 return 0
79 endif
80
81 " Find a non-blank line above the current line.
82 let l:preNoneBlankLineNum = s:RapidPreNoneBlank(v:lnum - 1)
83 if l:preNoneBlankLineNum == 0
84 " At the start of the file use zero indent.
85 return 0
86 endif
87
88 let l:preNoneBlankLine = getline(l:preNoneBlankLineNum)
89 let l:ind = indent(l:preNoneBlankLineNum)
90
91 " Define add a 'shiftwidth' pattern
92 let l:addShiftwidthPattern = '\c\v^\s*('
93 let l:addShiftwidthPattern .= '((local|task)\s+)?(module|record|proc|func|trap)\s+\k'
94 let l:addShiftwidthPattern .= '|(backward|error|undo)>'
95 let l:addShiftwidthPattern .= ')'
96 "
97 " Define Subtract 'shiftwidth' pattern
98 let l:subtractShiftwidthPattern = '\c\v^\s*('
99 let l:subtractShiftwidthPattern .= 'end(module|record|proc|func|trap)>'
100 let l:subtractShiftwidthPattern .= '|(backward|error|undo)>'
101 let l:subtractShiftwidthPattern .= ')'
102
103 " Add shiftwidth
104 if l:preNoneBlankLine =~ l:addShiftwidthPattern
105 \|| s:RapidLenTilStr(l:preNoneBlankLineNum, "then", 0)>=0
106 \|| s:RapidLenTilStr(l:preNoneBlankLineNum, "else", 0)>=0
107 \|| s:RapidLenTilStr(l:preNoneBlankLineNum, "do", 0)>=0
108 \|| s:RapidLenTilStr(l:preNoneBlankLineNum, "case", 0)>=0
109 \|| s:RapidLenTilStr(l:preNoneBlankLineNum, "default", 0)>=0
110 let l:ind += &sw
111 endif
112
113 " Subtract shiftwidth
114 if l:currentLine =~ l:subtractShiftwidthPattern
115 \|| s:RapidLenTilStr(l:currentLineNum, "endif", 0)>=0
116 \|| s:RapidLenTilStr(l:currentLineNum, "endfor", 0)>=0
117 \|| s:RapidLenTilStr(l:currentLineNum, "endwhile", 0)>=0
118 \|| s:RapidLenTilStr(l:currentLineNum, "endtest", 0)>=0
119 \|| s:RapidLenTilStr(l:currentLineNum, "else", 0)>=0
120 \|| s:RapidLenTilStr(l:currentLineNum, "elseif", 0)>=0
121 \|| s:RapidLenTilStr(l:currentLineNum, "case", 0)>=0
122 \|| s:RapidLenTilStr(l:currentLineNum, "default", 0)>=0
123 let l:ind = l:ind - &sw
124 endif
125
126 " First case (or default) after a test gets the indent of the test.
127 if (s:RapidLenTilStr(l:currentLineNum, "case", 0)>=0 || s:RapidLenTilStr(l:currentLineNum, "default", 0)>=0) && s:RapidLenTilStr(l:preNoneBlankLineNum, "test", 0)>=0
128 let l:ind += &sw
129 endif
130
131 " continued lines with () or []
132 let l:OpenSum = s:RapidLoneParen(l:preNoneBlankLineNum,"(") + s:RapidLoneParen(l:preNoneBlankLineNum,"[")
133 if get(g:,'rapidNewStyleIndent',0)
134 let l:CloseSum = s:RapidLoneParen(l:preNoneBlankLineNum,")") + s:RapidLoneParen(l:currentLineNum,"]")
135 else
136 let l:CloseSum = s:RapidLoneParen(l:preNoneBlankLineNum,")") + s:RapidLoneParen(l:preNoneBlankLineNum,"]")
137 endif
138 if l:OpenSum > l:CloseSum
139 let l:ind += (l:OpenSum * 4 * &sw)
140 elseif l:OpenSum < l:CloseSum
141 let l:ind -= (l:CloseSum * 4 * &sw)
142 endif
143
144 return l:ind
145endfunction
146
147" Returns the length of the line until a:str occur outside a string or
148" comment. Search starts at string index a:startIdx.
Viktor Szépe3fc7a7e2023-08-23 21:20:00 +0200149" If a:str is a word also add word boundaries and case insensitivity.
Christian Brabandt6efb1982023-08-10 05:44:25 +0200150" Note: rapidTodoComment and rapidDebugComment are not taken into account.
151function s:RapidLenTilStr(lnum, str, startIdx) abort
152
153 let l:line = getline(a:lnum)
154 let l:len = strlen(l:line)
155 let l:idx = a:startIdx
156 let l:str = a:str
157 if l:str =~ '^\k\+$'
158 let l:str = '\c\<' . l:str . '\>'
159 endif
160
161 while l:len > l:idx
162 let l:idx = match(l:line, l:str, l:idx)
163 if l:idx < 0
164 " a:str not found
165 return -1
166 endif
167 let l:synName = synIDattr(synID(a:lnum,l:idx+1,0),"name")
168 if l:synName != "rapidString"
169 \&& l:synName != "rapidConcealableString"
170 \&& (l:synName != "rapidComment" || l:str =~ '^!')
171 " a:str found outside string or line comment
172 return l:idx
173 endif
174 " a:str is part of string or line comment
175 let l:idx += 1 " continue search for a:str
176 endwhile
177
178 " a:str not found or l:len <= a:startIdx
179 return -1
180endfunction
181
Viktor Szépe3fc7a7e2023-08-23 21:20:00 +0200182" a:lchar should be one of (, ), [, ], { or }
183" returns the number of opening/closing parentheses which have no
Christian Brabandt6efb1982023-08-10 05:44:25 +0200184" closing/opening match in getline(a:lnum)
185function s:RapidLoneParen(lnum,lchar) abort
186 if a:lchar == "(" || a:lchar == ")"
187 let l:opnParChar = "("
188 let l:clsParChar = ")"
189 elseif a:lchar == "[" || a:lchar == "]"
190 let l:opnParChar = "["
191 let l:clsParChar = "]"
192 elseif a:lchar == "{" || a:lchar == "}"
193 let l:opnParChar = "{"
194 let l:clsParChar = "}"
195 else
196 return 0
197 endif
198
199 let l:line = getline(a:lnum)
200
201 " look for the first ! which is not part of a string
202 let l:len = s:RapidLenTilStr(a:lnum,"!",0)
203 if l:len == 0
204 return 0 " first char is !; ignored
205 endif
206
207 let l:opnParen = 0
Viktor Szépe3fc7a7e2023-08-23 21:20:00 +0200208 " count opening brackets
Christian Brabandt6efb1982023-08-10 05:44:25 +0200209 let l:i = 0
210 while l:i >= 0
211 let l:i = s:RapidLenTilStr(a:lnum, l:opnParChar, l:i)
212 if l:i >= 0
213 let l:opnParen += 1
214 let l:i += 1
215 endif
216 endwhile
217
218 let l:clsParen = 0
Viktor Szépe3fc7a7e2023-08-23 21:20:00 +0200219 " count closing brackets
Christian Brabandt6efb1982023-08-10 05:44:25 +0200220 let l:i = 0
221 while l:i >= 0
222 let l:i = s:RapidLenTilStr(a:lnum, l:clsParChar, l:i)
223 if l:i >= 0
224 let l:clsParen += 1
225 let l:i += 1
226 endif
227 endwhile
228
229 if (a:lchar == "(" || a:lchar == "[" || a:lchar == "{") && l:opnParen>l:clsParen
230 return (l:opnParen-l:clsParen)
231 elseif (a:lchar == ")" || a:lchar == "]" || a:lchar == "}") && l:clsParen>l:opnParen
232 return (l:clsParen-l:opnParen)
233 endif
234
235 return 0
236endfunction
237
238" This function works almost like prevnonblank() but handles %%%-headers and
239" comments like blank lines
240function s:RapidPreNoneBlank(lnum) abort
241
242 let nPreNoneBlank = prevnonblank(a:lnum)
243
244 while nPreNoneBlank>0 && getline(nPreNoneBlank) =~ '\v\c^\s*(\%\%\%|!)'
Viktor Szépe3fc7a7e2023-08-23 21:20:00 +0200245 " Previous none blank line irrelevant. Look further aback.
Christian Brabandt6efb1982023-08-10 05:44:25 +0200246 let nPreNoneBlank = prevnonblank(nPreNoneBlank - 1)
247 endwhile
248
249 return nPreNoneBlank
250endfunction
251
252let &cpo = s:keepcpo
253unlet s:keepcpo
254
255" vim:sw=2 sts=2 et