diff runtime/indent/mp.vim @ 29885:f00c56ee8118

Update runtime files Commit: https://github.com/vim/vim/commit/7dd543246a4c21c4d5a4242a28076706f5abebd4 Author: Bram Moolenaar <Bram@vim.org> Date: Fri Aug 26 18:01:12 2022 +0100 Update runtime files
author Bram Moolenaar <Bram@vim.org>
date Fri, 26 Aug 2022 19:15:05 +0200
parents bdd7fc1a38c0
children
line wrap: on
line diff
--- a/runtime/indent/mp.vim
+++ b/runtime/indent/mp.vim
@@ -1,360 +1,320 @@
-" MetaPost indent file
-" Language:           MetaPost
-" Maintainer:         Nicola Vitacolonna <nvitacolonna@gmail.com>
-" Former Maintainers: Eugene Minkovskii <emin@mccme.ru>
-" Last Change:        2016 Oct 2, 4:13pm
-" Version: 0.2
+vim9script
+
+# MetaPost indent file
+# Language:           MetaPost
+# Maintainer:         Nicola Vitacolonna <nvitacolonna@gmail.com>
+# Former Maintainers: Eugene Minkovskii <emin@mccme.ru>
+# Latest Revision:    2022 Aug 12
 
 if exists("b:did_indent")
   finish
 endif
-let b:did_indent = 1
 
-setlocal indentexpr=GetMetaPostIndent()
-setlocal indentkeys+==end,=else,=fi,=fill,0),0]
-
-let b:undo_indent = "setl indentkeys< indentexpr<"
+b:did_indent = 1
 
-" Only define the function once.
-if exists("*GetMetaPostIndent")
-  finish
-endif
-let s:keepcpo= &cpo
-set cpo&vim
+setlocal indentexpr=g:MetaPostIndent()
+setlocal indentkeys+==end,=else,=fi,=fill,0),0]
+setlocal nolisp
+setlocal nosmartindent
+
+b:undo_indent = "setl indentexpr< indentkeys< lisp< smartindent<"
 
-function GetMetaPostIndent()
-  let ignorecase_save = &ignorecase
-  try
-    let &ignorecase = 0
-    return GetMetaPostIndentIntern()
-  finally
-    let &ignorecase = ignorecase_save
-  endtry
-endfunc
+# Regexps {{{
+# Expressions starting indented blocks
+const MP_OPEN_TAG = [
+  '\<if\>',
+  '\<else\%[if]\>',
+  '\<for\%(\|ever\|suffixes\)\>',
+  '\<begingroup\>',
+  '\<\%(\|var\|primary\|secondary\|tertiary\)def\>',
+  '^\s*\<begin\%(fig\|graph\|glyph\|char\|logochar\)\>',
+  '[([{]',
+  ]->extend(get(g:, "mp_open_tag", []))->join('\|')
 
-" Regexps {{{
-" Note: the next three variables are made global so that a user may add
-" further keywords.
-"
-" Example:
-"
-"    Put these in ~/.vim/after/indent/mp.vim
-"
-"    let g:mp_open_tag .= '\|\<begintest\>'
-"    let g:mp_close_tag .= '\|\<endtest\>'
-
-" Expressions starting indented blocks
-let g:mp_open_tag = ''
-      \ . '\<if\>'
-      \ . '\|\<else\%[if]\>'
-      \ . '\|\<for\%(\|ever\|suffixes\)\>'
-      \ . '\|\<begingroup\>'
-      \ . '\|\<\%(\|var\|primary\|secondary\|tertiary\)def\>'
-      \ . '\|^\s*\<begin\%(fig\|graph\|glyph\|char\|logochar\)\>'
-      \ . '\|[([{]'
+# Expressions ending indented blocks
+const MP_CLOSE_TAG = [
+  '\<fi\>',
+  '\<else\%[if]\>',
+  '\<end\%(\|for\|group\|def\|fig\|char\|glyph\|graph\)\>',
+  '[)\]}]'
+  ]->extend(get(g:, "mp_close_tag", []))->join('\|')
 
