Christian Brabandt | 6efb198 | 2023-08-10 05:44:25 +0200 | [diff] [blame] | 1 | " 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 | |
| 18 | if exists("g:rapidNoSpaceIndent") |
| 19 | if !exists("g:rapidSpaceIndent") |
| 20 | let g:rapidSpaceIndent = !g:rapidNoSpaceIndent |
| 21 | endif |
| 22 | unlet g:rapidNoSpaceIndent |
| 23 | endif |
| 24 | |
| 25 | " Only load this indent file when no other was loaded. |
| 26 | if exists("b:did_indent") || get(g:,'rapidNoIndent',0) |
| 27 | finish |
| 28 | endif |
| 29 | let b:did_indent = 1 |
| 30 | |
| 31 | setlocal nolisp |
| 32 | setlocal nosmartindent |
| 33 | setlocal autoindent |
| 34 | setlocal indentexpr=GetRapidIndent() |
| 35 | if 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,:,<[>,<]>,<(>,<)> |
| 37 | else |
| 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,: |
| 39 | endif |
| 40 | let b:undo_indent="setlocal lisp< si< ai< inde< indk<" |
| 41 | |
| 42 | if 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<" |
| 50 | endif |
| 51 | |
| 52 | " Only define the function once. |
| 53 | if exists("*GetRapidIndent") |
| 54 | finish |
| 55 | endif |
| 56 | |
| 57 | let s:keepcpo= &cpo |
| 58 | set cpo&vim |
| 59 | |
| 60 | function 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 |
| 68 | endfunction |
| 69 | |
| 70 | function 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épe | dbf749b | 2023-10-16 09:53:37 +0200 | [diff] [blame] | 77 | " This may be useful if code is commented out at the first column. |
Christian Brabandt | 6efb198 | 2023-08-10 05:44:25 +0200 | [diff] [blame] | 78 | 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 |
| 145 | endfunction |
| 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épe | 3fc7a7e | 2023-08-23 21:20:00 +0200 | [diff] [blame] | 149 | " If a:str is a word also add word boundaries and case insensitivity. |
Christian Brabandt | 6efb198 | 2023-08-10 05:44:25 +0200 | [diff] [blame] | 150 | " Note: rapidTodoComment and rapidDebugComment are not taken into account. |
| 151 | function 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 |
| 180 | endfunction |
| 181 | |
Viktor Szépe | 3fc7a7e | 2023-08-23 21:20:00 +0200 | [diff] [blame] | 182 | " a:lchar should be one of (, ), [, ], { or } |
| 183 | " returns the number of opening/closing parentheses which have no |
Christian Brabandt | 6efb198 | 2023-08-10 05:44:25 +0200 | [diff] [blame] | 184 | " closing/opening match in getline(a:lnum) |
| 185 | function 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épe | 3fc7a7e | 2023-08-23 21:20:00 +0200 | [diff] [blame] | 208 | " count opening brackets |
Christian Brabandt | 6efb198 | 2023-08-10 05:44:25 +0200 | [diff] [blame] | 209 | 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épe | 3fc7a7e | 2023-08-23 21:20:00 +0200 | [diff] [blame] | 219 | " count closing brackets |
Christian Brabandt | 6efb198 | 2023-08-10 05:44:25 +0200 | [diff] [blame] | 220 | 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 |
| 236 | endfunction |
| 237 | |
| 238 | " This function works almost like prevnonblank() but handles %%%-headers and |
| 239 | " comments like blank lines |
| 240 | function s:RapidPreNoneBlank(lnum) abort |
| 241 | |
| 242 | let nPreNoneBlank = prevnonblank(a:lnum) |
| 243 | |
| 244 | while nPreNoneBlank>0 && getline(nPreNoneBlank) =~ '\v\c^\s*(\%\%\%|!)' |
Viktor Szépe | 3fc7a7e | 2023-08-23 21:20:00 +0200 | [diff] [blame] | 245 | " Previous none blank line irrelevant. Look further aback. |
Christian Brabandt | 6efb198 | 2023-08-10 05:44:25 +0200 | [diff] [blame] | 246 | let nPreNoneBlank = prevnonblank(nPreNoneBlank - 1) |
| 247 | endwhile |
| 248 | |
| 249 | return nPreNoneBlank |
| 250 | endfunction |
| 251 | |
| 252 | let &cpo = s:keepcpo |
| 253 | unlet s:keepcpo |
| 254 | |
| 255 | " vim:sw=2 sts=2 et |