changeset 33956:e0535b3b9d77

runtime(racket): update Racket runtime files (#13693) Commit: https://github.com/vim/vim/commit/5eb9cb53d619f89251d22299e2cb4f21918d9d38 Author: D. Ben Knoble <ben.knoble+github@gmail.com> Date: Sat Dec 16 08:24:15 2023 -0500 runtime(racket): update Racket runtime files (https://github.com/vim/vim/issues/13693) This brings the included Racket runtime files to commit 43bfc87 (update headers, 2023-12-15) of https://github.com/benknoble/vim-racket. Note that not all files from that repository are included. (In particular, the ftdetect script is omitted for now.) Signed-off-by: D. Ben Knoble <ben.knoble+github@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
author Christian Brabandt <cb@256bit.org>
date Sat, 16 Dec 2023 14:30:06 +0100
parents 2a36aec80c9e
children e1ae246924ee
files runtime/autoload/racket.vim runtime/indent/racket.vim runtime/syntax/racket.vim
diffstat 3 files changed, 231 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/runtime/autoload/racket.vim
@@ -0,0 +1,213 @@
+" Maintainer: D. Ben Knoble <ben.knoble+github@gmail.com>
+" URL: https://github.com/benknoble/vim-racket
+" Last Change: 2023 Sep 22
+vim9script
+
+def MakePatternFromLiterals(xs: list<string>): string
+  return printf('\V%s', xs->mapnew((_, v) => escape(v, '\'))->join('\|'))
+enddef
+
+const openers = ['(', '[', '{']
+const closers = {'(': ')', '[': ']', '{': '}'}
+const brackets_pattern: string = closers->items()->flattennew()->MakePatternFromLiterals()
+
+# transliterated from a modified copy of src/indent.c
+
+export def Indent(): number
+  if InHerestring(v:lnum)
+    return -1
+  endif
+  # Indent from first column to avoid odd results from nested forms.
+  cursor(v:lnum, 1)
+  const bracket = FindBracket()
+  if bracket == null_dict || !bracket.found
+    return -1
+  endif
+
+  # assert_report(printf('{lnum: %d, str: %s, found: %s, line: %d, column: %d}',
+  #   v:lnum, getline(bracket.line)[bracket.column - 1], bracket.found, bracket.line, bracket.column))
+  # N.B. Column =/= Line Index; Columns start at 1
+  const amount: number = bracket.column
+  const line = getline(bracket.line)
+
+  const lw = Lispword(line[bracket.column :])
+  if !IsForFold(lw) # skip: see comments about for/fold special case below
+    # "Extra trick"
+    var current = prevnonblank(v:lnum - 1)
+    while current > bracket.line
+      cursor(current, 1)
+      if getline(current) !~# '^\s*;' && synID(current, 1, 0)->synIDattr('name') !~? 'string' && FindBracket() == bracket
+        return indent(current)
+      endif
+      current = prevnonblank(current - 1)
+    endwhile
+    cursor(v:lnum, 1)
+  endif
+
+  if index(openers, line[bracket.column - 1]) >= 0 && !empty(lw)
+    # Special case for/fold &co. The iterator clause (2nd form) is indented
+    # under the accumulator clause (1st form). Everything else is standard.
+    const start_of_first_form = match(line[bracket.column :], MakePatternFromLiterals(openers))
+    # assert_report(printf('{line: %s}', line))
+    # assert_report(printf('{start: %s}', start_of_first_form >= 0 ? line[bracket.column + start_of_first_form :] : '<NULL>'))
+    if IsForFold(lw) && IsSecondForm(bracket.line, bracket.column, v:lnum) && start_of_first_form >= 0
+      return amount + start_of_first_form
+    else
+      # Lispword, but not for/fold second form (or first form couldn't be
+      # found): indent like define or lambda.
+      # 2 extra indent, but subtract 1 for columns starting at 1.
+      # Current vim9 doesn't constant fold "x + 2 - 1", so write "x + 1"
+      return amount + 1
+    endif
+  else
+    # assert_report(printf('{line: %s}', line[bracket.column :]))
+    return amount + IndentForContinuation(bracket.line, bracket.column, line[bracket.column :])
+  endif
+enddef
+
+def InHerestring(start: number): bool
+  return synID(start, col([start, '$']) - 1, 0)->synIDattr('name') =~? 'herestring'
+enddef
+
+def FindBracket(): dict<any>
+  const paren = FindMatch('(', ')')
+  const square = FindMatch('\[', ']')
+  const curly = FindMatch('{', '}')
+  return null_dict
+    ->MatchMax(paren)
+    ->MatchMax(square)
+    ->MatchMax(curly)
+enddef
+
+def Lispword(line: string): string
+  # assume keyword on same line as opener
+  const word: string = matchstr(line, '^\s*\k\+\>')->trim()
+  # assert_report(printf('line: %s; word: %s', line, word))
+  # assert_report(&l:lispwords->split(',')->index(word) >= 0 ? 't' : 'f')
+  return &l:lispwords->split(',')->index(word) >= 0 ? word : ''
+enddef
+
+# line contains everything on line_nr after column
+def IndentForContinuation(line_nr: number, column: number, line: string): number
+  const end = len(line)
+  var indent = match(line, '[^[:space:]]')
+  # first word is a string or some other literal (or maybe a form); assume that
+  # the current line is outside such a thing
+  if indent < end && ['"', '#']->index(line[indent]) >= 0
+    return indent
+  endif
+  if indent < end && ["'", '`']->index(line[indent]) >= 0
+    # could be a form or a word. Advance one and see.
+    ++indent
+  endif
+  if indent < end && ['(', '[', '{']->index(line[indent]) >= 0
+    # there's a form; assume outside, but need to skip it to see if any others
+    cursor(line_nr, column + indent + 1)
+    # assert_report(getline(line_nr)[column + indent :])
+    normal! %
+    const [_, matched_line, matched_col, _, _] = getcursorcharpos()
+    if line_nr != matched_line || matched_col == column + indent + 1
+      return indent
+    endif
+    indent = matched_col - column
+  endif
+  var in_delim: bool
+  var quoted: bool
+  while indent < end && (line[indent] !~# '\s' || in_delim || quoted)
+    if line[indent] == '\' && !in_delim
+      quoted = true
+    else
+      quoted = false
+    endif
+    if line[indent] == '|' && !quoted
+      in_delim = !in_delim
+    endif
+    ++indent
+  endwhile
+  # not handling newlines in first words
+  if quoted || in_delim
+    return 0
+  endif
+  # no other word on this line
+  if indent == end
+    return 0
+  endif
+  # find beginning of next word
+  indent += match(line[indent :], '[^[:space:]]')
+  return indent
+enddef
+
+def FindMatch(start: string, end: string): dict<any>
+  # TODO too slow…
+  # could try replicating C? might have false positives. Or make "100"
+  # configurable number: for amounts of indent bodies, we're still fast enough…
+  const [linenr, column] = searchpairpos(start, '', end, 'bnzW',
+    () =>
+      synID(line('.'), col('.'), 0)->synIDattr('name') =~? 'char\|string\|comment',
+    line('.') > 100 ? line('.') - 100 : 0)
+  if linenr > 0 && column > 0
+    return {found: true, line: linenr, column: column}
+  else
+    return {found: false, line: linenr, column: column}
+  endif
+enddef
+
+def MatchMax(left: dict<any>, right: dict<any>): dict<any>
+  if left == null_dict || !left.found
+    return right
+  endif
+  if right == null_dict || !right.found
+    return left
+  endif
+  # left and right non-null, both found
+  return PosLT(left, right) ? right : left
+enddef
+
+def PosLT(left: dict<any>, right: dict<any>): bool
+  return left.line != right.line
+     \ ? left.line < right.line
+     \ : (left.column != right.column && left.column < right.column)
+enddef
+
+def IsForFold(word: string): bool
+  return ['for/fold', 'for/foldr', 'for*/fold', 'for*/foldr']->index(word) >= 0
+enddef
+
+def IsSecondForm(blnum: number, bcol: number, vlnum: number): bool
+  var forms_seen: number # "top-level" (inside for/fold) counter only
+  var [lnum, col] = [blnum, bcol + 1]
+  cursor(lnum, col)
+  var stack: list<string> = []
+
+  while lnum <= vlnum
+    const found = search(brackets_pattern, '', vlnum, 0, () =>
+      synID(line('.'), col('.'), 0)->synIDattr('name') =~? 'char\|string\|comment')
+    if found <= 0
+      break
+    endif
+    const pos = getcursorcharpos()
+    lnum = pos[1]
+    col = pos[2]
+    var current_char = getline(lnum)[col - 1]
+    # assert_report(printf('search: %d, %d: %s', lnum, col, current_char))
+    # assert_report(printf('forms seen post-search: %d', forms_seen))
+    if index(openers, current_char) >= 0
+      insert(stack, current_char)
+    elseif !empty(stack) && current_char ==# closers[stack[0]]
+      stack = stack[1 :]
+      if empty(stack)
+        ++forms_seen
+      endif
+    else
+      # parse failure of some kind: not an opener or not the correct closer
+      return false
+    endif
+    # assert_report(printf('forms seen pre-check: %d', forms_seen))
+    if forms_seen > 2
+      return false
+    endif
+  endwhile
+
+  # assert_report(printf('forms seen pre-return: %d', forms_seen))
+  return forms_seen == 2 || (forms_seen == 1 && !empty(stack))
+enddef
--- a/runtime/indent/racket.vim
+++ b/runtime/indent/racket.vim
@@ -3,7 +3,7 @@
 " Maintainer:           D. Ben Knoble <ben.knoble+github@gmail.com>
 " Previous Maintainer:  Will Langstroth <will@langstroth.com>
 " URL:                  https://github.com/benknoble/vim-racket
-" Last Change: 2022 Aug 12
+" Last Change:          2023 Jul 17
 
 if exists("b:did_indent")
    finish
@@ -11,18 +11,25 @@ endif
 let b:did_indent = 1
 
 setlocal lisp autoindent nosmartindent
+if has('vim9script')
+  setlocal indentexpr=racket#Indent() lispoptions+=expr:1
+endif
 
-setlocal lispwords+=module,module*,module+,parameterize,let-values,let*-values,letrec-values,local
+setlocal lispwords+=module,module*,module+,parameterize,parameterize*,let-values,let*-values,letrec-values,local
 setlocal lispwords+=define/contract
 setlocal lispwords+=λ
 setlocal lispwords+=with-handlers
 setlocal lispwords+=define-values,opt-lambda,case-lambda,syntax-rules,with-syntax,syntax-case,syntax-parse
 setlocal lispwords+=define-for-syntax,define-syntax-parser,define-syntax-parse-rule,define-syntax-class,define-splicing-syntax-class
+setlocal lispwords+=define-syntax-parameter,syntax-parameterize
 setlocal lispwords+=define-signature,unit,unit/sig,compund-unit/sig,define-values/invoke-unit/sig
 setlocal lispwords+=define-opt/c,define-syntax-rule
-setlocal lispwords+=define-test-suite
+setlocal lispwords+=define-test-suite,test-case
 setlocal lispwords+=struct
 setlocal lispwords+=with-input-from-file,with-output-to-file
+setlocal lispwords+=begin,begin0
+setlocal lispwords+=place
+setlocal lispwords+=cond
 
 " Racket OOP
 " TODO missing a lot of define-like forms here (e.g., define/augment, etc.)
@@ -41,6 +48,8 @@ setlocal lispwords+=for/hash,for/hasheq,
 setlocal lispwords+=for/async
 setlocal lispwords+=for/set,for*/set
 setlocal lispwords+=for/first,for*/first
+setlocal lispwords+=for/last,for*/last
+setlocal lispwords+=for/stream,for*/stream
 
 setlocal lispwords+=match,match*,match/values,define/match,match-lambda,match-lambda*,match-lambda**
 setlocal lispwords+=match-let,match-let*,match-let-values,match-let*-values
@@ -57,4 +66,4 @@ setlocal lispwords+=if-view,case-view,co
 setlocal lispwords+=case/dep
 setlocal lispwords+=define/obs
 
-let b:undo_indent = "setlocal lisp< ai< si< lw<"
+let b:undo_indent = "setlocal indentexpr< lisp< lispoptions< ai< si< lw<"
--- a/runtime/syntax/racket.vim
+++ b/runtime/syntax/racket.vim
@@ -4,7 +4,7 @@
 " Previous Maintainer:  Will Langstroth <will@langstroth.com>
 " URL:                  https://github.com/benknoble/vim-racket
 " Description:          Contains all of the keywords in #lang racket
-" Last Change: 2022 Aug 12
+" Last Change:          2023 Sep 22
 
 " Initializing:
 if exists("b:current_syntax")
@@ -514,13 +514,13 @@ syntax region racketString start=/\%(\\\
 syntax region racketString start=/#"/           skip=/\\[\\"]/ end=/"/ contains=racketStringEscapeError,racketStringEscape
 
 if exists("racket_no_string_fold")
-  syn region racketString start=/#<<\z(.*\)$/ end=/^\z1$/
+  syn region racketHereString start=/#<<\z(.*\)$/ end=/^\z1$/
 else
-  syn region racketString start=/#<<\z(.*\)$/ end=/^\z1$/ fold
+  syn region racketHereString start=/#<<\z(.*\)$/ end=/^\z1$/ fold
 endif
 
 
-syntax cluster racketTop  add=racketError,racketConstant,racketStruc,racketString
+syntax cluster racketTop  add=racketError,racketConstant,racketStruc,racketString,racketHereString
 
 " Numbers
 
@@ -623,6 +623,7 @@ highlight default link racketFunc Functi
 
 highlight default link racketString String
 highlight default link racketStringEscape Special
+highlight default link racketHereString String
 highlight default link racketUStringEscape Special
 highlight default link racketStringEscapeError Error
 highlight default link racketChar Character