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