chore(lsprotocol): migrate to 2025.0.0 and cleanup artifacts
The changes align the project with the 2025.0.0 lsprotocol release, removing the old backport and updating type hints in the protocol hooks to use Sequence where appropriate. The dist-info and packaging metadata for older lsprotocol versions are replaced with the new 2025.0.0 artifacts. - Remove exceptiongroup backport used on Python <3.11 - Use Sequence instead of List in LS protocol hooks - Replace old dist-info with 2025.0.0 metadata
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
# file generated by setuptools-scm
|
||||
# file generated by vcs-versioning
|
||||
# don't change, don't track in version control
|
||||
from __future__ import annotations
|
||||
|
||||
__all__ = [
|
||||
"__version__",
|
||||
@@ -10,25 +11,14 @@ __all__ = [
|
||||
"commit_id",
|
||||
]
|
||||
|
||||
TYPE_CHECKING = False
|
||||
if TYPE_CHECKING:
|
||||
from typing import Tuple
|
||||
from typing import Union
|
||||
|
||||
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
||||
COMMIT_ID = Union[str, None]
|
||||
else:
|
||||
VERSION_TUPLE = object
|
||||
COMMIT_ID = object
|
||||
|
||||
version: str
|
||||
__version__: str
|
||||
__version_tuple__: VERSION_TUPLE
|
||||
version_tuple: VERSION_TUPLE
|
||||
commit_id: COMMIT_ID
|
||||
__commit_id__: COMMIT_ID
|
||||
__version_tuple__: tuple[int | str, ...]
|
||||
version_tuple: tuple[int | str, ...]
|
||||
commit_id: str | None
|
||||
__commit_id__: str | None
|
||||
|
||||
__version__ = version = '0.8.0'
|
||||
__version_tuple__ = version_tuple = (0, 8, 0)
|
||||
__version__ = version = '0.9.0'
|
||||
__version_tuple__ = version_tuple = (0, 9, 0)
|
||||
|
||||
__commit_id__ = commit_id = None
|
||||
|
||||
@@ -573,7 +573,16 @@ def _package_ifneeded(args, parser):
|
||||
|
||||
|
||||
def _proc(args, parser):
|
||||
if len(args) != 3:
|
||||
required_args = ["name", "args", "body"]
|
||||
if len(args) < len(required_args):
|
||||
missing_args = ", ".join(required_args[len(args) :])
|
||||
raise CommandArgError(
|
||||
"missing required"
|
||||
f" argument{'s' if len(args) < len(required_args) - 1 else ''} for proc:"
|
||||
f" {missing_args}"
|
||||
)
|
||||
|
||||
if len(args) > len(required_args):
|
||||
raise CommandArgError(f"wrong # of args to proc: got {len(args)}, expected 3")
|
||||
|
||||
# Parse args as list, then iterate over each item to parse arg specifier lists and
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from collections.abc import Callable, Iterable
|
||||
from difflib import get_close_matches
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from tclint.syntax_tree import ArgExpansion, BareWord, BracedWord, Node, QuotedWord
|
||||
@@ -18,6 +19,33 @@ class CommandArgError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def get_suggestion(value: str | None, candidates: Iterable[str]) -> Optional[str]:
|
||||
if value is None:
|
||||
return None
|
||||
|
||||
unique_candidates = sorted({candidate for candidate in candidates if candidate})
|
||||
if value in unique_candidates:
|
||||
unique_candidates.remove(value)
|
||||
|
||||
if not unique_candidates:
|
||||
return None
|
||||
|
||||
cutoff = 0.85 if len(value) <= 3 else 0.75
|
||||
matches = get_close_matches(value, unique_candidates, n=1, cutoff=cutoff)
|
||||
if not matches:
|
||||
return None
|
||||
|
||||
return matches[0]
|
||||
|
||||
|
||||
def _did_you_mean_suffix(value: str | None, candidates: Iterable[str]) -> str:
|
||||
suggestion = get_suggestion(value, candidates)
|
||||
if suggestion is None:
|
||||
return ""
|
||||
|
||||
return f"; did you mean {suggestion}?"
|
||||
|
||||
|
||||
def arg_count(args: list[Node], parser: Parser) -> tuple[int, bool]:
|
||||
"""Returns the number of arguments in args, taking {*} into account.
|
||||
|
||||
@@ -83,6 +111,9 @@ def check_count(command, min=None, max=None):
|
||||
|
||||
|
||||
def eval(args: list[Node], parser: Parser, command: str) -> list[Node]:
|
||||
if len(args) == 1:
|
||||
return [parser.parse_script(args[0])]
|
||||
|
||||
if len(args) > 1 and any(isinstance(arg, (QuotedWord, BracedWord)) for arg in args):
|
||||
# Slightly odd restriction, but our syntax tree doesn't have a great way
|
||||
# to handle this case. We require each command argument to correspond to
|
||||
@@ -174,10 +205,19 @@ def check_arg_spec(
|
||||
mapping = map_positionals(positionals, arg_spec["positionals"], command)
|
||||
args = list(args)
|
||||
for arg_i, map_to_spec in zip(positional_args, mapping):
|
||||
arg = args[arg_i]
|
||||
|
||||
if _positional_has_type("script", arg_spec, map_to_spec):
|
||||
args[arg_i] = parser.parse_script(args[arg_i])
|
||||
args[arg_i] = parser.parse_script(arg)
|
||||
elif _positional_has_type("expression", arg_spec, map_to_spec):
|
||||
args[arg_i] = parser.parse_expression(args[arg_i])
|
||||
args[arg_i] = parser.parse_expression(arg)
|
||||
elif len(map_to_spec) == 1:
|
||||
positional_spec = arg_spec["positionals"][map_to_spec[0]]
|
||||
_validate_value(
|
||||
arg,
|
||||
positional_spec["value"],
|
||||
f"{command} {positional_spec['name']}",
|
||||
)
|
||||
|
||||
return args
|
||||
|
||||
@@ -201,12 +241,18 @@ def dispatch_subcommands(
|
||||
if "" in spec:
|
||||
return check_command(command, args, parser, spec[""])
|
||||
|
||||
valid_subcommands = [name for name in spec.keys() if name != ""]
|
||||
|
||||
if subcommand is not None:
|
||||
msg = f"invalid subcommand for {command}: got {subcommand}"
|
||||
suggestion = _did_you_mean_suffix(subcommand, valid_subcommands)
|
||||
else:
|
||||
msg = f"no subcommand provided for {command}"
|
||||
suggestion = ""
|
||||
|
||||
raise CommandArgError(f"{msg}, expected one of {', '.join(spec.keys())}")
|
||||
raise CommandArgError(
|
||||
f"{msg}, expected one of {', '.join(valid_subcommands)}{suggestion}"
|
||||
)
|
||||
|
||||
|
||||
def map_switches(
|
||||
@@ -250,17 +296,23 @@ def map_switches(
|
||||
continue
|
||||
|
||||
if contents in switches:
|
||||
if contents in mapped and not switches[contents]["repeated"]:
|
||||
switch_spec = switches[contents]
|
||||
|
||||
if contents in mapped and not switch_spec["repeated"]:
|
||||
raise CommandArgError(
|
||||
f"duplicate argument for {command_name}: {contents}"
|
||||
)
|
||||
if switches[contents]["value"]:
|
||||
if switch_spec["value"]:
|
||||
arg_i += 1
|
||||
if arg_i > len(args):
|
||||
expected = _switch_value_description(switch_spec)
|
||||
raise CommandArgError(
|
||||
f"invalid arguments for {command_name}: expected value after"
|
||||
f" {contents}"
|
||||
f"invalid arguments for {command_name}: expected"
|
||||
f" {expected} after {contents}"
|
||||
)
|
||||
_validate_value(
|
||||
args[arg_i - 1], switch_spec["value"], f"{command_name} {contents}"
|
||||
)
|
||||
mapped.add(contents)
|
||||
continue
|
||||
|
||||
@@ -281,11 +333,52 @@ def map_switches(
|
||||
f" {', '.join(prefix_matches)}"
|
||||
)
|
||||
|
||||
raise CommandArgError(f"unrecognized argument for {command_name}: {contents}")
|
||||
raise CommandArgError(
|
||||
f"unrecognized argument for {command_name}: {contents}"
|
||||
f"{_did_you_mean_suffix(contents, switches.keys())}"
|
||||
)
|
||||
|
||||
return mapped, positional_args
|
||||
|
||||
|
||||
def _switch_value_description(switch_spec: dict) -> str:
|
||||
metavar = switch_spec.get("metavar")
|
||||
if metavar is not None:
|
||||
return metavar
|
||||
|
||||
value_spec = switch_spec.get("value")
|
||||
if value_spec is None:
|
||||
return "value"
|
||||
|
||||
value_type = value_spec.get("type")
|
||||
if value_type == "int":
|
||||
return "int value"
|
||||
|
||||
return "value"
|
||||
|
||||
|
||||
def _validate_value(arg: Node, value_spec: dict | None, context: str) -> None:
|
||||
if value_spec is None:
|
||||
return
|
||||
|
||||
contents = arg.contents
|
||||
if contents is None:
|
||||
return
|
||||
|
||||
value_type = value_spec.get("type")
|
||||
if value_type in {"any", "variadic", "script", "expression"}:
|
||||
return
|
||||
|
||||
if value_type == "int":
|
||||
try:
|
||||
int(contents, 0)
|
||||
return
|
||||
except ValueError as error:
|
||||
raise CommandArgError(
|
||||
f"invalid value for {context}: got {contents}, expected {value_type}"
|
||||
) from error
|
||||
|
||||
|
||||
def map_positionals(
|
||||
args: list[Node], spec: list[dict], command_name: str
|
||||
) -> list[list[int]]:
|
||||
|
||||
@@ -2,6 +2,15 @@ from collections.abc import Callable
|
||||
|
||||
from voluptuous import Optional, Or, Schema, Self
|
||||
|
||||
_switch_value = Or({"type": "any"}, {"type": "int"}, None)
|
||||
_positional_value = Or(
|
||||
{"type": "any"},
|
||||
{"type": "int"},
|
||||
{"type": "variadic"},
|
||||
{"type": "script"},
|
||||
{"type": "expression"},
|
||||
)
|
||||
|
||||
# Need to define this as a Schema with required=True to ensure that this requirement
|
||||
# persists through the Or in the main schema definition.
|
||||
_command_args = Schema(
|
||||
@@ -10,19 +19,14 @@ _command_args = Schema(
|
||||
{
|
||||
"name": str,
|
||||
"required": bool,
|
||||
"value": Or(
|
||||
{"type": "any"},
|
||||
{"type": "variadic"},
|
||||
{"type": "script"},
|
||||
{"type": "expression"},
|
||||
),
|
||||
"value": _positional_value,
|
||||
}
|
||||
],
|
||||
Optional("switches", default={}): {
|
||||
Optional(str): {
|
||||
"required": bool,
|
||||
"repeated": bool,
|
||||
"value": Or({"type": "any"}, None),
|
||||
"value": _switch_value,
|
||||
Optional("metavar"): str,
|
||||
}
|
||||
},
|
||||
|
||||
@@ -69,13 +69,15 @@ class Formatter:
|
||||
assert len(debug_char) == 1
|
||||
if space is None:
|
||||
space = self.opts.indent
|
||||
elif isinstance(space, int):
|
||||
space = space * " "
|
||||
if self.opts.debug_whitespace:
|
||||
# Enable this to return a string of debug_char.
|
||||
return len(space) * debug_char
|
||||
return space
|
||||
|
||||
def get_spaces_in_braces(self, space: tuple[int, int]):
|
||||
spaces_in_braces = self.space("A", " ") if self.opts.spaces_in_braces else ""
|
||||
spaces_in_braces = self.space("A", 1) if self.opts.spaces_in_braces else ""
|
||||
if not self.opts.balanced_spaces_in_braces:
|
||||
# No balancing.
|
||||
return spaces_in_braces
|
||||
@@ -88,7 +90,7 @@ class Formatter:
|
||||
if space[0] != -1 and space[1] == -1:
|
||||
# we've got empty braces. Keep "{}" and "{ }" as is, but normalize
|
||||
# more than one space to a single space.
|
||||
return min(space[0], 1) * self.space("B", " ")
|
||||
return min(space[0], 1) * self.space("B", 1)
|
||||
|
||||
# Normalize more than one space to a single space.
|
||||
before = min(space[0], 1)
|
||||
@@ -101,7 +103,7 @@ class Formatter:
|
||||
# Check that we have a balanced expression.
|
||||
assert before == after
|
||||
# Keep either "{1}" or "{ 1 }".
|
||||
return before * self.space("C", " ")
|
||||
return before * self.space("C", 1)
|
||||
|
||||
def _brace(self, lines: list[str], space: tuple[int, int]) -> list[str]:
|
||||
"""Format content between braces.
|
||||
@@ -307,6 +309,10 @@ class Formatter:
|
||||
|
||||
def format_script(self, script: Script, should_indent=True) -> list[str]:
|
||||
lines = self.format_script_contents(script)
|
||||
if not script.braced:
|
||||
# Script came from a non-braced word argument (e.g. plugin called
|
||||
# parse_script on a BareWord). Don't wrap in braces.
|
||||
return lines
|
||||
if script.pos[0] == script.end_pos[0]:
|
||||
space_before = -1
|
||||
space_after = -1
|
||||
@@ -327,7 +333,7 @@ class Formatter:
|
||||
and isinstance(script.children[0], Comment)
|
||||
and script.pos[0] == script.children[0].pos[0]
|
||||
):
|
||||
open_brace += self.space("D", " ") + lines[0]
|
||||
open_brace += self.space("D", 1) + lines[0]
|
||||
lines = lines[1:]
|
||||
|
||||
if should_indent:
|
||||
@@ -353,14 +359,26 @@ class Formatter:
|
||||
child_lines = self.format(child)
|
||||
|
||||
if last_line == child.pos[0]:
|
||||
formatted[-1] += self.space("F", " ")
|
||||
if self.opts.emacs and child_lines[0][-1] == "\\":
|
||||
base_indent = (len(formatted[-1])) * self.space("G", " ")
|
||||
else:
|
||||
base_indent = ""
|
||||
formatted[-1] += self.space("F", 1)
|
||||
base_indent = ""
|
||||
if self.opts.emacs:
|
||||
if child_lines[0][-1] == "\\":
|
||||
base_indent = (len(formatted[-1])) * self.space("G", 1)
|
||||
elif (isinstance(child, BracedExpression)) and child_lines[
|
||||
-1
|
||||
] == "}":
|
||||
child_lines[1:-1] = self._indent(
|
||||
child_lines[1:-1], self.space("V", len(formatted[-1]))
|
||||
)
|
||||
elif (isinstance(child, BracedExpression)) and child_lines[-1][
|
||||
-1
|
||||
] == "}":
|
||||
child_lines[1:] = self._indent(
|
||||
child_lines[1:], self.space("W", len(formatted[-1]))
|
||||
)
|
||||
formatted[-1] += child_lines[0]
|
||||
else:
|
||||
formatted[-1] += self.space("H", " ") + "\\"
|
||||
formatted[-1] += self.space("H", 1) + "\\"
|
||||
formatted.append(self.space("I") + child_lines[0])
|
||||
hanging_indent = True
|
||||
|
||||
@@ -383,13 +401,19 @@ class Formatter:
|
||||
formatted = []
|
||||
contents = self.format_script_contents(command_sub)
|
||||
if len(command_sub.children) > 1 and len(contents) > 1:
|
||||
formatted.append("[")
|
||||
formatted.extend(self._indent(contents, self.space("K")))
|
||||
formatted.append("]")
|
||||
if self.opts.emacs and command_sub.pos[0] == command_sub.children[0].pos[0]:
|
||||
formatted = contents
|
||||
formatted[0] = "[" + formatted[0]
|
||||
formatted[-1] = formatted[-1] + "]"
|
||||
formatted[1:] = self._indent(formatted[1:], self.space("U", 1))
|
||||
else:
|
||||
formatted.append("[")
|
||||
formatted.extend(self._indent(contents, self.space("K")))
|
||||
formatted.append("]")
|
||||
else:
|
||||
formatted.append("[" + contents[0])
|
||||
if self.opts.emacs:
|
||||
indent = self.space("L", " ")
|
||||
indent = self.space("L", 1)
|
||||
else:
|
||||
indent = ""
|
||||
formatted.extend(self._indent(contents[1:], indent))
|
||||
@@ -460,7 +484,7 @@ class Formatter:
|
||||
for child in list_node.children:
|
||||
if last_line is not None:
|
||||
if last_line == child.pos[0]:
|
||||
contents[-1] += self.space("M", " ")
|
||||
contents[-1] += self.space("M", 1)
|
||||
else:
|
||||
newlines = child.pos[0] - last_line
|
||||
newlines = min(newlines, 3)
|
||||
@@ -516,6 +540,29 @@ class Formatter:
|
||||
space_after = expr.end_pos[1] - expr.children[-1].end_pos[1] - 1
|
||||
return self._brace(formatted, (space_before, space_after))
|
||||
|
||||
if self.opts.emacs:
|
||||
pre = []
|
||||
post = []
|
||||
indent_first = 0
|
||||
indent_last = len(formatted)
|
||||
if expr.pos[0] == expr.children[0].pos[0]:
|
||||
formatted[0] = "{" + formatted[0]
|
||||
indent_first = 1
|
||||
else:
|
||||
pre = ["{"]
|
||||
if expr.end_pos[0] == expr.children[-1].end_pos[0]:
|
||||
formatted[-1] = formatted[-1] + "}"
|
||||
else:
|
||||
post = ["}"]
|
||||
formatted = (
|
||||
pre
|
||||
+ formatted[0:indent_first]
|
||||
+ self._indent(formatted[indent_first:indent_last], self.space("X", 1))
|
||||
+ formatted[indent_last:]
|
||||
+ post
|
||||
)
|
||||
return formatted
|
||||
|
||||
return ["{"] + self._indent(formatted, self.space("P")) + ["}"]
|
||||
|
||||
def format_paren_expression(self, expr) -> list[str]:
|
||||
@@ -556,7 +603,7 @@ class Formatter:
|
||||
if last.end_pos[0] != next.pos[0]:
|
||||
formatted.extend(lines)
|
||||
else:
|
||||
formatted[-1] += self.space("R", " ")
|
||||
formatted[-1] += self.space("R", 1)
|
||||
formatted[-1] += lines[0]
|
||||
formatted.extend(lines[1:])
|
||||
last = next
|
||||
@@ -585,7 +632,7 @@ class Formatter:
|
||||
formatted.extend(lines)
|
||||
else:
|
||||
if i > 0:
|
||||
formatted[-1] += self.space("S", " ")
|
||||
formatted[-1] += self.space("S", 1)
|
||||
formatted[-1] += lines[0]
|
||||
formatted.extend(lines[1:])
|
||||
last = child
|
||||
|
||||
@@ -13,6 +13,9 @@ class SymbolTable:
|
||||
def add_proc_definition(self, command: Command) -> None:
|
||||
"""Add definition of procedure"""
|
||||
# command holds the "proc" keyword, so the proc name is 1st argument
|
||||
if len(command.args) == 0:
|
||||
return
|
||||
|
||||
proc_name_node = command.args[0]
|
||||
proc_name = proc_name_node.contents
|
||||
if not proc_name:
|
||||
|
||||
@@ -253,7 +253,7 @@ class Node:
|
||||
class Script(Node):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
# hack for spaces-in-braces check
|
||||
# Used by formatter.
|
||||
self.braced = False
|
||||
|
||||
def accept(self, visitor, recurse=False):
|
||||
|
||||
Reference in New Issue
Block a user