vhdl.vim 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. " VHDL indent ('93 syntax)
  2. " Language: VHDL
  3. " Maintainer: Gerald Lai <laigera+vim?gmail.com>
  4. " Version: 1.62
  5. " Last Change: 2017 Oct 17
  6. " 2023 Aug 28 by Vim Project (undo_indent)
  7. " URL: http://www.vim.org/scripts/script.php?script_id=1450
  8. " only load this indent file when no other was loaded
  9. if exists("b:did_indent")
  10. finish
  11. endif
  12. let b:did_indent = 1
  13. " setup indent options for local VHDL buffer
  14. setlocal indentexpr=GetVHDLindent()
  15. setlocal indentkeys=!^F,o,O,0(,0)
  16. setlocal indentkeys+==~begin,=~end\ ,=~end\ ,=~is,=~select,=~when
  17. setlocal indentkeys+==~if,=~then,=~elsif,=~else
  18. setlocal indentkeys+==~case,=~loop,=~for,=~generate,=~record,=~units,=~process,=~block,=~function,=~component,=~procedure
  19. setlocal indentkeys+==~architecture,=~configuration,=~entity,=~package
  20. let b:undo_indent = "setlocal indentexpr< indentkeys<"
  21. " constants
  22. " not a comment
  23. let s:NC = '\%(--.*\)\@<!'
  24. " end of string
  25. let s:ES = '\s*\%(--.*\)\=$'
  26. " no "end" keyword in front
  27. let s:NE = '\%(\<end\s\+\)\@<!'
  28. " option to disable alignment of generic/port mappings
  29. if !exists("g:vhdl_indent_genportmap")
  30. let g:vhdl_indent_genportmap = 1
  31. endif
  32. " option to disable alignment of right-hand side assignment "<=" statements
  33. if !exists("g:vhdl_indent_rhsassign")
  34. let g:vhdl_indent_rhsassign = 1
  35. endif
  36. " only define indent function once
  37. if exists("*GetVHDLindent")
  38. finish
  39. endif
  40. function GetVHDLindent()
  41. " store current line & string
  42. let curn = v:lnum
  43. let curs = getline(curn)
  44. " find previous line that is not a comment
  45. let prevn = prevnonblank(curn - 1)
  46. let prevs = getline(prevn)
  47. while prevn > 0 && prevs =~ '^\s*--'
  48. let prevn = prevnonblank(prevn - 1)
  49. let prevs = getline(prevn)
  50. endwhile
  51. let prevs_noi = substitute(prevs, '^\s*', '', '')
  52. " default indent starts as previous non-comment line's indent
  53. let ind = prevn > 0 ? indent(prevn) : 0
  54. " backup default
  55. let ind2 = ind
  56. " indent: special; kill string so it would not affect other filters
  57. " keywords: "report" + string
  58. " where: anywhere in current or previous line
  59. let s0 = s:NC.'\<report\>\s*".*"'
  60. if curs =~? s0
  61. let curs = ""
  62. endif
  63. if prevs =~? s0
  64. let prevs = ""
  65. endif
  66. " indent: previous line's comment position, otherwise follow next non-comment line if possible
  67. " keyword: "--"
  68. " where: start of current line
  69. if curs =~ '^\s*--'
  70. let pn = curn - 1
  71. let ps = getline(pn)
  72. if curs =~ '^\s*--\s' && ps =~ '--'
  73. return indent(pn) + stridx(substitute(ps, '^\s*', '', ''), '--')
  74. else
  75. " find nextnonblank line that is not a comment
  76. let nn = nextnonblank(curn + 1)
  77. let ns = getline(nn)
  78. while nn > 0 && ns =~ '^\s*--'
  79. let nn = nextnonblank(nn + 1)
  80. let ns = getline(nn)
  81. endwhile
  82. let n = indent(nn)
  83. return n != -1 ? n : ind
  84. endif
  85. endif
  86. " ****************************************************************************************
  87. " indent: align generic variables & port names
  88. " keywords: "procedure" + name, "generic", "map", "port" + "(", provided current line is part of mapping
  89. " where: anywhere in previous 2 lines
  90. " find following previous non-comment line
  91. let pn = prevnonblank(prevn - 1)
  92. let ps = getline(pn)
  93. while pn > 0 && ps =~ '^\s*--'
  94. let pn = prevnonblank(pn - 1)
  95. let ps = getline(pn)
  96. endwhile
  97. if (curs =~ '^\s*)' || curs =~? '^\s*\%(\<\%(procedure\|generic\|map\|port\)\>.*\)\@<!\w\+\s*\w*\s*\((.*)\)*\s*\%(=>\s*\S\+\|:[^=]\@=\s*\%(\%(in\|out\|inout\|buffer\|linkage\)\>\|\s\+\)\)') && (prevs =~? s:NC.'\<\%(procedure\s\+\S\+\|generic\|map\|port\)\s*(\%(\s*\w\)\=' || (ps =~? s:NC.'\<\%(procedure\|generic\|map\|port\)'.s:ES && prevs =~ '^\s*('))
  98. " align closing ")" with opening "("
  99. if curs =~ '^\s*)'
  100. return ind2 + stridx(prevs_noi, '(')
  101. endif
  102. let m = matchend(prevs_noi, '(\s*\ze\w')
  103. if m != -1
  104. return ind2 + m
  105. else
  106. if g:vhdl_indent_genportmap
  107. return ind2 + stridx(prevs_noi, '(') + shiftwidth()
  108. else
  109. return ind2 + shiftwidth()
  110. endif
  111. endif
  112. endif
  113. " indent: align conditional/select statement
  114. " keywords: variable + "<=" without ";" ending
  115. " where: start of previous line
  116. if prevs =~? '^\s*\S\+\s*<=[^;]*'.s:ES
  117. if g:vhdl_indent_rhsassign
  118. return ind2 + matchend(prevs_noi, '<=\s*\ze.')
  119. else
  120. return ind2 + shiftwidth()
  121. endif
  122. endif
  123. " indent: backtrace previous non-comment lines for next smaller or equal size indent
  124. " keywords: "end" + "record", "units"
  125. " where: start of previous line
  126. " keyword: ")"
  127. " where: start of previous line
  128. " keyword: without "<=" + ";" ending
  129. " where: anywhere in previous line
  130. " keyword: "=>" + ")" ending, provided current line does not begin with ")"
  131. " where: anywhere in previous line
  132. " _note_: indent allowed to leave this filter
  133. let m = 0
  134. if prevs =~? '^\s*end\s\+\%(record\|units\)\>'
  135. let m = 3
  136. elseif prevs =~ '^\s*)'
  137. let m = 1
  138. elseif prevs =~ s:NC.'\%(<=.*\)\@<!;'.s:ES || (curs !~ '^\s*)' && prevs =~ s:NC.'=>.*'.s:NC.')'.s:ES)
  139. let m = 2
  140. endif
  141. if m > 0
  142. let pn = prevnonblank(prevn - 1)
  143. let ps = getline(pn)
  144. while pn > 0
  145. let t = indent(pn)
  146. if ps !~ '^\s*--' && (t < ind || (t == ind && m == 3))
  147. " make sure one of these is true
  148. " keywords: variable + "<=" without ";" ending
  149. " where: start of previous non-comment line
  150. " keywords: "procedure", "generic", "map", "port"
  151. " where: anywhere in previous non-comment line
  152. " keyword: "("
  153. " where: start of previous non-comment line
  154. if m < 3 && ps !~? '^\s*\S\+\s*<=[^;]*'.s:ES
  155. if ps =~? s:NC.'\<\%(procedure\|generic\|map\|port\)\>' || ps =~ '^\s*('
  156. let ind = t
  157. endif
  158. break
  159. endif
  160. let ind = t
  161. if m > 1
  162. " find following previous non-comment line
  163. let ppn = prevnonblank(pn - 1)
  164. let pps = getline(ppn)
  165. while ppn > 0 && pps =~ '^\s*--'
  166. let ppn = prevnonblank(ppn - 1)
  167. let pps = getline(ppn)
  168. endwhile
  169. " indent: follow
  170. " keyword: "select"
  171. " where: end of following previous non-comment line
  172. " keyword: "type"
  173. " where: start of following previous non-comment line
  174. if m == 2
  175. let s1 = s:NC.'\<select'.s:ES
  176. if ps !~? s1 && pps =~? s1
  177. let ind = indent(ppn)
  178. endif
  179. elseif m == 3
  180. let s1 = '^\s*type\>'
  181. if ps !~? s1 && pps =~? s1
  182. let ind = indent(ppn)
  183. endif
  184. endif
  185. endif
  186. break
  187. endif
  188. let pn = prevnonblank(pn - 1)
  189. let ps = getline(pn)
  190. endwhile
  191. endif
  192. " indent: follow indent of previous opening statement, otherwise -sw
  193. " keyword: "begin"
  194. " where: anywhere in current line
  195. if curs =~? s:NC.'\<begin\>'
  196. " find previous opening statement of
  197. " keywords: "architecture", "block", "entity", "function", "generate", "procedure", "process"
  198. let s2 = s:NC.s:NE.'\<\%(architecture\|block\|entity\|function\|generate\|procedure\|process\)\>'
  199. let pn = prevnonblank(curn - 1)
  200. let ps = getline(pn)
  201. while pn > 0 && (ps =~ '^\s*--' || ps !~? s2)
  202. let pn = prevnonblank(pn - 1)
  203. let ps = getline(pn)
  204. if (ps =~? s:NC.'\<begin\>')
  205. return indent(pn) - shiftwidth()
  206. endif
  207. endwhile
  208. if (pn == 0)
  209. return ind - shiftwidth()
  210. else
  211. return indent(pn)
  212. endif
  213. endif
  214. " indent: +sw if previous line is previous opening statement
  215. " keywords: "record", "units"
  216. " where: anywhere in current line
  217. if curs =~? s:NC.s:NE.'\<\%(record\|units\)\>'
  218. " find previous opening statement of
  219. " keyword: "type"
  220. let s3 = s:NC.s:NE.'\<type\>'
  221. if curs !~? s3.'.*'.s:NC.'\<\%(record\|units\)\>.*'.s:ES && prevs =~? s3
  222. let ind = ind + shiftwidth()
  223. endif
  224. return ind
  225. endif
  226. " ****************************************************************************************
  227. " indent: 0
  228. " keywords: "architecture", "configuration", "entity", "library", "package"
  229. " where: start of current line
  230. if curs =~? '^\s*\%(architecture\|configuration\|entity\|library\|package\)\>'
  231. return 0
  232. endif
  233. " indent: maintain indent of previous opening statement
  234. " keyword: "is"
  235. " where: start of current line
  236. " find previous opening statement of
  237. " keywords: "architecture", "block", "configuration", "entity", "function", "package", "procedure", "process", "type"
  238. if curs =~? '^\s*\<is\>' && prevs =~? s:NC.s:NE.'\<\%(architecture\|block\|configuration\|entity\|function\|package\|procedure\|process\|type\)\>'
  239. return ind2
  240. endif
  241. " indent: maintain indent of previous opening statement
  242. " keyword: "then"
  243. " where: start of current line
  244. " find previous opening statement of
  245. " keywords: "elsif", "if"
  246. if curs =~? '^\s*\<then\>' && prevs =~? s:NC.'\%(\<elsif\>\|'.s:NE.'\<if\>\)'
  247. return ind2
  248. endif
  249. " indent: maintain indent of previous opening statement
  250. " keyword: "generate"
  251. " where: start of current line
  252. " find previous opening statement of
  253. " keywords: "for", "if"
  254. if curs =~? '^\s*\<generate\>' && prevs =~? s:NC.s:NE.'\%(\%(\<wait\s\+\)\@<!\<for\|\<if\)\>'
  255. return ind2
  256. endif
  257. " indent: +sw
  258. " keywords: "block", "process"
  259. " removed: "begin", "case", "elsif", "if", "loop", "record", "units", "while"
  260. " where: anywhere in previous line
  261. if prevs =~? s:NC.s:NE.'\<\%(block\|process\)\>'
  262. return ind + shiftwidth()
  263. endif
  264. " indent: +sw
  265. " keywords: "architecture", "configuration", "entity", "package"
  266. " removed: "component", "for", "when", "with"
  267. " where: start of previous line
  268. if prevs =~? '^\s*\%(architecture\|configuration\|entity\|package\)\>'
  269. return ind + shiftwidth()
  270. endif
  271. " indent: +sw
  272. " keyword: "select"
  273. " removed: "generate", "is", "=>"
  274. " where: end of previous line
  275. if prevs =~? s:NC.'\<select'.s:ES
  276. return ind + shiftwidth()
  277. endif
  278. " indent: +sw
  279. " keyword: "begin", "loop", "record", "units"
  280. " where: anywhere in previous line
  281. " keyword: "component", "else", "for"
  282. " where: start of previous line
  283. " keyword: "generate", "is", "then", "=>"
  284. " where: end of previous line
  285. " _note_: indent allowed to leave this filter
  286. if prevs =~? s:NC.'\%(\<begin\>\|'.s:NE.'\<\%(loop\|record\|units\)\>\)' || prevs =~? '^\s*\%(component\|else\|for\)\>' || prevs =~? s:NC.'\%('.s:NE.'\<generate\|\<\%(is\|then\)\|=>\)'.s:ES
  287. let ind = ind + shiftwidth()
  288. endif
  289. " ****************************************************************************************
  290. " indent: -sw
  291. " keywords: "when", provided previous line does not begin with "when", does not end with "is"
  292. " where: start of current line
  293. let s4 = '^\s*when\>'
  294. if curs =~? s4
  295. if prevs =~? s:NC.'\<is'.s:ES
  296. return ind
  297. elseif prevs !~? s4
  298. return ind - shiftwidth()
  299. else
  300. return ind2
  301. endif
  302. endif
  303. " indent: -sw
  304. " keywords: "else", "elsif", "end" + "block", "for", "function", "generate", "if", "loop", "procedure", "process", "record", "units"
  305. " where: start of current line
  306. let s5 = 'block\|for\|function\|generate\|if\|loop\|procedure\|process\|record\|units'
  307. if curs =~? '^\s*\%(else\|elsif\|end\s\+\%('.s5.'\)\)\>'
  308. if prevs =~? '^\s*\%(elsif\|'.s5.'\)'
  309. return ind
  310. else
  311. return ind - shiftwidth()
  312. endif
  313. endif
  314. " indent: backtrace previous non-comment lines
  315. " keyword: "end" + "case", "component"
  316. " where: start of current line
  317. let m = 0
  318. if curs =~? '^\s*end\s\+case\>'
  319. let m = 1
  320. elseif curs =~? '^\s*end\s\+component\>'
  321. let m = 2
  322. endif
  323. if m > 0
  324. " find following previous non-comment line
  325. let pn = prevn
  326. let ps = getline(pn)
  327. while pn > 0
  328. if ps !~ '^\s*--'
  329. "indent: -2sw
  330. "keywords: "end" + "case"
  331. "where: start of previous non-comment line
  332. "indent: -sw
  333. "keywords: "when"
  334. "where: start of previous non-comment line
  335. "indent: follow
  336. "keywords: "case"
  337. "where: start of previous non-comment line
  338. if m == 1
  339. if ps =~? '^\s*end\s\+case\>'
  340. return indent(pn) - 2 * shiftwidth()
  341. elseif ps =~? '^\s*when\>'
  342. return indent(pn) - shiftwidth()
  343. elseif ps =~? '^\s*case\>'
  344. return indent(pn)
  345. endif
  346. "indent: follow
  347. "keyword: "component"
  348. "where: start of previous non-comment line
  349. elseif m == 2
  350. if ps =~? '^\s*component\>'
  351. return indent(pn)
  352. endif
  353. endif
  354. endif
  355. let pn = prevnonblank(pn - 1)
  356. let ps = getline(pn)
  357. endwhile
  358. return ind - shiftwidth()
  359. endif
  360. " indent: -sw
  361. " keyword: ")"
  362. " where: start of current line
  363. if curs =~ '^\s*)'
  364. return ind - shiftwidth()
  365. endif
  366. " indent: 0
  367. " keywords: "end" + "architecture", "configuration", "entity", "package"
  368. " where: start of current line
  369. if curs =~? '^\s*end\s\+\%(architecture\|configuration\|entity\|package\)\>'
  370. return 0
  371. endif
  372. " indent: -sw
  373. " keywords: "end" + identifier, ";"
  374. " where: start of current line
  375. "if curs =~? '^\s*end\s\+\w\+\>'
  376. if curs =~? '^\s*end\%(\s\|;'.s:ES.'\)'
  377. return ind - shiftwidth()
  378. endif
  379. " ****************************************************************************************
  380. " indent: maintain indent of previous opening statement
  381. " keywords: without "procedure", "generic", "map", "port" + ":" but not ":=" + "in", "out", "inout", "buffer", "linkage", variable & ":="
  382. " where: start of current line
  383. if curs =~? '^\s*\%(\<\%(procedure\|generic\|map\|port\)\>.*\)\@<!\w\+\s*\w*\s*:[^=]\@=\s*\%(\%(in\|out\|inout\|buffer\|linkage\)\>\|\w\+\s\+:=\)'
  384. return ind2
  385. endif
  386. " ****************************************************************************************
  387. " indent: maintain indent of previous opening statement, corner case which
  388. " does not end in ;, but is part of a mapping
  389. " keywords: without "procedure", "generic", "map", "port" + ":" but not ":=", never + ;$ and
  390. " prevline without "procedure", "generic", "map", "port" + ":" but not ":=" + eventually ;$
  391. " where: start of current line
  392. if curs =~? '^\s*\%(\<\%(procedure\|generic\|map\|port\)\>.*\)\@<!\w\+\s*\w*\s*:[^=].*[^;].*$'
  393. if prevs =~? '^\s*\%(\<\%(procedure\|generic\|map\|port\)\>.*\)\@<!\w\+\s*\w*\s*:[^=].*;.*$'
  394. return ind2
  395. endif
  396. endif
  397. " return leftover filtered indent
  398. return ind
  399. endfunction