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