hare.vim

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

hare.vim (4316B)


      1 " Vim indent file
      2 " Language: Hare
      3 
      4 if exists("b:did_indent")
      5   finish
      6 endif
      7 let b:did_indent = 1
      8 
      9 if !has("cindent") || !has("eval")
     10   finish
     11 endif
     12 
     13 setlocal cindent
     14 
     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 #
     23 setlocal 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
     35 setlocal indentkeys=0{,0},0),0],!^F,o,O,e,0=case
     36 
     37 setlocal cinwords=if,else,for,switch,match
     38 
     39 setlocal indentexpr=GetHareIndent()
     40 
     41 function! FloorCindent(lnum)
     42   return cindent(a:lnum) / shiftwidth() * shiftwidth()
     43 endfunction
     44 
     45 function! GetHareIndent()
     46   let line = getline(v:lnum)
     47   let prevlnum = prevnonblank(v:lnum - 1)
     48   let prevline = getline(prevlnum)
     49   let prevprevline = getline(prevnonblank(prevlnum - 1))
     50 
     51   " This is all very hacky and imperfect, but it's tough to do much better when
     52   " working with regex-based indenting rules.
     53 
     54   " If the previous line ended with =, indent by one shiftwidth.
     55   if prevline =~# '\v\=\s*(//.*)?$'
     56     return indent(prevlnum) + shiftwidth()
     57   endif
     58 
     59   " If the previous line ended in a semicolon and the line before that ended
     60   " with =, deindent by one shiftwidth.
     61   if prevline =~# '\v;\s*(//.*)?$' && prevprevline =~# '\v\=\s*(//.*)?$'
     62     return indent(prevlnum) - shiftwidth()
     63   endif
     64 
     65   " TODO: The following edge-case is still indented incorrectly:
     66   " case =>
     67   "         if (foo) {
     68   "                 bar;
     69   "         };
     70   " | // cursor is incorrectly deindented by one shiftwidth.
     71   "
     72   " This only happens if the {} block is the first statement in the case body.
     73   " If `case` is typed, the case will also be incorrectly deindented by one
     74   " shiftwidth. Are you having fun yet?
     75 
     76   " Deindent cases.
     77   if line =~# '\v^\s*case'
     78     " If the previous line was also a case, don't do any special indenting.
     79     if prevline =~# '\v^\s*case'
     80       return indent(prevlnum)
     81     end
     82 
     83     " If the previous line was a multiline case, deindent by one shiftwidth.
     84     if prevline =~# '\v\=\>\s*(//.*)?$'
     85       return indent(prevlnum) - shiftwidth()
     86     endif
     87 
     88     " If the previous line started a block, deindent by one shiftwidth.
     89     " This handles the first case in a switch/match block.
     90     if prevline =~# '\v\{\s*(//.*)?$'
     91       return FloorCindent(v:lnum) - shiftwidth()
     92     end
     93 
     94     " If the previous line ended in a semicolon and the line before that wasn't
     95     " a case, deindent by one shiftwidth.
     96     if prevline =~# '\v;\s*(//.*)?$' && prevprevline !~# '\v\=\>\s*(//.*)?$'
     97       return FloorCindent(v:lnum) - shiftwidth()
     98     end
     99 
    100     let l:indent = FloorCindent(v:lnum)
    101 
    102     " If a normal cindent would indent the same amount as the previous line,
    103     " deindent by one shiftwidth. This fixes some issues with `case let` blocks.
    104     if l:indent == indent(prevlnum)
    105       return l:indent - shiftwidth()
    106     endif
    107 
    108     " Otherwise, do a normal cindent.
    109     return l:indent
    110   endif
    111 
    112   " Don't indent an extra shiftwidth for cases which span multiple lines.
    113   if prevline =~# '\v\=\>\s*(//.*)?$' && prevline !~# '\v^\s*case\W'
    114     return indent(prevlnum)
    115   endif
    116 
    117   " Indent the body of a case.
    118   " If the previous line ended in a semicolon and the line before that was a
    119   " case, don't do any special indenting.
    120   if prevline =~# '\v;\s*(//.*)?$' && prevprevline =~# '\v\=\>\s*(//.*)?$'
    121         \ && line !~# '\v^\s*}'
    122     return indent(prevlnum)
    123   endif
    124 
    125   let l:indent = FloorCindent(v:lnum)
    126 
    127   " If the previous line was a case and a normal cindent wouldn't indent, indent
    128   " an extra shiftwidth.
    129   if prevline =~# '\v\=\>\s*(//.*)?$' && l:indent == indent(prevlnum)
    130     return l:indent + shiftwidth()
    131   endif
    132 
    133   " If everything above is false, do a normal cindent.
    134   return l:indent
    135 endfunction
    136 
    137 " vim: tabstop=2 shiftwidth=2 expandtab