-" Expressions ending indented blocks
-let g:mp_close_tag = ''
-      \ . '\<fi\>'
-      \ . '\|\<else\%[if]\>'
-      \ . '\|\<end\%(\|for\|group\|def\|fig\|char\|glyph\|graph\)\>'
-      \ . '\|[)\]}]'
+# Statements that may span multiple lines and are ended by a semicolon. To
+# keep this list short, statements that are unlikely to be very long or are
+# not very common (e.g., keywords like `interim` or `showtoken`) are not
+# included.
+#
+# The regex for assignments and equations (the last branch) is tricky, because
+# it must not match things like `for i :=`, `if a=b`, `def...=`, etc... It is
+# not perfect, but it works reasonably well.
+const MP_STATEMENT = [
+  '\<\%(\|un\|cut\)draw\%(dot\)\=\>',
+  '\<\%(\|un\)fill\%[draw]\>',
+  '\<draw\%(dbl\)\=arrow\>',
+  '\<clip\>',
+  '\<addto\>',
+  '\<save\>',
+  '\<setbounds\>',
+  '\<message\>',
+  '\<errmessage\>',
+  '\<errhelp\>',
+  '\<fontmapline\>',
+  '\<pickup\>',
+  '\<show\>',
+  '\<special\>',
+  '\<write\>',
+  '\%(^\|;\)\%([^;=]*\%(' .. MP_OPEN_TAG .. '\)\)\@!.\{-}:\==',
+  ]->join('\|')
 
-" Statements that may span multiple lines and are ended by a semicolon. To
-" keep this list short, statements that are unlikely to be very long or are
-" not very common (e.g., keywords like `interim` or `showtoken`) are not
-" included.
-"
-" The regex for assignments and equations (the last branch) is tricky, because
-" it must not match things like `for i :=`, `if a=b`, `def...=`, etc... It is
-" not perfect, but it works reasonably well.
-let g:mp_statement = ''
-      \ . '\<\%(\|un\|cut\)draw\>'
-      \ . '\|\<\%(\|un\)fill\%[draw]\>'
-      \ . '\|\<draw\%(dbl\)\=arrow\>'
-      \ . '\|\<clip\>'
-      \ . '\|\<addto\>'
-      \ . '\|\<save\>'
-      \ . '\|\<setbounds\>'
-      \ . '\|\<message\>'
-      \ . '\|\<errmessage\>'
-      \ . '\|\<errhelp\>'
-      \ . '\|\<fontmapline\>'
-      \ . '\|\<pickup\>'
-      \ . '\|\<show\>'
-      \ . '\|\<special\>'
-      \ . '\|\<write\>'
-      \ . '\|\%(^\|;\)\%([^;=]*\%('.g:mp_open_tag.'\)\)\@!.\{-}:\=='
+# A line ends with zero or more spaces, possibly followed by a comment.
+const EOL = '\s*\%($\|%\)'
+# }}}
+
+# Auxiliary functions {{{
+# Returns true if (0-based) position immediately preceding `pos` in `line` is
+# inside a string or a comment; returns false otherwise.
 
-" A line ends with zero or more spaces, possibly followed by a comment.
-let s:eol = '\s*\%($\|%\)'
-" }}}
-
-" Auxiliary functions {{{
-" Returns 1 if (0-based) position immediately preceding `pos` in `line` is
-" inside a string or a comment; returns 0 otherwise.
+# This is the function that is called more often when indenting, so it is
+# critical that it is efficient. The method we use is significantly faster
+# than using syntax attributes, and more general (it does not require
+# syntax_items). It is also faster than using a single regex matching an even
+# number of quotes. It helps that MetaPost strings cannot span more than one
+# line and cannot contain escaped quotes.
+def IsCommentOrString(line: string, pos: number): bool
+  var in_string = 0
+  var q = stridx(line, '"')
+  var c = stridx(line, '%')
 
-" This is the function that is called more often when indenting, so it is
-" critical that it is efficient. The method we use is significantly faster
-" than using syntax attributes, and more general (it does not require
-" syntax_items). It is also faster than using a single regex matching an even
-" number of quotes. It helps that MetaPost strings cannot span more than one
-" line and cannot contain escaped quotes.
-function! s:CommentOrString(line, pos)
-  let in_string = 0
-  let q = stridx(a:line, '"')
-  let c = stridx(a:line, '%')
-  while q >= 0 && q < a:pos
+  while q >= 0 && q < pos
     if c >= 0 && c < q
