rapid.vim 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  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. if exists("g:rapidNoSpaceIndent")
  18. if !exists("g:rapidSpaceIndent")
  19. let g:rapidSpaceIndent = !g:rapidNoSpaceIndent
  20. endif
  21. unlet g:rapidNoSpaceIndent
  22. endif
  23. " Only load this indent file when no other was loaded.
  24. if exists("b:did_indent") || get(g:,'rapidNoIndent',0)
  25. finish
  26. endif
  27. let b:did_indent = 1
  28. setlocal nolisp
  29. setlocal nosmartindent
  30. setlocal autoindent
  31. setlocal indentexpr=GetRapidIndent()
  32. if get(g:,'rapidNewStyleIndent',0)
  33. 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,:,<[>,<]>,<(>,<)>
  34. else
  35. 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,:
  36. endif
  37. let b:undo_indent="setlocal lisp< si< ai< inde< indk<"
  38. if get(g:,'rapidSpaceIndent',1)
  39. " Use spaces for indention, 2 is enough.
  40. " More or even tabs wastes space on the teach pendant.
  41. setlocal softtabstop=2
  42. setlocal shiftwidth=2
  43. setlocal expandtab
  44. setlocal shiftround
  45. let b:undo_indent = b:undo_indent." sts< sw< et< sr<"
  46. endif
  47. " Only define the function once.
  48. if exists("*GetRapidIndent")
  49. finish
  50. endif
  51. let s:keepcpo= &cpo
  52. set cpo&vim
  53. function GetRapidIndent()
  54. let ignorecase_save = &ignorecase
  55. try
  56. let &ignorecase = 0
  57. return s:GetRapidIndentIntern()
  58. finally
  59. let &ignorecase = ignorecase_save
  60. endtry
  61. endfunction
  62. function s:GetRapidIndentIntern() abort
  63. let l:currentLineNum = v:lnum
  64. let l:currentLine = getline(l:currentLineNum)
  65. if l:currentLine =~ '^!' && !get(g:,'rapidCommentIndent',0)
  66. " If current line is ! line comment, do not change indent
  67. " This may be useful if code is commented out at the first column.
  68. return 0
  69. endif
  70. " Find a non-blank line above the current line.
  71. let l:preNoneBlankLineNum = s:RapidPreNoneBlank(v:lnum - 1)
  72. if l:preNoneBlankLineNum == 0
  73. " At the start of the file use zero indent.
  74. return 0
  75. endif
  76. let l:preNoneBlankLine = getline(l:preNoneBlankLineNum)
  77. let l:ind = indent(l:preNoneBlankLineNum)
  78. " Define add a 'shiftwidth' pattern
  79. let l:addShiftwidthPattern = '\c\v^\s*('
  80. let l:addShiftwidthPattern .= '((local|task)\s+)?(module|record|proc|func|trap)\s+\k'
  81. let l:addShiftwidthPattern .= '|(backward|error|undo)>'
  82. let l:addShiftwidthPattern .= ')'
  83. "
  84. " Define Subtract 'shiftwidth' pattern
  85. let l:subtractShiftwidthPattern = '\c\v^\s*('
  86. let l:subtractShiftwidthPattern .= 'end(module|record|proc|func|trap)>'
  87. let l:subtractShiftwidthPattern .= '|(backward|error|undo)>'
  88. let l:subtractShiftwidthPattern .= ')'
  89. " Add shiftwidth
  90. if l:preNoneBlankLine =~ l:addShiftwidthPattern
  91. \|| s:RapidLenTilStr(l:preNoneBlankLineNum, "then", 0)>=0
  92. \|| s:RapidLenTilStr(l:preNoneBlankLineNum, "else", 0)>=0
  93. \|| s:RapidLenTilStr(l:preNoneBlankLineNum, "do", 0)>=0
  94. \|| s:RapidLenTilStr(l:preNoneBlankLineNum, "case", 0)>=0
  95. \|| s:RapidLenTilStr(l:preNoneBlankLineNum, "default", 0)>=0
  96. let l:ind += &sw
  97. endif
  98. " Subtract shiftwidth
  99. if l:currentLine =~ l:subtractShiftwidthPattern
  100. \|| s:RapidLenTilStr(l:currentLineNum, "endif", 0)>=0
  101. \|| s:RapidLenTilStr(l:currentLineNum, "endfor", 0)>=0
  102. \|| s:RapidLenTilStr(l:currentLineNum, "endwhile", 0)>=0
  103. \|| s:RapidLenTilStr(l:currentLineNum, "endtest", 0)>=0
  104. \|| s:RapidLenTilStr(l:currentLineNum, "else", 0)>=0
  105. \|| s:RapidLenTilStr(l:currentLineNum, "elseif", 0)>=0
  106. \|| s:RapidLenTilStr(l:currentLineNum, "case", 0)>=0
  107. \|| s:RapidLenTilStr(l:currentLineNum, "default", 0)>=0
  108. let l:ind = l:ind - &sw
  109. endif
  110. " First case (or default) after a test gets the indent of the test.
  111. if (s:RapidLenTilStr(l:currentLineNum, "case", 0)>=0 || s:RapidLenTilStr(l:currentLineNum, "default", 0)>=0) && s:RapidLenTilStr(l:preNoneBlankLineNum, "test", 0)>=0
  112. let l:ind += &sw
  113. endif
  114. " continued lines with () or []
  115. let l:OpenSum = s:RapidLoneParen(l:preNoneBlankLineNum,"(") + s:RapidLoneParen(l:preNoneBlankLineNum,"[")
  116. if get(g:,'rapidNewStyleIndent',0)
  117. let l:CloseSum = s:RapidLoneParen(l:preNoneBlankLineNum,")") + s:RapidLoneParen(l:currentLineNum,"]")
  118. else
  119. let l:CloseSum = s:RapidLoneParen(l:preNoneBlankLineNum,")") + s:RapidLoneParen(l:preNoneBlankLineNum,"]")
  120. endif
  121. if l:OpenSum > l:CloseSum
  122. let l:ind += (l:OpenSum * 4 * &sw)
  123. elseif l:OpenSum < l:CloseSum
  124. let l:ind -= (l:CloseSum * 4 * &sw)
  125. endif
  126. return l:ind
  127. endfunction
  128. " Returns the length of the line until a:str occur outside a string or
  129. " comment. Search starts at string index a:startIdx.
  130. " If a:str is a word also add word boundaries and case insensitivity.
  131. " Note: rapidTodoComment and rapidDebugComment are not taken into account.
  132. function s:RapidLenTilStr(lnum, str, startIdx) abort
  133. let l:line = getline(a:lnum)
  134. let l:len = strlen(l:line)
  135. let l:idx = a:startIdx
  136. let l:str = a:str
  137. if l:str =~ '^\k\+$'
  138. let l:str = '\c\<' . l:str . '\>'
  139. endif
  140. while l:len > l:idx
  141. let l:idx = match(l:line, l:str, l:idx)
  142. if l:idx < 0
  143. " a:str not found
  144. return -1
  145. endif
  146. let l:synName = synIDattr(synID(a:lnum,l:idx+1,0),"name")
  147. if l:synName != "rapidString"
  148. \&& l:synName != "rapidConcealableString"
  149. \&& (l:synName != "rapidComment" || l:str =~ '^!')
  150. " a:str found outside string or line comment
  151. return l:idx
  152. endif
  153. " a:str is part of string or line comment
  154. let l:idx += 1 " continue search for a:str
  155. endwhile
  156. " a:str not found or l:len <= a:startIdx
  157. return -1
  158. endfunction
  159. " a:lchar should be one of (, ), [, ], { or }
  160. " returns the number of opening/closing parentheses which have no
  161. " closing/opening match in getline(a:lnum)
  162. function s:RapidLoneParen(lnum,lchar) abort
  163. if a:lchar == "(" || a:lchar == ")"
  164. let l:opnParChar = "("
  165. let l:clsParChar = ")"
  166. elseif a:lchar == "[" || a:lchar == "]"
  167. let l:opnParChar = "["
  168. let l:clsParChar = "]"
  169. elseif a:lchar == "{" || a:lchar == "}"
  170. let l:opnParChar = "{"
  171. let l:clsParChar = "}"
  172. else
  173. return 0
  174. endif
  175. let l:line = getline(a:lnum)
  176. " look for the first ! which is not part of a string
  177. let l:len = s:RapidLenTilStr(a:lnum,"!",0)
  178. if l:len == 0
  179. return 0 " first char is !; ignored
  180. endif
  181. let l:opnParen = 0
  182. " count opening brackets
  183. let l:i = 0
  184. while l:i >= 0
  185. let l:i = s:RapidLenTilStr(a:lnum, l:opnParChar, l:i)
  186. if l:i >= 0
  187. let l:opnParen += 1
  188. let l:i += 1
  189. endif
  190. endwhile
  191. let l:clsParen = 0
  192. " count closing brackets
  193. let l:i = 0
  194. while l:i >= 0
  195. let l:i = s:RapidLenTilStr(a:lnum, l:clsParChar, l:i)
  196. if l:i >= 0
  197. let l:clsParen += 1
  198. let l:i += 1
  199. endif
  200. endwhile
  201. if (a:lchar == "(" || a:lchar == "[" || a:lchar == "{") && l:opnParen>l:clsParen
  202. return (l:opnParen-l:clsParen)
  203. elseif (a:lchar == ")" || a:lchar == "]" || a:lchar == "}") && l:clsParen>l:opnParen
  204. return (l:clsParen-l:opnParen)
  205. endif
  206. return 0
  207. endfunction
  208. " This function works almost like prevnonblank() but handles %%%-headers and
  209. " comments like blank lines
  210. function s:RapidPreNoneBlank(lnum) abort
  211. let nPreNoneBlank = prevnonblank(a:lnum)
  212. while nPreNoneBlank>0 && getline(nPreNoneBlank) =~ '\v\c^\s*(\%\%\%|!)'
  213. " Previous none blank line irrelevant. Look further aback.
  214. let nPreNoneBlank = prevnonblank(nPreNoneBlank - 1)
  215. endwhile
  216. return nPreNoneBlank
  217. endfunction
  218. let &cpo = s:keepcpo
  219. unlet s:keepcpo
  220. " vim:sw=2 sts=2 et