hare.vim 4.3 KB

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