-      if in_string " Find next percent symbol
-        let c = stridx(a:line, '%', q + 1)
-      else " Inside comment
-        return 1
+      if in_string # Find next percent symbol
+        c = stridx(line, '%', q + 1)
+      else # Inside comment
+        return true
       endif
     endif
-    let in_string = 1 - in_string
-    let q = stridx(a:line, '"', q + 1) " Find next quote
+    in_string = 1 - in_string
+    q = stridx(line, '"', q + 1) # Find next quote
   endwhile
-  return in_string || (c >= 0 && c <= a:pos)
-endfunction
+
+  return in_string || (c >= 0 && c <= pos)
+enddef
 
-" Find the first non-comment non-blank line before the current line.
-function! s:PrevNonBlankNonComment(lnum)
-  let l:lnum = prevnonblank(a:lnum - 1)
-  while getline(l:lnum) =~# '^\s*%'
-    let l:lnum = prevnonblank(l:lnum - 1)
+# Find the first non-comment non-blank line before the given line.
+def PrevNonBlankNonComment(lnum: number): number
+  var nr = prevnonblank(lnum - 1)
+  while getline(nr) =~# '^\s*%'
+    nr = prevnonblank(nr - 1)
   endwhile
-  return l:lnum
-endfunction
+  return nr
+enddef
 
-" Returns true if the last tag appearing in the line is an open tag; returns
-" false otherwise.
-function! s:LastTagIsOpen(line)
-  let o = s:LastValidMatchEnd(a:line, g:mp_open_tag, 0)
-  if o == - 1 | return v:false | endif
-  return s:LastValidMatchEnd(a:line, g:mp_close_tag, o) < 0
-endfunction
+# Returns true if the last tag appearing in the line is an open tag; returns
+# false otherwise.
+def LastTagIsOpen(line: string): bool
+  var o = LastValidMatchEnd(line, MP_OPEN_TAG, 0)
+  if o == - 1
+    return false
+  endif
+  return LastValidMatchEnd(line, MP_CLOSE_TAG, o) < 0
+enddef
 
-" A simple, efficient and quite effective heuristics is used to test whether
-" a line should cause the next line to be indented: count the "opening tags"
-" (if, for, def, ...) in the line, count the "closing tags" (endif, endfor,
-" ...) in the line, and compute the difference. We call the result the
-" "weight" of the line. If the weight is positive, then the next line should
-" most likely be indented. Note that `else` and `elseif` are both opening and
-" closing tags, so they "cancel out" in almost all cases, the only exception
-" being a leading `else[if]`, which is counted as an opening tag, but not as
-" a closing tag (so that, for instance, a line containing a single `else:`
-" will have weight equal to one, not zero). We do not treat a trailing
-" `else[if]` in any special way, because lines ending with an open tag are
-" dealt with separately before this function is called (see
-" GetMetaPostIndentIntern()).
-"
-" Example:
-"
-"     forsuffixes $=a,b: if x.$ = y.$ : draw else: fill fi
-"       % This line will be indented because |{forsuffixes,if,else}| > |{else,fi}| (3 > 2)
-"     endfor
-
-function! s:Weight(line)
-  let [o, i] = [0, s:ValidMatchEnd(a:line, g:mp_open_tag, 0)]
+# A simple, efficient and quite effective heuristics is used to test whether
+# a line should cause the next line to be indented: count the "opening tags"
+# (if, for, def, ...) in the line, count the "closing tags" (endif, endfor,
+# ...) in the line, and compute the difference. We call the result the
+# "weight" of the line. If the weight is positive, then the next line should
+# most likely be indented. Note that `else` and `elseif` are both opening and
+# closing tags, so they "cancel out" in almost all cases, the only exception
+# being a leading `else[if]`, which is counted as an opening tag, but not as
+# a closing tag (so that, for instance, a line containing a single `else:`
+# will have weight equal to one, not zero). We do not treat a trailing
+# `else[if]` in any special way, because lines ending with an open tag are
+# dealt with separately before this function is called (see MetaPostIndent()).
+#
+# Example:
+#
+#     forsuffixes $=a,b: if x.$ = y.$ : draw else: fill fi
+#       % This line will be indented because |{forsuffixes,if,else}| > |{else,fi}| (3 > 2)
+#     endfor
+def Weight(line: string): number
+  var o = 0
+  var i = ValidMatchEnd(line, MP_OPEN_TAG, 0)
   while i > 0
