hare.vim

[vim] Hare vim plugin
git clone https://git.torresjrjr.com/hare.vim.git
Log | Files | Refs | README | LICENSE

hare.vim (4372B)


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