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:
Christoph Brandau
2026-09-03 08:39:12 +02:00
parent a0a0d38fe5
commit 53ebc5d055
101 changed files with 13838 additions and 7624 deletions
+10 -1
View File
@@ -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
+102 -9
View File
@@ -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]]:
+11 -7
View File
@@ -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,
}
},