-    let o += 1
-    let i = s:ValidMatchEnd(a:line, g:mp_open_tag, i)
+    o += 1
+    i = ValidMatchEnd(line, MP_OPEN_TAG, i)
   endwhile
-  let [c, i] = [0, matchend(a:line, '^\s*\<else\%[if]\>')] " Skip a leading else[if]
-  let i = s:ValidMatchEnd(a:line, g:mp_close_tag, i)
+  var c = 0
+  i = matchend(line, '^\s*\<else\%[if]\>')  # Skip a leading else[if]
+  i = ValidMatchEnd(line, MP_CLOSE_TAG, i)
   while i > 0
-    let c += 1
-    let i = s:ValidMatchEnd(a:line, g:mp_close_tag, i)
+    c += 1
+    i = ValidMatchEnd(line, MP_CLOSE_TAG, i)
   endwhile
   return o - c
-endfunction
+enddef
 
-" Similar to matchend(), but skips strings and comments.
-" line: a String
-function! s:ValidMatchEnd(line, pat, start)
-  let i = matchend(a:line, a:pat, a:start)
-  while i > 0 && s:CommentOrString(a:line, i)
-    let i = matchend(a:line, a:pat, i)
+# Similar to matchend(), but skips strings and comments.
+# line: a String
+def ValidMatchEnd(line: string, pat: string, start: number): number
+  var i = matchend(line, pat, start)
+  while i > 0 && IsCommentOrString(line, i)
+    i = matchend(line, pat, i)
   endwhile
   return i
-endfunction
+enddef
 
-" Like s:ValidMatchEnd(), but returns the end position of the last (i.e.,
-" rightmost) match.
-function! s:LastValidMatchEnd(line, pat, start)
-  let last_found = -1
-  let i = matchend(a:line, a:pat, a:start)
+# Like s:ValidMatchEnd(), but returns the end position of the last (i.e.,
+# rightmost) match.
+def LastValidMatchEnd(line: string, pat: string, start: number): number
+  var last_found = -1
+  var i = matchend(line, pat, start)
   while i > 0
-    if !s:CommentOrString(a:line, i)
-      let last_found = i
+    if !IsCommentOrString(line, i)
+      last_found = i
     endif
-    let i = matchend(a:line, a:pat, i)
+    i = matchend(line, pat, i)
   endwhile
   return last_found
-endfunction
-
-function! s:DecreaseIndentOnClosingTag(curr_indent)
-  let cur_text = getline(v:lnum)
-  if cur_text =~# '^\s*\%('.g:mp_close_tag.'\)'
-    return max([a:curr_indent - shiftwidth(), 0])
-  endif
-  return a:curr_indent
-endfunction
-" }}}
+enddef
 
-" Main function {{{
-"
-" Note: Every rule of indentation in MetaPost is very subjective. We might get
-" creative, but things get murky very soon (there are too many corner cases).
-" So, we provide a means for the user to decide what to do when this script
-" doesn't get it. We use a simple idea: use '%>', '%<' and '%=' to explicitly
-" control indentation. The '<' and '>' symbols may be repeated many times
-" (e.g., '%>>' will cause the next line to be indented twice).
-"
-" By using '%>...', '%<...' and '%=', the indentation the user wants is
-" preserved by commands like gg=G, even if it does not follow the rules of
-" this script.
-"
-" Example:
-"
-"    def foo =
-"        makepen(
-"            subpath(T-n,t) of r  %>
-"                shifted .5down   %>
-"                    --subpath(t,T) of r shifted .5up -- cycle   %<<<
-"        )
-"        withcolor black
-"    enddef
-"
-" The default indentation of the previous example would be:
-"
-"    def foo =
-"        makepen(
-"            subpath(T-n,t) of r
-"            shifted .5down
-"            --subpath(t,T) of r shifted .5up -- cycle
-"        )
-"        withcolor black
-"    enddef
-"
-" Personally, I prefer the latter, but anyway...
-function! GetMetaPostIndentIntern()
-  " Do not touch indentation inside verbatimtex/btex.. etex blocks.
+def DecreaseIndentOnClosingTag(curr_indent: number): number
+  var cur_text = getline(v:lnum)
+  if cur_text =~# '^\s*\%(' .. MP_CLOSE_TAG .. '\)'
+    return max([curr_indent - shiftwidth(), 0])
+  endif
+  return curr_indent
+enddef
+# }}}
+
+# Main function {{{
+def g:MetaPostIndent(): number
+  # Do not touch indentation inside verbatimtex/btex.. etex blocks.
   if synIDattr(synID(v:lnum, 1, 1), "name") =~# '^mpTeXinsert$\|^tex\|^Delimiter'
     return -1
   endif
 
