neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

hare.vim (4538B)


      1 " Vim indent file
      2 " Language:    Hare
      3 " Maintainer:  Amelia Clarke <selene@perilune.dev>
      4 " Last Change: 2024-04-14
      5 " Upstream:    https://git.sr.ht/~sircmpwn/hare.vim
      6 
      7 if exists('b:did_indent')
      8  finish
      9 endif
     10 let b:did_indent = 1
     11 
     12 let s:cpo_save = &cpo
     13 set cpo&vim
     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 let b:undo_indent = 'setl cino< cinw< inde< indk<'
     42 
     43 if exists('*GetHareIndent()')
     44  finish
     45 endif
     46 
     47 function! FloorCindent(lnum)
     48  return cindent(a:lnum) / shiftwidth() * shiftwidth()
     49 endfunction
     50 
     51 function! 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.
    126  if prevline =~# '\v;\s*(//.*)?$' && prevprevline =~# '\v\=\>\s*(//.*)?$'
    127        \ && line !~# '\v^\s*}'
    128    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
    141 endfunction
    142 
    143 let &cpo = s:cpo_save
    144 unlet s:cpo_save
    145 
    146 " vim: et sw=2 sts=2 ts=8