hare.vim 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. " Vim indent file
  2. " Language: Hare
  3. " Maintainer: Amelia Clarke <selene@perilune.dev>
  4. " Last Change: 2024-04-14
  5. " Upstream: https://git.sr.ht/~sircmpwn/hare.vim
  6. if exists('b:did_indent')
  7. finish
  8. endif
  9. let b:did_indent = 1
  10. let s:cpo_save = &cpo
  11. set cpo&vim
  12. " L0 -> don't deindent labels
  13. " (s -> use one indent after a trailing (
  14. " m1 -> if ) starts a line, indent it the same as its matching (
  15. " ks -> add an extra indent to extra lines in an if expression or for expression
  16. " j1 -> indent code inside {} one level when in parentheses
  17. " J1 -> see j1
  18. " *0 -> don't search for unclosed block comments
  19. " #1 -> don't deindent lines that begin with #
  20. setlocal cinoptions=L0,(s,m1,ks,j1,J1,*0,#1
  21. " Controls which keys reindent the current line.
  22. " 0{ -> { at beginning of line
  23. " 0} -> } at beginning of line
  24. " 0) -> ) at beginning of line
  25. " 0] -> ] at beginning of line
  26. " !^F -> <C-f> (not inserted)
  27. " o -> <CR> or `o` command
  28. " O -> `O` command
  29. " e -> else
  30. " 0=case -> case
  31. setlocal indentkeys=0{,0},0),0],!^F,o,O,e,0=case
  32. setlocal cinwords=if,else,for,switch,match
  33. setlocal indentexpr=GetHareIndent()
  34. let b:undo_indent = 'setl cino< cinw< inde< indk<'
  35. if exists('*GetHareIndent()')
  36. finish
  37. endif
  38. function! FloorCindent(lnum)
  39. return cindent(a:lnum) / shiftwidth() * shiftwidth()
  40. endfunction
  41. function! GetHareIndent()
  42. let line = getline(v:lnum)
  43. let prevlnum = prevnonblank(v:lnum - 1)
  44. let prevline = getline(prevlnum)
  45. let prevprevline = getline(prevnonblank(prevlnum - 1))
  46. " This is all very hacky and imperfect, but it's tough to do much better when
  47. " working with regex-based indenting rules.
  48. " If the previous line ended with =, indent by one shiftwidth.
  49. if prevline =~# '\v\=\s*(//.*)?$'
  50. return indent(prevlnum) + shiftwidth()
  51. endif
  52. " If the previous line ended in a semicolon and the line before that ended
  53. " with =, deindent by one shiftwidth.
  54. if prevline =~# '\v;\s*(//.*)?$' && prevprevline =~# '\v\=\s*(//.*)?$'
  55. return indent(prevlnum) - shiftwidth()
  56. endif
  57. " TODO: The following edge-case is still indented incorrectly:
  58. " case =>
  59. " if (foo) {
  60. " bar;
  61. " };
  62. " | // cursor is incorrectly deindented by one shiftwidth.
  63. "
  64. " This only happens if the {} block is the first statement in the case body.
  65. " If `case` is typed, the case will also be incorrectly deindented by one
  66. " shiftwidth. Are you having fun yet?
  67. " Deindent cases.
  68. if line =~# '\v^\s*case'
  69. " If the previous line was also a case, don't do any special indenting.
  70. if prevline =~# '\v^\s*case'
  71. return indent(prevlnum)
  72. end
  73. " If the previous line was a multiline case, deindent by one shiftwidth.
  74. if prevline =~# '\v\=\>\s*(//.*)?$'
  75. return indent(prevlnum) - shiftwidth()
  76. endif
  77. " If the previous line started a block, deindent by one shiftwidth.
  78. " This handles the first case in a switch/match block.
  79. if prevline =~# '\v\{\s*(//.*)?$'
  80. return FloorCindent(v:lnum) - shiftwidth()
  81. end
  82. " If the previous line ended in a semicolon and the line before that wasn't
  83. " a case, deindent by one shiftwidth.
  84. if prevline =~# '\v;\s*(//.*)?$' && prevprevline !~# '\v\=\>\s*(//.*)?$'
  85. return FloorCindent(v:lnum) - shiftwidth()
  86. end
  87. let l:indent = FloorCindent(v:lnum)
  88. " If a normal cindent would indent the same amount as the previous line,
  89. " deindent by one shiftwidth. This fixes some issues with `case let` blocks.
  90. if l:indent == indent(prevlnum)
  91. return l:indent - shiftwidth()
  92. endif
  93. " Otherwise, do a normal cindent.
  94. return l:indent
  95. endif
  96. " Don't indent an extra shiftwidth for cases which span multiple lines.
  97. if prevline =~# '\v\=\>\s*(//.*)?$' && prevline !~# '\v^\s*case\W'
  98. return indent(prevlnum)
  99. endif
  100. " Indent the body of a case.
  101. " If the previous line ended in a semicolon and the line before that was a
  102. " case, don't do any special indenting.
  103. if prevline =~# '\v;\s*(//.*)?$' && prevprevline =~# '\v\=\>\s*(//.*)?$'
  104. \ && line !~# '\v^\s*}'
  105. return indent(prevlnum)
  106. endif
  107. let l:indent = FloorCindent(v:lnum)
  108. " If the previous line was a case and a normal cindent wouldn't indent, indent
  109. " an extra shiftwidth.
  110. if prevline =~# '\v\=\>\s*(//.*)?$' && l:indent == indent(prevlnum)
  111. return l:indent + shiftwidth()
  112. endif
  113. " If everything above is false, do a normal cindent.
  114. return l:indent
  115. endfunction
  116. let &cpo = s:cpo_save
  117. unlet s:cpo_save
  118. " vim: et sw=2 sts=2 ts=8