-  " This is the reference line relative to which the current line is indented
-  " (but see below).
-  let lnum = s:PrevNonBlankNonComment(v:lnum)
+  # At the start of a MetaPost block inside ConTeXt, do not touch indentation
+  if synIDattr(synID(prevnonblank(v:lnum - 1), 1, 1), "name") == "contextBlockDelim"
+    return -1
+  endif
 
-  " At the start of the file use zero indent.
+  var lnum = PrevNonBlankNonComment(v:lnum)
+
+  # At the start of the file use zero indent.
   if lnum == 0
     return 0
   endif
 
-  let prev_text = getline(lnum)
+  var prev_text = getline(lnum)
 
-  " User-defined overrides take precedence over anything else.
-  " See above for an example.
-  let j = match(prev_text, '%[<>=]')
+  # Every rule of indentation in MetaPost is very subjective. We might get
+  # creative, but things get murky very soon (there are too many corner
+  # cases). So, we provide a means for the user to decide what to do when this
+  # script doesn't get it. We use a simple idea: use '%>', '%<', '%=', and
+  # '%!', to explicitly control indentation. The '<' and '>' symbols may be
+  # repeated many times (e.g., '%>>' will cause the next line to be indented
+  # twice).
+  #
+  # User-defined overrides take precedence over anything else.
+  var j = match(prev_text, '%[<>=!]')
   if j > 0
-    let i = strlen(matchstr(prev_text, '%>\+', j)) - 1
+    var i = strlen(matchstr(prev_text, '%>\+', j)) - 1
     if i > 0
       return indent(lnum) + i * shiftwidth()
     endif
 
-    let i = strlen(matchstr(prev_text, '%<\+', j)) - 1
+    i = strlen(matchstr(prev_text, '%<\+', j)) - 1
     if i > 0
       return max([indent(lnum) - i * shiftwidth(), 0])
     endif
 
-    if match(prev_text, '%=', j)
+    if match(prev_text, '%=', j) > -1
       return indent(lnum)
     endif
+
+    if match(prev_text, '%!', j) > -1
+      return -1
+    endif
   endif
 
-  " If the reference line ends with an open tag, indent.
-  "
-  " Example:
-  "
-  " if c:
-  "     0
-  " else:
-  "     1
-  " fi if c2: % Note that this line has weight equal to zero.
-  "     ...   % This line will be indented
-  if s:LastTagIsOpen(prev_text)
-    return s:DecreaseIndentOnClosingTag(indent(lnum) + shiftwidth())
+  # If the reference line ends with an open tag, indent.
+  #
+  # Example:
+  #
+  # if c:
+  #     0
+  # else:
+  #     1
+  # fi if c2: % Note that this line has weight equal to zero.
+  #     ...   % This line will be indented
+  if LastTagIsOpen(prev_text)
+    return DecreaseIndentOnClosingTag(indent(lnum) + shiftwidth())
   endif
 
-  " Lines with a positive weight are unbalanced and should likely be indented.
-  "
-  " Example:
-  "
-  " def f = enddef for i = 1 upto 5: if x[i] > 0: 1 else: 2 fi
-  "     ... % This line will be indented (because of the unterminated `for`)
-  if s:Weight(prev_text) > 0
-    return s:DecreaseIndentOnClosingTag(indent(lnum) + shiftwidth())
+  # Lines with a positive weight are unbalanced and should likely be indented.
+  #
+  # Example:
+  #
+  # def f = enddef for i = 1 upto 5: if x[i] > 0: 1 else: 2 fi
+  #     ... % This line will be indented (because of the unterminated `for`)
+  if Weight(prev_text) > 0
+    return DecreaseIndentOnClosingTag(indent(lnum) + shiftwidth())
   endif
 
