Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
hare.vim345 linesDownload Raw Back to indent
1vim9script2 3# Vim indent file.4# Language:    Hare5# Maintainer:  Amelia Clarke <selene@perilune.dev>6# Last Change: 2026 Jan 247# Upstream:    https://git.sr.ht/~sircmpwn/hare.vim8 9if exists('b:did_indent')10  finish11endif12b:did_indent = 113 14# L0 -> Don't unindent lines that look like C labels.15# :0 -> Don't indent `case` in match and switch expressions. This only affects16#       lines containing `:` (that isn't part of `::`).17# +0 -> Don't indent continuation lines.18# (s -> Indent one level inside parens.19# u0 -> Don't indent additional levels inside nested parens.20# U1 -> Don't treat `(` any differently if it started a line.21# m1 -> Indent lines starting with `)` the same as the matching `(`.22# j1 -> Indent blocks one level inside parens.23# J1 -> Indent structs and unions correctly.24# *0 -> Don't search for unclosed C-style block comments.25# #1 -> Don't unindent lines starting with `#`.26setlocal cinoptions=L0,:0,+0,(s,u0,U1,m1,j1,J1,*0,#127setlocal cinscopedecls=28setlocal indentexpr=GetHareIndent()29setlocal indentkeys=0{,0},0),0],!^F,o,O,e,0=case30setlocal nolisp31b:undo_indent = 'setl cino< cinsd< inde< indk< lisp<'32 33# Calculates the indentation for the current line, using the value computed by34# cindent and manually fixing the cases where it behaves incorrectly.35def GetHareIndent(): number36  # Get the preceding lines of context and the value computed by cindent.37  const line = getline(v:lnum)38  const [plnum, pline] = PrevNonBlank(v:lnum - 1)39  const [pplnum, ppline] = PrevNonBlank(plnum - 1)40  const pindent = indent(plnum)41  const ppindent = indent(pplnum)42  const cindent = cindent(v:lnum) / shiftwidth() * shiftwidth()43 44  # If this line is a comment, don't try to align it with a comment at the end45  # of the previous line.46  if line =~ '^\s*//' && getline(plnum) =~ '\s*//.*$'47    return -148  endif49 50  # Indent `case`.51  if line =~ '^\s*case\>'52    # If the previous line was also a `case`, use the same indent.53    if pline =~ '^\s*case\>'54      return pindent55    endif56 57    # If the previous line started the block, use the same indent.58    if pline =~ '{$'59      if pline =~ '\v<%(match|switch)>[^(]*\('60        return pindent61      endif62      return pindent - GetValue('hare_indent_match_switch', 1, 1, 2)63        * shiftwidth()64    endif65 66    # If the current line contains a `:` that is not part of `::`, use the67    # computed cindent.68    if line =~ '\v%(%(::)*)@>:'69      return cindent70    endif71 72    # Unindent after a multi-line `case`.73    if pline =~ '=>$'74      return pindent - shiftwidth() * GetValue('hare_indent_case', 2)75    endif76 77    # If the previous line closed a set of parens, search for the previous78    # `case` within the same block and use the same indent. This fixes issues79    # with `case` not being correctly unindented after a function call80    # continuation line:81    #82    #   case let err: fs::error =>83    #           fmt::fatalf("Unable to open {}: {}",84    #                   os::args[1], fs::strerror(err));85    #           case // <-- cindent tries to unindent by only one shiftwidth86    if pline =~ ');$'87      const case = PrevMatchInBlock('^\s*case\>', plnum - 1)88      if case > 089        return indent(case)90      endif91    endif92 93    # If cindent would indent the same or more than the previous line, unindent.94    if cindent >= pindent95      return pindent - shiftwidth()96    endif97 98    # Otherwise, use the computed cindent.99    return cindent100  endif101 102  # Indent after `case`.103  if line !~ '^\s*}'104    # If the previous `case` started and ended on the same line, indent.105    if pline =~ '^\s*case\>.*;$'106      return pindent + shiftwidth()107    endif108 109    # Indent after a single-line `case`.110    if pline =~ '^\s*case\>.*=>$'111      return pindent + shiftwidth()112    endif113 114    # Indent inside a multi-line `case`.115    if pline =~ '^\s*case\>' && pline !~ '=>'116      return pindent + shiftwidth() * GetValue('hare_indent_case', 2)117    endif118 119    # Indent after a multi-line `case`.120    if pline =~ '=>$'121      return pindent - shiftwidth() * (GetValue('hare_indent_case', 2) - 1)122    endif123 124    # Don't unindent while inside a `case` body.125    if ppline =~ '=>$' && pline =~ ';$'126      return pindent127    endif128 129    # Don't unindent if the previous line ended a block. This fixes a very130    # peculiar edge case where cindent would try to unindent after a block, but131    # only if it is the first expression within a `case` body:132    #133    #   case =>134    #           if (foo) {135    #                   bar();136    #           };137    #   | <-- cindent tries to unindent by one shiftwidth138    if pline =~ '};$' && cindent < pindent139      return pindent140    endif141 142    # If the previous line closed a set of parens, and cindent would try to143    # unindent more than one level, search for the previous `case` within the144    # same block. If that line didn't contain a `:` (excluding `::`), indent one145    # level more. This fixes an issue where cindent would unindent too far when146    # there was no `:` after a `case`:147    #148    #   case foo =>149    #           bar(baz,150    #                   quux);151    #   | <-- cindent tries to unindent by two shiftwidths152    if pline =~ ').*;$' && cindent < pindent - shiftwidth()153      const case = PrevMatchInBlock('^\s*case\>', plnum - 1)154      if case > 0 && GetTrimmedLine(case) !~ '\v%(%(::)*)@>:'155        return indent(case) + shiftwidth()156      endif157    endif158  endif159 160  # If the previous line ended with `=`, indent.161  if pline =~ '=$'162    return pindent + shiftwidth()163  endif164 165  # If the previous line opened an array literal, indent.166  if pline =~ '[$'167    return pindent + shiftwidth()168  endif169 170  # If the previous line started a binding expression, indent.171  if pline =~ '\v<%(const|def|let|type)$'172    return pindent + shiftwidth()173  endif174 175  # Indent continuation lines.176  if !TrailingParen(pline)177    # If this line closed an array and cindent would indent the same amount as178    # the previous line, unindent.179    if line =~ '^\s*]' && cindent == pindent180      return cindent - shiftwidth()181    endif182 183    # If the previous line closed an array literal, use the same indent. This184    # fixes an issue where cindent would try to indent an additional level after185    # an array literal containing indexing or slicing expressions, but only186    # inside a block:187    #188    #   export fn main() void = {189    #           const foo = [190    #                   bar[..4],191    #                   baz[..],192    #                   quux[1..],193    #           ];194    #                   | <-- cindent tries to indent by one shiftwidth195    if pline =~ '^\s*];$' && cindent > pindent196      return pindent197    endif198 199    # Don't indent any further if the previous line closed an enum, struct, or200    # union.201    if pline =~ '^\s*},$' && cindent > pindent202      return pindent203    endif204 205    # If the previous line started a binding expression, and the first binding206    # was on the same line, indent.207    if pline =~ '\v<%(const|def|let|type)>.{-}\=.*,$'208      return pindent + shiftwidth()209    endif210 211    # Use the original indentation after a single continuation line.212    if pline =~ '[,;]$' && ppline =~ '=$'213      return ppindent214    endif215 216    # Don't unindent within a binding expression.217    if pline =~ ',$' && ppline =~ '\v<%(const|def|let|type)$'218      return pindent219    endif220  endif221 222  # If the previous line had an unclosed `if` or `for` condition, indent twice.223  if pline =~ '\v<%(if|for)>'224    const cond = match(pline, '\v%(if|for)>[^(]*\zs\(')225    if cond != -1 && TrailingParen(pline, cond)226      return pindent + shiftwidth() * 2227    endif228  endif229 230  # Optionally indent unclosed `match` and `switch` conditions an extra level.231  if pline =~ '\v<%(match|switch)>'232    const cond = match(pline, '\v<%(match|switch)>[^(]*\zs\(')233    if cond != -1 && TrailingParen(pline, cond)234      return pindent + shiftwidth()235        * GetValue('hare_indent_match_switch', 1, 1, 2)236    endif237  endif238 239  # Otherwise, use the computed cindent.240  return cindent241enddef242 243# Returns a line, with any comments or whitespace trimmed from the end.244def GetTrimmedLine(lnum: number): string245  var line = getline(lnum)246 247  # Use syntax highlighting attributes when possible.248  if has('syntax_items')249    # If the last character is inside a comment, do a binary search to find the250    # beginning of the comment.251    const len = strlen(line)252    if synIDattr(synID(lnum, len, true), 'name') =~ 'Comment\|Todo'253      var min = 1254      var max = len255      while min < max256        const col = (min + max) / 2257        if synIDattr(synID(lnum, col, true), 'name') =~ 'Comment\|Todo'258          max = col259        else260          min = col + 1261        endif262      endwhile263      line = strpart(line, 0, min - 1)264    endif265    return substitute(line, '\s*$', '', '')266  endif267 268  # Otherwise, use a regex as a fallback.269  return substitute(line, '\s*//.*$', '', '')270enddef271 272# Returns the value of a configuration variable, clamped within the given range.273def GetValue(274  name: string,275  default: number,276  min: number = 0,277  max: number = default,278): number279  const n = get(b:, name, get(g:, name, default))280  return min([max, max([n, min])])281enddef282 283# Returns the line number of the previous match for a pattern within the same284# block. Returns 0 if nothing was found.285def PrevMatchInBlock(286  pattern: string,287  lnum: number,288  maxlines: number = 20,289): number290  var block = 0291  for n in range(lnum, lnum - maxlines, -1)292    if n < 1293      break294    endif295 296    const line = GetTrimmedLine(n)297    if line =~ '{$'298      block -= 1299      if block < 0300        break301      endif302    endif303 304    if line =~ pattern && block == 0305      return n306    endif307 308    if line =~ '^\s*}'309      block += 1310    endif311  endfor312  return 0313enddef314 315# Returns the line number and contents of the previous non-blank line, with any316# comments trimmed.317def PrevNonBlank(lnum: number): tuple<number, string>318  var plnum = prevnonblank(lnum)319  var pline = GetTrimmedLine(plnum)320  while plnum > 1 && pline !~ '[^[:blank:]]'321    plnum = prevnonblank(plnum - 1)322    pline = GetTrimmedLine(plnum)323  endwhile324  return (plnum, pline)325enddef326 327# Returns whether a line contains at least one unclosed `(`.328# XXX: Can still be fooled by parens inside rune and string literals.329def TrailingParen(line: string, start: number = 0): bool330  var total = 0331  for n in strpart(line, start)->filter((_, n) => n =~ '[()]')->reverse()332    if n == ')'333      total += 1334    else335      total -= 1336      if total < 0337        return true338      endif339    endif340  endfor341  return false342enddef343 344# vim: et sts=2 sw=2 ts=8 tw=80345 
codekingpro/portable-devtools · Team Ai