| 
									
										
										
										
											2022-09-27 17:30:34 +01:00
										 |  |  | " Vim indent file | 
					
						
							|  |  |  | " Language: Hare | 
					
						
							|  |  |  | " Maintainer: Amelia Clarke <me@rsaihe.dev> | 
					
						
							|  |  |  | " Last Change: 2022 Sep 22 | 
					
						
							| 
									
										
										
										
											2023-08-29 05:32:59 +10:00
										 |  |  | "              2023 Aug 28 by Vim Project (undo_indent) | 
					
						
							| 
									
										
										
										
											2022-09-27 17:30:34 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | if exists("b:did_indent") | 
					
						
							|  |  |  |   finish | 
					
						
							|  |  |  | endif | 
					
						
							|  |  |  | let b:did_indent = 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if !has("cindent") || !has("eval") | 
					
						
							|  |  |  |   finish | 
					
						
							|  |  |  | endif | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | setlocal cindent | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | " L0 -> don't deindent labels | 
					
						
							|  |  |  | " (s -> use one indent after a trailing ( | 
					
						
							|  |  |  | " m1 -> if ) starts a line, indent it the same as its matching ( | 
					
						
							|  |  |  | " ks -> add an extra indent to extra lines in an if expression or for expression | 
					
						
							|  |  |  | " j1 -> indent code inside {} one level when in parentheses | 
					
						
							|  |  |  | " J1 -> see j1 | 
					
						
							|  |  |  | " *0 -> don't search for unclosed block comments | 
					
						
							|  |  |  | " #1 -> don't deindent lines that begin with # | 
					
						
							|  |  |  | setlocal cinoptions=L0,(s,m1,ks,j1,J1,*0,#1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | " Controls which keys reindent the current line. | 
					
						
							|  |  |  | " 0{     -> { at beginning of line | 
					
						
							|  |  |  | " 0}     -> } at beginning of line | 
					
						
							|  |  |  | " 0)     -> ) at beginning of line | 
					
						
							|  |  |  | " 0]     -> ] at beginning of line | 
					
						
							|  |  |  | " !^F    -> <C-f> (not inserted) | 
					
						
							|  |  |  | " o      -> <CR> or `o` command | 
					
						
							|  |  |  | " O      -> `O` command | 
					
						
							|  |  |  | " e      -> else | 
					
						
							|  |  |  | " 0=case -> case | 
					
						
							|  |  |  | setlocal indentkeys=0{,0},0),0],!^F,o,O,e,0=case | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | setlocal cinwords=if,else,for,switch,match | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | setlocal indentexpr=GetHareIndent() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-29 05:32:59 +10:00
										 |  |  | let b:undo_indent = "setl cin< cino< cinw< inde< indk<" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-27 17:30:34 +01:00
										 |  |  | function! FloorCindent(lnum) | 
					
						
							|  |  |  |   return cindent(a:lnum) / shiftwidth() * shiftwidth() | 
					
						
							|  |  |  | endfunction | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function! GetHareIndent() | 
					
						
							|  |  |  |   let line = getline(v:lnum) | 
					
						
							|  |  |  |   let prevlnum = prevnonblank(v:lnum - 1) | 
					
						
							|  |  |  |   let prevline = getline(prevlnum) | 
					
						
							|  |  |  |   let prevprevline = getline(prevnonblank(prevlnum - 1)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   " This is all very hacky and imperfect, but it's tough to do much better when | 
					
						
							|  |  |  |   " working with regex-based indenting rules. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   " If the previous line ended with =, indent by one shiftwidth. | 
					
						
							|  |  |  |   if prevline =~# '\v\=\s*(//.*)?$' | 
					
						
							|  |  |  |     return indent(prevlnum) + shiftwidth() | 
					
						
							|  |  |  |   endif | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   " If the previous line ended in a semicolon and the line before that ended | 
					
						
							|  |  |  |   " with =, deindent by one shiftwidth. | 
					
						
							|  |  |  |   if prevline =~# '\v;\s*(//.*)?$' && prevprevline =~# '\v\=\s*(//.*)?$' | 
					
						
							|  |  |  |     return indent(prevlnum) - shiftwidth() | 
					
						
							|  |  |  |   endif | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   " TODO: The following edge-case is still indented incorrectly: | 
					
						
							|  |  |  |   " case => | 
					
						
							|  |  |  |   "         if (foo) { | 
					
						
							|  |  |  |   "                 bar; | 
					
						
							|  |  |  |   "         }; | 
					
						
							|  |  |  |   " | // cursor is incorrectly deindented by one shiftwidth. | 
					
						
							|  |  |  |   " | 
					
						
							|  |  |  |   " This only happens if the {} block is the first statement in the case body. | 
					
						
							|  |  |  |   " If `case` is typed, the case will also be incorrectly deindented by one | 
					
						
							|  |  |  |   " shiftwidth. Are you having fun yet? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   " Deindent cases. | 
					
						
							|  |  |  |   if line =~# '\v^\s*case' | 
					
						
							|  |  |  |     " If the previous line was also a case, don't do any special indenting. | 
					
						
							|  |  |  |     if prevline =~# '\v^\s*case' | 
					
						
							|  |  |  |       return indent(prevlnum) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     " If the previous line was a multiline case, deindent by one shiftwidth. | 
					
						
							|  |  |  |     if prevline =~# '\v\=\>\s*(//.*)?$' | 
					
						
							|  |  |  |       return indent(prevlnum) - shiftwidth() | 
					
						
							|  |  |  |     endif | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     " If the previous line started a block, deindent by one shiftwidth. | 
					
						
							|  |  |  |     " This handles the first case in a switch/match block. | 
					
						
							|  |  |  |     if prevline =~# '\v\{\s*(//.*)?$' | 
					
						
							|  |  |  |       return FloorCindent(v:lnum) - shiftwidth() | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     " If the previous line ended in a semicolon and the line before that wasn't | 
					
						
							|  |  |  |     " a case, deindent by one shiftwidth. | 
					
						
							|  |  |  |     if prevline =~# '\v;\s*(//.*)?$' && prevprevline !~# '\v\=\>\s*(//.*)?$' | 
					
						
							|  |  |  |       return FloorCindent(v:lnum) - shiftwidth() | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     let l:indent = FloorCindent(v:lnum) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     " If a normal cindent would indent the same amount as the previous line, | 
					
						
							|  |  |  |     " deindent by one shiftwidth. This fixes some issues with `case let` blocks. | 
					
						
							|  |  |  |     if l:indent == indent(prevlnum) | 
					
						
							|  |  |  |       return l:indent - shiftwidth() | 
					
						
							|  |  |  |     endif | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     " Otherwise, do a normal cindent. | 
					
						
							|  |  |  |     return l:indent | 
					
						
							|  |  |  |   endif | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   " Don't indent an extra shiftwidth for cases which span multiple lines. | 
					
						
							|  |  |  |   if prevline =~# '\v\=\>\s*(//.*)?$' && prevline !~# '\v^\s*case\W' | 
					
						
							|  |  |  |     return indent(prevlnum) | 
					
						
							|  |  |  |   endif | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   " Indent the body of a case. | 
					
						
							|  |  |  |   " If the previous line ended in a semicolon and the line before that was a | 
					
						
							|  |  |  |   " case, don't do any special indenting. | 
					
						
							|  |  |  |   if prevline =~# '\v;\s*(//.*)?$' && prevprevline =~# '\v\=\>\s*(//.*)?$' && line !~# '\v^\s*}' | 
					
						
							|  |  |  |     return indent(prevlnum) | 
					
						
							|  |  |  |   endif | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   let l:indent = FloorCindent(v:lnum) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   " If the previous line was a case and a normal cindent wouldn't indent, indent | 
					
						
							|  |  |  |   " an extra shiftwidth. | 
					
						
							|  |  |  |   if prevline =~# '\v\=\>\s*(//.*)?$' && l:indent == indent(prevlnum) | 
					
						
							|  |  |  |     return l:indent + shiftwidth() | 
					
						
							|  |  |  |   endif | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   " If everything above is false, do a normal cindent. | 
					
						
							|  |  |  |   return l:indent | 
					
						
							|  |  |  | endfunction | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | " vim: tabstop=2 shiftwidth=2 expandtab |