-  " Unterminated statements cause indentation to kick in.
-  "
-  " Example:
-  "
-  " draw unitsquare
-  "     withcolor black; % This line is indented because of `draw`.
-  " x := a + b + c
-  "     + d + e;         % This line is indented because of `:=`.
-  "
-  let i = s:LastValidMatchEnd(prev_text, g:mp_statement, 0)
-  if i >= 0 " Does the line contain a statement?
-    if s:ValidMatchEnd(prev_text, ';', i) < 0 " Is the statement unterminated?
+  # Unterminated statements cause indentation to kick in.
+  #
+  # Example:
+  #
+  # draw unitsquare
+  #     withcolor black; % This line is indented because of `draw`.
+  # x := a + b + c
+  #     + d + e;         % This line is indented because of `:=`.
+  #
+  var i = LastValidMatchEnd(prev_text, MP_STATEMENT, 0)
+  if i >= 0 # Does the line contain a statement?
+    if ValidMatchEnd(prev_text, ';', i) < 0 # Is the statement unterminated?
       return indent(lnum) + shiftwidth()
     else
-      return s:DecreaseIndentOnClosingTag(indent(lnum))
+      return DecreaseIndentOnClosingTag(indent(lnum))
     endif
   endif
 
-  " Deal with the special case of a statement spanning multiple lines. If the
-  " current reference line L ends with a semicolon, search backwards for
-  " another semicolon or a statement keyword. If the latter is found first,
-  " its line is used as the reference line for indenting the current line
-  " instead of L.
-  "
-  "  Example:
-  "
-  "  if cond:
-  "    draw if a: z0 else: z1 fi
-  "        shifted S
-  "        scaled T;      % L
-  "
-  "    for i = 1 upto 3:  % <-- Current line: this gets the same indent as `draw ...`
-  "
-  " NOTE: we get here only if L does not contain a statement (among those
-  " listed in g:mp_statement).
-  if s:ValidMatchEnd(prev_text, ';'.s:eol, 0) >= 0 " L ends with a semicolon
-    let stm_lnum = s:PrevNonBlankNonComment(lnum)
+  # Deal with the special case of a statement spanning multiple lines. If the
+  # current reference line L ends with a semicolon, search backwards for
+  # another semicolon or a statement keyword. If the latter is found first,
+  # its line is used as the reference line for indenting the current line
+  # instead of L.
+  #
+  #  Example:
+  #
+  #  if cond:
+  #    draw if a: z0 else: z1 fi
+  #        shifted S
+  #        scaled T;      % L
+  #
+  #    for i = 1 upto 3:  % <-- Current line: this gets the same indent as `draw ...`
+  #
+  # NOTE: we get here only if L does not contain a statement (among those
+  # listed in g:MP_STATEMENT).
+  if ValidMatchEnd(prev_text, ';' .. EOL, 0) >= 0 # L ends with a semicolon
+    var stm_lnum = PrevNonBlankNonComment(lnum)
     while stm_lnum > 0
-      let prev_text = getline(stm_lnum)
-      let sc_pos = s:LastValidMatchEnd(prev_text, ';', 0)
-      let stm_pos = s:ValidMatchEnd(prev_text, g:mp_statement, sc_pos)
+      prev_text = getline(stm_lnum)
+      var sc_pos = LastValidMatchEnd(prev_text, ';', 0)
+      var stm_pos = ValidMatchEnd(prev_text, MP_STATEMENT, sc_pos)
       if stm_pos > sc_pos
-        let lnum = stm_lnum
+        lnum = stm_lnum
         break
       elseif sc_pos > stm_pos
         break
       endif
-      let stm_lnum = s:PrevNonBlankNonComment(stm_lnum)
+      stm_lnum = PrevNonBlankNonComment(stm_lnum)
     endwhile
   endif
 
-  return s:DecreaseIndentOnClosingTag(indent(lnum))
-endfunction
-" }}}
+  return DecreaseIndentOnClosingTag(indent(lnum))
+enddef
+# }}}
 
-let &cpo = s:keepcpo
-unlet s:keepcpo
-
-" vim:sw=2:fdm=marker
+# vim: sw=2 fdm=marker