summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 20:06:24 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 20:06:24 +0000
commitf965424faf1c5e6183eaa55bd3e9e080d66cab07 (patch)
treead833dece5839adb32b4fa4b629709a73cf8ca6b /src
parentReleasing progress-linux version 0.1.21-0.0~progress7.99u1. (diff)
downloaddebputy-f965424faf1c5e6183eaa55bd3e9e080d66cab07.tar.xz
debputy-f965424faf1c5e6183eaa55bd3e9e080d66cab07.zip
Merging upstream version 0.1.22.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--src/debputy/commands/debputy_cmd/__main__.py30
-rw-r--r--src/debputy/commands/debputy_cmd/lint_and_lsp_cmds.py6
-rw-r--r--src/debputy/commands/debputy_cmd/plugin_cmds.py16
-rw-r--r--src/debputy/deb_packaging_support.py72
-rw-r--r--src/debputy/debhelper_emulation.py15
-rw-r--r--src/debputy/dh_migration/migration.py4
-rw-r--r--src/debputy/dh_migration/migrators_impl.py31
-rw-r--r--src/debputy/filesystem_scan.py2
-rw-r--r--src/debputy/linting/lint_impl.py4
-rw-r--r--src/debputy/lsp/lsp_debian_changelog.py124
-rw-r--r--src/debputy/lsp/lsp_debian_control.py130
-rw-r--r--src/debputy/lsp/lsp_debian_copyright.py156
-rw-r--r--src/debputy/lsp/lsp_debian_debputy_manifest.py10
-rw-r--r--src/debputy/lsp/lsp_debian_rules.py53
-rw-r--r--src/debputy/lsp/lsp_dispatch.py123
-rw-r--r--src/debputy/lsp/lsp_features.py22
-rw-r--r--src/debputy/lsp/lsp_generic_deb822.py161
-rw-r--r--src/debputy/lsp/quickfixes.py1
-rw-r--r--src/debputy/lsp/spellchecking.py2
-rw-r--r--src/debputy/lsp/vendoring/_deb822_repro/__init__.py2
-rw-r--r--src/debputy/lsp/vendoring/_deb822_repro/locatable.py2
-rw-r--r--src/debputy/lsp/vendoring/_deb822_repro/parsing.py2
-rw-r--r--src/debputy/lsp/vendoring/_deb822_repro/tokens.py2
-rw-r--r--src/debputy/manifest_parser/declarative_parser.py4
-rw-r--r--src/debputy/plugin/api/impl.py8
-rw-r--r--src/debputy/plugin/api/test_api/test_spec.py4
-rw-r--r--src/debputy/plugin/debputy/binary_package_rules.py10
-rw-r--r--src/debputy/plugin/debputy/manifest_root_rules.py10
-rw-r--r--src/debputy/plugin/debputy/private_api.py38
-rw-r--r--src/debputy/transformation_rules.py2
30 files changed, 658 insertions, 388 deletions
diff --git a/src/debputy/commands/debputy_cmd/__main__.py b/src/debputy/commands/debputy_cmd/__main__.py
index d894731..27edf49 100644
--- a/src/debputy/commands/debputy_cmd/__main__.py
+++ b/src/debputy/commands/debputy_cmd/__main__.py
@@ -39,6 +39,7 @@ from debputy.commands.debputy_cmd.context import (
from debputy.commands.debputy_cmd.dc_util import flatten_ppfs
from debputy.commands.debputy_cmd.output import _stream_to_pager
from debputy.dh_migration.migrators import MIGRATORS
+from debputy.dh_migration.migrators_impl import read_dh_addon_sequences
from debputy.exceptions import (
DebputyRuntimeError,
PluginNotFoundError,
@@ -93,8 +94,6 @@ from debputy.dh_migration.models import AcceptableMigrationIssues
from debputy.packages import BinaryPackage
from debputy.debhelper_emulation import (
dhe_pkgdir,
- parse_drules_for_addons,
- extract_dh_addons_from_control,
)
from debputy.deb_packaging_support import (
@@ -1053,19 +1052,6 @@ def _merge_ppfs(
_merge_list(details, "documentation-uris", documentation_uris)
-def _is_debputy_package(context: CommandContext, dh_rules_addons: Set[str]) -> bool:
- drules = context.debian_dir.get("rules")
- sequences = set()
- source_package = context.source_package()
- if drules is not None and not drules.is_dir:
- parse_drules_for_addons(drules, dh_rules_addons)
- extract_dh_addons_from_control(source_package.fields, sequences)
- sequences.update(dh_rules_addons)
- return (
- "debputy" in sequences or "zz-debputy" in sequences or "zz_debputy" in sequences
- )
-
-
def _extract_dh_compat_level() -> Tuple[Optional[int], int]:
try:
output = subprocess.check_output(
@@ -1254,8 +1240,18 @@ def _annotate_debian_directory(context: CommandContext) -> None:
annotated: List[PackagingFileInfo] = []
seen_paths = set()
- drules_sequences = set()
- is_debputy_package = _is_debputy_package(context, drules_sequences)
+ r = read_dh_addon_sequences(context.debian_dir)
+ if r is not None:
+ bd_sequences, dr_sequences = r
+ drules_sequences = bd_sequences | dr_sequences
+ else:
+ drules_sequences = set()
+ is_debputy_package = (
+ "debputy" in drules_sequences
+ or "zz-debputy" in drules_sequences
+ or "zz_debputy" in drules_sequences
+ or "zz-debputy-rrr" in drules_sequences
+ )
dh_compat_level, dh_assistant_exit_code = _extract_dh_compat_level()
dh_issues = []
diff --git a/src/debputy/commands/debputy_cmd/lint_and_lsp_cmds.py b/src/debputy/commands/debputy_cmd/lint_and_lsp_cmds.py
index 0f2ae0f..b30b98d 100644
--- a/src/debputy/commands/debputy_cmd/lint_and_lsp_cmds.py
+++ b/src/debputy/commands/debputy_cmd/lint_and_lsp_cmds.py
@@ -14,6 +14,7 @@ _EDITOR_SNIPPETS = {
;; Add to ~/.emacs or ~/.emacs.d/init.el and then activate via `M-x eglot`.
;;
;; Requires: apt install elpa-dpkg-dev-el
+ ;; Recommends: apt install elpa-markdown-mode
;; Make emacs recognize debian/debputy.manifest as a YAML file
(add-to-list 'auto-mode-alist '("/debian/debputy.manifest\\'" . yaml-mode))
@@ -52,13 +53,14 @@ _EDITOR_SNIPPETS = {
# Inform vim/ycm about the debputy LSP
let g:ycm_language_server = [
\\ { 'name': 'debputy',
- \\ 'filetypes': [ 'debcontrol', 'debcopyright', 'debchangelog', 'make'],
+ \\ 'filetypes': [ 'debcontrol', 'debcopyright', 'debchangelog', 'make', 'yaml'],
\\ 'cmdline': [ 'debputy', 'lsp', 'server' ]
\\ },
\\ ]
packadd! youcompleteme
- nmap <leader>d <plug>(YCMHover)
+ # Add relevant ycm keybinding such as:
+ # nmap <leader>d <plug>(YCMHover)
"""
),
}
diff --git a/src/debputy/commands/debputy_cmd/plugin_cmds.py b/src/debputy/commands/debputy_cmd/plugin_cmds.py
index 3d8bdcb..54acdc5 100644
--- a/src/debputy/commands/debputy_cmd/plugin_cmds.py
+++ b/src/debputy/commands/debputy_cmd/plugin_cmds.py
@@ -437,8 +437,8 @@ def _parser_type_name(v: Union[str, Type[Any]]) -> str:
@plugin_list_cmds.register_subcommand(
- ["plugable-manifest-rules", "p-m-r", "pmr"],
- help_description="Plugable manifest rules (such as install rules)",
+ ["pluggable-manifest-rules", "p-m-r", "pmr"],
+ help_description="Pluggable manifest rules (such as install rules)",
argparser=TEXT_CSV_FORMAT_NO_STABILITY_PROMISE,
)
def _plugin_cmd_list_manifest_rules(context: CommandContext) -> None:
@@ -953,8 +953,8 @@ def _plugin_cmd_show_ppf(context: CommandContext) -> None:
@plugin_show_cmds.register_subcommand(
- ["plugable-manifest-rules", "p-m-r", "pmr"],
- help_description="Plugable manifest rules (such as install rules)",
+ ["pluggable-manifest-rules", "p-m-r", "pmr"],
+ help_description="Pluggable manifest rules (such as install rules)",
argparser=add_arg(
"pmr_rule_name",
metavar="rule-name",
@@ -991,8 +991,8 @@ def _plugin_cmd_show_manifest_rule(context: CommandContext) -> None:
if len(matched) != 1 and (matched or rule_name != "::"):
if not matched:
_error(
- f"Could not find any plugable manifest rule related to {parsed_args.pmr_rule_name}."
- f" Please use `debputy plugin list plugable-manifest-rules` to see the list of rules."
+ f"Could not find any pluggable manifest rule related to {parsed_args.pmr_rule_name}."
+ f" Please use `debputy plugin list pluggable-manifest-rules` to see the list of rules."
)
match_a = matched[0][0]
match_b = matched[1][0]
@@ -1000,7 +1000,7 @@ def _plugin_cmd_show_manifest_rule(context: CommandContext) -> None:
f"The name {rule_name} was ambiguous and matched multiple rule types. Please use"
f" <rule-type>::{rule_name} to clarify which rule to use"
f" (such as {_parser_type_name(match_a)}::{rule_name} or {_parser_type_name(match_b)}::{rule_name})."
- f" Please use `debputy plugin list plugable-manifest-rules` to see the list of rules."
+ f" Please use `debputy plugin list pluggable-manifest-rules` to see the list of rules."
)
if matched:
@@ -1154,7 +1154,7 @@ def _render_discard_rule(
@plugin_show_cmds.register_subcommand(
["automatic-discard-rules", "a-d-r"],
- help_description="Plugable manifest rules (such as install rules)",
+ help_description="Pluggable manifest rules (such as install rules)",
argparser=add_arg(
"discard_rule",
metavar="automatic-discard-rule",
diff --git a/src/debputy/deb_packaging_support.py b/src/debputy/deb_packaging_support.py
index 4cb4e8f..863e394 100644
--- a/src/debputy/deb_packaging_support.py
+++ b/src/debputy/deb_packaging_support.py
@@ -1148,6 +1148,7 @@ def _generate_dbgsym_control_file_if_relevant(
dbgsym_root_dir: str,
dbgsym_ids: str,
multi_arch: Optional[str],
+ dctrl: str,
extra_common_params: Sequence[str],
) -> None:
section = binary_package.archive_section
@@ -1170,13 +1171,18 @@ def _generate_dbgsym_control_file_if_relevant(
extra_params.append(f"-VInstalled-Size={total_size}")
extra_params.extend(extra_common_params)
- package = binary_package.name
+ package = (
+ binary_package.name
+ if dctrl == "debian/control"
+ else f"{binary_package.name}-dbgsym"
+ )
dpkg_cmd = [
"dpkg-gencontrol",
f"-p{package}",
# FIXME: Support d/<pkg>.changelog at some point.
"-ldebian/changelog",
"-T/dev/null",
+ f"-c{dctrl}",
f"-P{dbgsym_root_dir}",
f"-DPackage={package}-dbgsym",
"-DDepends=" + package + " (= ${binary:Version})",
@@ -1339,14 +1345,22 @@ def dpkg_field_list_pkg_dep() -> Sequence[str]:
def _handle_relationship_substvars(
source: SourcePackage,
- dctrl: BinaryPackage,
+ dctrl_file: BinaryPackage,
substvars: FlushableSubstvars,
+ has_dbgsym: bool,
) -> Optional[str]:
relationship_fields = dpkg_field_list_pkg_dep()
relationship_fields_lc = frozenset(x.lower() for x in relationship_fields)
substvar_fields = collections.defaultdict(list)
+ needs_dbgsym_stanza = False
for substvar_name, substvar in substvars.as_substvar.items():
- if substvar.assignment_operator == "$=" or ":" not in substvar_name:
+ if ":" not in substvar_name:
+ continue
+ if substvar.assignment_operator in ("$=", "!="):
+ # Will create incorrect results if there is a dbgsym and we do nothing
+ needs_dbgsym_stanza = True
+
+ if substvar.assignment_operator == "$=":
# Automatically handled; no need for manual merging.
continue
_, field = substvar_name.rsplit(":", 1)
@@ -1354,10 +1368,14 @@ def _handle_relationship_substvars(
if field_lc not in relationship_fields_lc:
continue
substvar_fields[field_lc].append("${" + substvar_name + "}")
- if not substvar_fields:
+
+ if not has_dbgsym:
+ needs_dbgsym_stanza = False
+
+ if not substvar_fields and not needs_dbgsym_stanza:
return None
- replacement_stanza = debian.deb822.Deb822(dctrl.fields)
+ replacement_stanza = debian.deb822.Deb822(dctrl_file.fields)
for field_name in relationship_fields:
field_name_lc = field_name.lower()
@@ -1375,7 +1393,7 @@ def _handle_relationship_substvars(
final_value = f"{existing_value}, {substvars_part}"
replacement_stanza[field_name] = final_value
- tmpdir = generated_content_dir(package=dctrl)
+ tmpdir = generated_content_dir(package=dctrl_file)
with tempfile.NamedTemporaryFile(
mode="wb",
dir=tmpdir,
@@ -1388,6 +1406,17 @@ def _handle_relationship_substvars(
debian.deb822.Deb822(source.fields).dump(fd)
fd.write(b"\n")
replacement_stanza.dump(fd)
+
+ if has_dbgsym:
+ # Minimal stanza to avoid substvars warnings. Most fields are still set
+ # via -D.
+ dbgsym_stanza = Deb822()
+ dbgsym_stanza["Package"] = f"{dctrl_file.name}-dbgsym"
+ dbgsym_stanza["Architecture"] = dctrl_file.fields["Architecture"]
+ dbgsym_stanza["Description"] = f"debug symbols for {dctrl_file.name}"
+ fd.write(b"\n")
+ dbgsym_stanza.dump(fd)
+
return fd.name
@@ -1424,9 +1453,11 @@ def _generate_control_files(
' accordingly in the binary. If this auto-correction is wrong, please add "Multi-Arch: no" to the'
' relevant part of "debian/control" to disable this feature.'
)
- extra_params_specific.append(f"-DMulti-Arch={ma_value}")
+ # We want this to apply to the `-dbgsym` package as well to avoid
+ # lintian `debug-package-for-multi-arch-same-pkg-not-coinstallable`
+ extra_common_params.append(f"-DMulti-Arch={ma_value}")
elif ma_value == "no":
- extra_params_specific.append("-UMulti-Arch")
+ extra_common_params.append("-UMulti-Arch")
dbgsym_root_dir = dhe_dbgsym_root_dir(binary_package)
dbgsym_ids = " ".join(dbgsym_build_ids) if dbgsym_build_ids else ""
@@ -1436,15 +1467,26 @@ def _generate_control_files(
_t64_migration_substvar(binary_package, control_output_dir, substvars)
with substvars.flush() as flushed_substvars:
- if dbgsym_root_fs is not None and any(
+ has_dbgsym = dbgsym_root_fs is not None and any(
f for f in dbgsym_root_fs.all_paths() if f.is_file
- ):
+ )
+ dctrl_file = _handle_relationship_substvars(
+ source_package,
+ binary_package,
+ substvars,
+ has_dbgsym,
+ )
+ if dctrl_file is None:
+ dctrl_file = "debian/control"
+
+ if has_dbgsym:
_generate_dbgsym_control_file_if_relevant(
binary_package,
dbgsym_root_fs,
dbgsym_root_dir,
dbgsym_ids,
ma_value,
+ dctrl_file,
extra_common_params,
)
generate_md5sums_file(
@@ -1454,21 +1496,13 @@ def _generate_control_files(
elif dbgsym_ids:
extra_common_params.append(f"-DBuild-Ids={dbgsym_ids}")
- dctrl = _handle_relationship_substvars(
- source_package,
- binary_package,
- substvars,
- )
- if dctrl is None:
- dctrl = "debian/control"
-
ctrl_file = os.path.join(control_output_dir, "control")
dpkg_cmd = [
"dpkg-gencontrol",
f"-p{package}",
# FIXME: Support d/<pkg>.changelog at some point.
"-ldebian/changelog",
- f"-c{dctrl}",
+ f"-c{dctrl_file}",
f"-T{flushed_substvars}",
f"-O{ctrl_file}",
f"-P{control_output_dir}",
diff --git a/src/debputy/debhelper_emulation.py b/src/debputy/debhelper_emulation.py
index 88352bd..38d9a15 100644
--- a/src/debputy/debhelper_emulation.py
+++ b/src/debputy/debhelper_emulation.py
@@ -241,14 +241,13 @@ _FIND_DH_WITH = re.compile(r"--with(?:\s+|=)(\S+)")
_DEP_REGEX = re.compile("^([a-z0-9][-+.a-z0-9]+)", re.ASCII)
-def parse_drules_for_addons(debian_rules: VirtualPath, sequences: Set[str]) -> None:
- with debian_rules.open() as fd:
- for line in fd:
- if not line.startswith("\tdh "):
- continue
- for match in _FIND_DH_WITH.finditer(line):
- sequence_def = match.group(1)
- sequences.update(sequence_def.split(","))
+def parse_drules_for_addons(lines: Iterable[str], sequences: Set[str]) -> None:
+ for line in lines:
+ if not line.startswith("\tdh "):
+ continue
+ for match in _FIND_DH_WITH.finditer(line):
+ sequence_def = match.group(1)
+ sequences.update(sequence_def.split(","))
def extract_dh_addons_from_control(
diff --git a/src/debputy/dh_migration/migration.py b/src/debputy/dh_migration/migration.py
index 1366f22..bcdd3f9 100644
--- a/src/debputy/dh_migration/migration.py
+++ b/src/debputy/dh_migration/migration.py
@@ -170,7 +170,9 @@ def _check_migration_target(
f'Using "{resolved_migration_target}" as migration target based on the packaging'
)
else:
- _info(f'Using "{resolved_migration_target}" as default migration target.')
+ _info(
+ f'Using "{resolved_migration_target}" as default migration target. Use --migration-target to choose!'
+ )
return resolved_migration_target
diff --git a/src/debputy/dh_migration/migrators_impl.py b/src/debputy/dh_migration/migrators_impl.py
index 6613c25..7856d27 100644
--- a/src/debputy/dh_migration/migrators_impl.py
+++ b/src/debputy/dh_migration/migrators_impl.py
@@ -1,5 +1,6 @@
import collections
import dataclasses
+import functools
import json
import os
import re
@@ -675,16 +676,15 @@ def migrate_install_file(
current_sources.extend(sources)
continue
key = (dest_dir, dhe_line.conditional_key())
+ ctor = functools.partial(
+ SourcesAndConditional,
+ dest_dir=dest_dir,
+ conditional=dhe_line.conditional(),
+ )
md = _fetch_or_create(
sources_by_destdir,
key,
- # Use named parameters to avoid warnings about the values possible changing
- # in the next iteration. We always resolve the lambda in this iteration, so
- # the bug is non-existent. However, that is harder for a linter to prove.
- lambda *, dest=dest_dir, dhe=dhe_line: SourcesAndConditional(
- dest_dir=dest,
- conditional=dhe.conditional(),
- ),
+ ctor,
)
md.sources.extend(sources)
@@ -908,10 +908,13 @@ def migrate_installinfo_file(
info_files_by_condition: Dict[Tuple[str, ...], InfoFilesDefinition] = {}
for dhe_line in content:
key = dhe_line.conditional_key()
+ ctr = functools.partial(
+ InfoFilesDefinition, conditional=dhe_line.conditional()
+ )
info_def = _fetch_or_create(
info_files_by_condition,
key,
- lambda: InfoFilesDefinition(conditional=dhe_line.conditional()),
+ ctr,
)
info_def.sources.extend(
_normalize_path(w, with_prefix=False) for w in dhe_line.tokens
@@ -1023,12 +1026,15 @@ def migrate_installman_file(
else:
language = None
key = (language, dhe_line.conditional_key())
+ ctor = functools.partial(
+ ManpageDefinition,
+ language=language,
+ conditional=dhe_line.conditional(),
+ )
manpage_def = _fetch_or_create(
complex_definitions,
key,
- lambda: ManpageDefinition(
- language=language, conditional=dhe_line.conditional()
- ),
+ ctor,
)
manpage_def.sources.extend(sources)
else:
@@ -1483,7 +1489,8 @@ def read_dh_addon_sequences(
drules = debian_dir.get("rules")
if drules and drules.is_file:
- parse_drules_for_addons(drules, dr_sequences)
+ with drules.open() as fd:
+ parse_drules_for_addons(fd, dr_sequences)
with ctrl_file.open() as fd:
ctrl = list(Deb822.iter_paragraphs(fd))
diff --git a/src/debputy/filesystem_scan.py b/src/debputy/filesystem_scan.py
index f7f97c2..dec123c 100644
--- a/src/debputy/filesystem_scan.py
+++ b/src/debputy/filesystem_scan.py
@@ -325,7 +325,7 @@ class VirtualPathBase(VirtualPath, ABC):
if current.path in link_expansions:
# This is our loop detection for now. It might have some false positives where you
# could safely resolve the same symlink twice. However, given that this use-case is
- # basically none existent in practice for packaging, we just stop here for now.
+ # basically non-existent in practice for packaging, we just stop here for now.
raise SymlinkLoopError(
f'The path "{path}" traversed the symlink "{current.path}" multiple'
" times. Currently, traversing the same symlink twice is considered"
diff --git a/src/debputy/linting/lint_impl.py b/src/debputy/linting/lint_impl.py
index 68be9d9..7248022 100644
--- a/src/debputy/linting/lint_impl.py
+++ b/src/debputy/linting/lint_impl.py
@@ -26,7 +26,7 @@ from debputy.lsp.lsp_debian_changelog import _lint_debian_changelog
from debputy.lsp.lsp_debian_control import _lint_debian_control
from debputy.lsp.lsp_debian_copyright import _lint_debian_copyright
from debputy.lsp.lsp_debian_debputy_manifest import _lint_debian_debputy_manifest
-from debputy.lsp.lsp_debian_rules import _lint_debian_rules
+from debputy.lsp.lsp_debian_rules import _lint_debian_rules_impl
from debputy.lsp.quickfixes import provide_standard_quickfixes_from_diagnostics
from debputy.lsp.spellchecking import disable_spellchecking
from debputy.lsp.text_edit import (
@@ -40,7 +40,7 @@ LINTER_FORMATS = {
"debian/control": _lint_debian_control,
"debian/copyright": _lint_debian_copyright,
"debian/changelog": _lint_debian_changelog,
- "debian/rules": _lint_debian_rules,
+ "debian/rules": _lint_debian_rules_impl,
"debian/debputy.manifest": _lint_debian_debputy_manifest,
}
diff --git a/src/debputy/lsp/lsp_debian_changelog.py b/src/debputy/lsp/lsp_debian_changelog.py
index 3ec0b4d..77df145 100644
--- a/src/debputy/lsp/lsp_debian_changelog.py
+++ b/src/debputy/lsp/lsp_debian_changelog.py
@@ -1,4 +1,5 @@
import sys
+from email.utils import parsedate_to_datetime
from typing import (
Union,
List,
@@ -26,6 +27,7 @@ from lsprotocol.types import (
from debputy.lsp.lsp_features import lsp_diagnostics, lsp_standard_handler
from debputy.lsp.quickfixes import (
provide_standard_quickfixes_from_diagnostics,
+ propose_correct_text_quick_fix,
)
from debputy.lsp.spellchecking import spellcheck_line
from debputy.lsp.text_util import (
@@ -52,6 +54,17 @@ _LANGUAGE_IDS = [
"debchangelog",
]
+_WEEKDAYS_BY_IDX = [
+ "Mon",
+ "Tue",
+ "Wed",
+ "Thu",
+ "Fri",
+ "Sat",
+ "Sun",
+]
+_KNOWN_WEEK_DAYS = frozenset(_WEEKDAYS_BY_IDX)
+
DOCUMENT_VERSION_TABLE: Dict[str, int] = {}
@@ -106,6 +119,114 @@ def _diagnostics_debian_changelog(
yield from scanner
+def _check_footer_line(
+ line: str,
+ line_no: int,
+ lines: List[str],
+ position_codec: LintCapablePositionCodec,
+) -> Iterator[Diagnostic]:
+ try:
+ end_email_idx = line.rindex("> ")
+ except ValueError:
+ # Syntax error; flag later
+ return
+ line_len = len(line)
+ start_date_idx = end_email_idx + 3
+ # 3 characters for the day name (Mon), then a comma plus a space followed by the
+ # actual date. The 6 characters limit is a gross under estimation of the real
+ # size.
+ if line_len < start_date_idx + 6:
+ range_server_units = Range(
+ Position(
+ line_no,
+ start_date_idx,
+ ),
+ Position(
+ line_no,
+ line_len,
+ ),
+ )
+ yield Diagnostic(
+ position_codec.range_to_client_units(lines, range_server_units),
+ "Expected a date in RFC822 format (Tue, 12 Mar 2024 12:34:56 +0000)",
+ severity=DiagnosticSeverity.Error,
+ source="debputy",
+ )
+ return
+ day_name_range_server_units = Range(
+ Position(
+ line_no,
+ start_date_idx,
+ ),
+ Position(
+ line_no,
+ start_date_idx + 3,
+ ),
+ )
+ day_name = line[start_date_idx : start_date_idx + 3]
+ if day_name not in _KNOWN_WEEK_DAYS:
+ yield Diagnostic(
+ position_codec.range_to_client_units(lines, day_name_range_server_units),
+ "Expected a three letter date here (Mon, Tue, ..., Sun).",
+ severity=DiagnosticSeverity.Error,
+ source="debputy",
+ )
+ return
+
+ date_str = line[start_date_idx + 5 :]
+
+ if line[start_date_idx + 3 : start_date_idx + 5] != ", ":
+ sep = line[start_date_idx + 3 : start_date_idx + 5]
+ range_server_units = Range(
+ Position(
+ line_no,
+ start_date_idx + 3,
+ ),
+ Position(
+ line_no,
+ start_date_idx + 4,
+ ),
+ )
+ yield Diagnostic(
+ position_codec.range_to_client_units(lines, range_server_units),
+ f'Improper formatting of date. Expected ", " here, not "{sep}"',
+ severity=DiagnosticSeverity.Error,
+ source="debputy",
+ )
+ return
+
+ try:
+ # FIXME: this parser is too forgiving (it ignores trailing garbage)
+ date = parsedate_to_datetime(date_str)
+ except ValueError as e:
+ range_server_units = Range(
+ Position(
+ line_no,
+ start_date_idx + 5,
+ ),
+ Position(
+ line_no,
+ line_len,
+ ),
+ )
+ yield Diagnostic(
+ position_codec.range_to_client_units(lines, range_server_units),
+ f"Unable to the date as a valid RFC822 date: {e.args[0]}",
+ severity=DiagnosticSeverity.Error,
+ source="debputy",
+ )
+ return
+ expected_week_day = _WEEKDAYS_BY_IDX[date.weekday()]
+ if expected_week_day != day_name:
+ yield Diagnostic(
+ position_codec.range_to_client_units(lines, day_name_range_server_units),
+ f"The date was a {expected_week_day}day.",
+ severity=DiagnosticSeverity.Warning,
+ source="debputy",
+ data=[propose_correct_text_quick_fix(expected_week_day)],
+ )
+
+
def _scan_debian_changelog_for_diagnostics(
lines: List[str],
position_codec: LintCapablePositionCodec,
@@ -123,6 +244,9 @@ def _scan_debian_changelog_for_diagnostics(
line = line.rstrip()
if not line:
continue
+ if line.startswith(" --"):
+ diagnostics.extend(_check_footer_line(line, line_no, lines, position_codec))
+ continue
if not line.startswith(" "):
continue
# minus 1 for newline
diff --git a/src/debputy/lsp/lsp_debian_control.py b/src/debputy/lsp/lsp_debian_control.py
index d00f1c2..f73612f 100644
--- a/src/debputy/lsp/lsp_debian_control.py
+++ b/src/debputy/lsp/lsp_debian_control.py
@@ -9,20 +9,6 @@ from typing import (
List,
)
-from debputy.lsp.vendoring._deb822_repro import (
- parse_deb822_file,
- Deb822FileElement,
- Deb822ParagraphElement,
-)
-from debputy.lsp.vendoring._deb822_repro.parsing import (
- Deb822KeyValuePairElement,
- LIST_SPACE_SEPARATED_INTERPRETATION,
-)
-from debputy.lsp.vendoring._deb822_repro.tokens import (
- Deb822Token,
- tokenize_deb822_file,
- Deb822FieldNameToken,
-)
from lsprotocol.types import (
DiagnosticSeverity,
Range,
@@ -30,7 +16,6 @@ from lsprotocol.types import (
Position,
DidOpenTextDocumentParams,
DidChangeTextDocumentParams,
- FoldingRangeKind,
FoldingRange,
FoldingRangeParams,
CompletionItem,
@@ -38,7 +23,6 @@ from lsprotocol.types import (
CompletionParams,
TEXT_DOCUMENT_DID_OPEN,
TEXT_DOCUMENT_DID_CHANGE,
- TEXT_DOCUMENT_FOLDING_RANGE,
TEXT_DOCUMENT_COMPLETION,
TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL,
DiagnosticRelatedInformation,
@@ -58,7 +42,6 @@ from debputy.lsp.lsp_debian_control_reference_data import (
DctrlKnownField,
BINARY_FIELDS,
SOURCE_FIELDS,
- FieldValueClass,
DctrlFileMetadata,
)
from debputy.lsp.lsp_features import (
@@ -66,8 +49,15 @@ from debputy.lsp.lsp_features import (
lsp_completer,
lsp_hover,
lsp_standard_handler,
+ lsp_folding_ranges,
+ lsp_semantic_tokens_full,
+)
+from debputy.lsp.lsp_generic_deb822 import (
+ deb822_completer,
+ deb822_hover,
+ deb822_folding_ranges,
+ deb822_semantic_tokens_full,
)
-from debputy.lsp.lsp_generic_deb822 import deb822_completer, deb822_hover
from debputy.lsp.quickfixes import (
propose_remove_line_quick_fix,
range_compatible_with_remove_line_fix,
@@ -82,6 +72,19 @@ from debputy.lsp.text_util import (
detect_possible_typo,
te_range_to_lsp,
)
+from debputy.lsp.vendoring._deb822_repro import (
+ parse_deb822_file,
+ Deb822FileElement,
+ Deb822ParagraphElement,
+)
+from debputy.lsp.vendoring._deb822_repro.parsing import (
+ Deb822KeyValuePairElement,
+ LIST_SPACE_SEPARATED_INTERPRETATION,
+)
+from debputy.lsp.vendoring._deb822_repro.tokens import (
+ Deb822Token,
+ Deb822FieldNameToken,
+)
from debputy.util import _info, _error
try:
@@ -106,33 +109,9 @@ _LANGUAGE_IDS = [
]
-SEMANTIC_TOKENS_LEGEND = SemanticTokensLegend(
- token_types=["keyword"],
- token_modifiers=[],
-)
_DCTRL_FILE_METADATA = DctrlFileMetadata()
-def register_dctrl_lsp(ls: "LanguageServer") -> None:
- try:
- from debputy.lsp.vendoring._deb822_repro.locatable import Locatable
- except ImportError:
- _error(
- 'Sorry; this feature requires a newer version of python-debian (with "Locatable").'
- )
-
- ls.feature(TEXT_DOCUMENT_DID_OPEN)(_diagnostics_debian_control)
- ls.feature(TEXT_DOCUMENT_DID_CHANGE)(_diagnostics_debian_control)
- ls.feature(TEXT_DOCUMENT_FOLDING_RANGE)(_detect_folding_ranges_debian_control)
- ls.feature(TEXT_DOCUMENT_COMPLETION)(_debian_control_completions)
- ls.feature(TEXT_DOCUMENT_CODE_ACTION)(provide_standard_quickfixes_from_diagnostics)
- ls.feature(TEXT_DOCUMENT_HOVER)(_debian_control_hover)
- ls.feature(TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL)(on_save_trim_end_of_line_whitespace)
- ls.feature(TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL, SEMANTIC_TOKENS_LEGEND)(
- _handle_semantic_tokens_full
- )
-
-
lsp_standard_handler(_LANGUAGE_IDS, TEXT_DOCUMENT_CODE_ACTION)
lsp_standard_handler(_LANGUAGE_IDS, TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL)
@@ -153,36 +132,12 @@ def _debian_control_completions(
return deb822_completer(ls, params, _DCTRL_FILE_METADATA)
-def _detect_folding_ranges_debian_control(
+@lsp_folding_ranges(_LANGUAGE_IDS)
+def _debian_control_folding_ranges(
ls: "LanguageServer",
params: FoldingRangeParams,
) -> Optional[Sequence[FoldingRange]]:
- doc = ls.workspace.get_text_document(params.text_document.uri)
- comment_start = -1
- folding_ranges = []
- for (
- token,
- start_line,
- start_offset,
- end_line,
- end_offset,
- ) in _deb822_token_iter(tokenize_deb822_file(doc.lines)):
- if token.is_comment:
- if comment_start < 0:
- comment_start = start_line
- _info(f"Detected new comment: {start_line}")
- elif comment_start > -1:
- comment_start = -1
- folding_range = FoldingRange(
- comment_start,
- end_line,
- kind=FoldingRangeKind.Comment,
- )
-
- folding_ranges.append(folding_range)
- _info(f"Detected folding range: {folding_range}")
-
- return folding_ranges
+ return deb822_folding_ranges(ls, params, _DCTRL_FILE_METADATA)
def _deb822_token_iter(
@@ -760,38 +715,13 @@ def _lint_debian_control(
return diagnostics
-def _handle_semantic_tokens_full(
+@lsp_semantic_tokens_full(_LANGUAGE_IDS)
+def _semantic_tokens_full(
ls: "LanguageServer",
request: SemanticTokensParams,
) -> Optional[SemanticTokens]:
- doc = ls.workspace.get_text_document(request.text_document.uri)
- lines = doc.lines
- deb822_file = parse_deb822_file(
- lines,
- accept_files_with_duplicated_fields=True,
- accept_files_with_error_tokens=True,
+ return deb822_semantic_tokens_full(
+ ls,
+ request,
+ _DCTRL_FILE_METADATA,
)
- tokens = []
- previous_line = 0
- keyword_token = 0
- no_modifiers = 0
-
- for paragraph_no, paragraph in enumerate(deb822_file, start=1):
- paragraph_position = paragraph.position_in_file()
- for kvpair in paragraph.iter_parts_of_type(Deb822KeyValuePairElement):
- field_position_without_comments = kvpair.position_in_parent().relative_to(
- paragraph_position
- )
- field_size = doc.position_codec.client_num_units(kvpair.field_name)
- current_line = field_position_without_comments.line_position
- line_delta = current_line - previous_line
- previous_line = current_line
- tokens.append(line_delta) # Line delta
- tokens.append(0) # Token delta
- tokens.append(field_size) # Token length
- tokens.append(keyword_token)
- tokens.append(no_modifiers)
-
- if not tokens:
- return None
- return SemanticTokens(tokens)
diff --git a/src/debputy/lsp/lsp_debian_copyright.py b/src/debputy/lsp/lsp_debian_copyright.py
index 052654a..f22bd0b 100644
--- a/src/debputy/lsp/lsp_debian_copyright.py
+++ b/src/debputy/lsp/lsp_debian_copyright.py
@@ -10,36 +10,16 @@ from typing import (
List,
)
-from debputy.lsp.vendoring._deb822_repro import (
- parse_deb822_file,
- Deb822FileElement,
- Deb822ParagraphElement,
-)
-from debputy.lsp.vendoring._deb822_repro.parsing import (
- Deb822KeyValuePairElement,
- LIST_SPACE_SEPARATED_INTERPRETATION,
-)
-from debputy.lsp.vendoring._deb822_repro.tokens import (
- Deb822Token,
- tokenize_deb822_file,
- Deb822FieldNameToken,
-)
from lsprotocol.types import (
DiagnosticSeverity,
Range,
Diagnostic,
Position,
- DidOpenTextDocumentParams,
- DidChangeTextDocumentParams,
- FoldingRangeKind,
- FoldingRange,
- FoldingRangeParams,
CompletionItem,
CompletionList,
CompletionParams,
TEXT_DOCUMENT_DID_OPEN,
TEXT_DOCUMENT_DID_CHANGE,
- TEXT_DOCUMENT_FOLDING_RANGE,
TEXT_DOCUMENT_COMPLETION,
TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL,
DiagnosticRelatedInformation,
@@ -53,10 +33,11 @@ from lsprotocol.types import (
TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL,
SemanticTokens,
SemanticTokensParams,
+ FoldingRangeParams,
+ FoldingRange,
)
from debputy.lsp.lsp_debian_control_reference_data import (
- FieldValueClass,
_DEP5_HEADER_FIELDS,
_DEP5_FILES_FIELDS,
Deb822KnownField,
@@ -68,8 +49,15 @@ from debputy.lsp.lsp_features import (
lsp_completer,
lsp_hover,
lsp_standard_handler,
+ lsp_folding_ranges,
+ lsp_semantic_tokens_full,
+)
+from debputy.lsp.lsp_generic_deb822 import (
+ deb822_completer,
+ deb822_hover,
+ deb822_folding_ranges,
+ deb822_semantic_tokens_full,
)
-from debputy.lsp.lsp_generic_deb822 import deb822_completer, deb822_hover
from debputy.lsp.quickfixes import (
propose_remove_line_quick_fix,
propose_correct_text_quick_fix,
@@ -83,7 +71,20 @@ from debputy.lsp.text_util import (
detect_possible_typo,
te_range_to_lsp,
)
-from debputy.util import _info, _error
+from debputy.lsp.vendoring._deb822_repro import (
+ parse_deb822_file,
+ Deb822FileElement,
+ Deb822ParagraphElement,
+)
+from debputy.lsp.vendoring._deb822_repro.parsing import (
+ Deb822KeyValuePairElement,
+ LIST_SPACE_SEPARATED_INTERPRETATION,
+)
+from debputy.lsp.vendoring._deb822_repro.tokens import (
+ Deb822Token,
+ Deb822FieldNameToken,
+)
+from debputy.util import _error
try:
from debputy.lsp.vendoring._deb822_repro.locatable import (
@@ -109,32 +110,6 @@ _LANGUAGE_IDS = [
_DEP5_FILE_METADATA = Dep5FileMetadata()
-SEMANTIC_TOKENS_LEGEND = SemanticTokensLegend(
- token_types=["keyword"],
- token_modifiers=[],
-)
-
-
-def register_dcpy_lsp(ls: "LanguageServer") -> None:
- try:
- from debian._deb822_repro.locatable import Locatable
- except ImportError:
- _error(
- 'Sorry; this feature requires a newer version of python-debian (with "Locatable").'
- )
-
- ls.feature(TEXT_DOCUMENT_DID_OPEN)(_diagnostics_debian_copyright)
- ls.feature(TEXT_DOCUMENT_DID_CHANGE)(_diagnostics_debian_copyright)
- ls.feature(TEXT_DOCUMENT_FOLDING_RANGE)(_detect_folding_ranges_debian_copyright)
- ls.feature(TEXT_DOCUMENT_COMPLETION)(_debian_copyright_completions)
- ls.feature(TEXT_DOCUMENT_CODE_ACTION)(provide_standard_quickfixes_from_diagnostics)
- ls.feature(TEXT_DOCUMENT_HOVER)(_debian_copyright_hover)
- ls.feature(TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL)(on_save_trim_end_of_line_whitespace)
- ls.feature(TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL, SEMANTIC_TOKENS_LEGEND)(
- _handle_semantic_tokens_full
- )
-
-
lsp_standard_handler(_LANGUAGE_IDS, TEXT_DOCUMENT_CODE_ACTION)
lsp_standard_handler(_LANGUAGE_IDS, TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL)
@@ -155,36 +130,12 @@ def _debian_copyright_completions(
return deb822_completer(ls, params, _DEP5_FILE_METADATA)
-def _detect_folding_ranges_debian_copyright(
+@lsp_folding_ranges(_LANGUAGE_IDS)
+def _debian_copyright_folding_ranges(
ls: "LanguageServer",
params: FoldingRangeParams,
) -> Optional[Sequence[FoldingRange]]:
- doc = ls.workspace.get_text_document(params.text_document.uri)
- comment_start = -1
- folding_ranges = []
- for (
- token,
- start_line,
- start_offset,
- end_line,
- end_offset,
- ) in _deb822_token_iter(tokenize_deb822_file(doc.lines)):
- if token.is_comment:
- if comment_start < 0:
- comment_start = start_line
- _info(f"Detected new comment: {start_line}")
- elif comment_start > -1:
- comment_start = -1
- folding_range = FoldingRange(
- comment_start,
- end_line,
- kind=FoldingRangeKind.Comment,
- )
-
- folding_ranges.append(folding_range)
- _info(f"Detected folding range: {folding_range}")
-
- return folding_ranges
+ return deb822_folding_ranges(ls, params, _DEP5_FILE_METADATA)
def _deb822_token_iter(
@@ -576,22 +527,6 @@ def _scan_for_syntax_errors_and_token_level_diagnostics(
return first_error
-def _diagnostics_debian_copyright(
- ls: "LanguageServer",
- params: Union[DidOpenTextDocumentParams, DidChangeTextDocumentParams],
-) -> None:
- doc = ls.workspace.get_text_document(params.text_document.uri)
- _info(f"Opened document: {doc.path} ({doc.language_id})")
- lines = doc.lines
- position_codec: LintCapablePositionCodec = doc.position_codec
-
- diagnostics = _lint_debian_copyright(doc.uri, doc.path, lines, position_codec)
- ls.publish_diagnostics(
- doc.uri,
- diagnostics,
- )
-
-
@lint_diagnostics(_LANGUAGE_IDS)
def _lint_debian_copyright(
doc_reference: str,
@@ -648,38 +583,13 @@ def _lint_debian_copyright(
return diagnostics
-def _handle_semantic_tokens_full(
+@lsp_semantic_tokens_full(_LANGUAGE_IDS)
+def _semantic_tokens_full(
ls: "LanguageServer",
request: SemanticTokensParams,
) -> Optional[SemanticTokens]:
- doc = ls.workspace.get_text_document(request.text_document.uri)
- lines = doc.lines
- deb822_file = parse_deb822_file(
- lines,
- accept_files_with_duplicated_fields=True,
- accept_files_with_error_tokens=True,
+ return deb822_semantic_tokens_full(
+ ls,
+ request,
+ _DEP5_FILE_METADATA,
)
- tokens = []
- previous_line = 0
- keyword_token = 0
- no_modifiers = 0
-
- for paragraph_no, paragraph in enumerate(deb822_file, start=1):
- paragraph_position = paragraph.position_in_file()
- for kvpair in paragraph.iter_parts_of_type(Deb822KeyValuePairElement):
- field_position_without_comments = kvpair.position_in_parent().relative_to(
- paragraph_position
- )
- field_size = doc.position_codec.client_num_units(kvpair.field_name)
- current_line = field_position_without_comments.line_position
- line_delta = current_line - previous_line
- previous_line = current_line
- tokens.append(line_delta) # Line delta
- tokens.append(0) # Token delta
- tokens.append(field_size) # Token length
- tokens.append(keyword_token)
- tokens.append(no_modifiers)
-
- if not tokens:
- return None
- return SemanticTokens(tokens)
diff --git a/src/debputy/lsp/lsp_debian_debputy_manifest.py b/src/debputy/lsp/lsp_debian_debputy_manifest.py
index 2f9920e..97fffcc 100644
--- a/src/debputy/lsp/lsp_debian_debputy_manifest.py
+++ b/src/debputy/lsp/lsp_debian_debputy_manifest.py
@@ -45,6 +45,12 @@ _LANGUAGE_IDS = [
lsp_standard_handler(_LANGUAGE_IDS, TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL)
+def is_valid_file(path: str) -> bool:
+ # For debian/debputy.manifest, the language ID is often set to makefile meaning we get random
+ # "non-debian/debputy.manifest" YAML files here. Skip those.
+ return path.endswith("debian/debputy.manifest")
+
+
def _word_range_at_position(
lines: List[str],
line_no: int,
@@ -69,10 +75,12 @@ def _word_range_at_position(
@lint_diagnostics(_LANGUAGE_IDS)
def _lint_debian_debputy_manifest(
_doc_reference: str,
- _path: str,
+ path: str,
lines: List[str],
position_codec: LintCapablePositionCodec,
) -> Optional[List[Diagnostic]]:
+ if not is_valid_file(path):
+ return None
diagnostics = []
try:
MANIFEST_YAML.load("".join(lines))
diff --git a/src/debputy/lsp/lsp_debian_rules.py b/src/debputy/lsp/lsp_debian_rules.py
index 7f0e5fb..86b114c 100644
--- a/src/debputy/lsp/lsp_debian_rules.py
+++ b/src/debputy/lsp/lsp_debian_rules.py
@@ -15,8 +15,6 @@ from typing import (
from lsprotocol.types import (
CompletionItem,
- DidOpenTextDocumentParams,
- DidChangeTextDocumentParams,
Diagnostic,
Range,
Position,
@@ -27,6 +25,7 @@ from lsprotocol.types import (
TEXT_DOCUMENT_CODE_ACTION,
)
+from debputy.debhelper_emulation import parse_drules_for_addons
from debputy.lsp.lsp_features import (
lint_diagnostics,
lsp_standard_handler,
@@ -126,40 +125,26 @@ def _as_hook_targets(command_name: str) -> Iterable[str]:
yield f"{prefix}{command_name}{suffix}"
-def _diagnostics_debian_rules(
- ls: "LanguageServer",
- params: Union[DidOpenTextDocumentParams, DidChangeTextDocumentParams],
-) -> None:
- doc = ls.workspace.get_text_document(params.text_document.uri)
- if not doc.path.endswith("debian/rules"):
- return
- lines = doc.lines
- diagnostics = _lint_debian_rules(
- doc.uri,
- doc.path,
- lines,
- doc.position_codec,
- )
- ls.publish_diagnostics(
- doc.uri,
- diagnostics,
- )
-
-
lsp_standard_handler(_LANGUAGE_IDS, TEXT_DOCUMENT_CODE_ACTION)
lsp_standard_handler(_LANGUAGE_IDS, TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL)
+def is_valid_file(path: str) -> bool:
+ # For debian/rules, the language ID is often set to makefile meaning we get random "non-debian/rules"
+ # makefiles here. Skip those.
+ return path.endswith("debian/rules")
+
+
@lint_diagnostics(_LANGUAGE_IDS)
-def _lint_debian_rules_via_debputy_lsp(
+def _lint_debian_rules(
doc_reference: str,
path: str,
lines: List[str],
position_codec: LintCapablePositionCodec,
) -> Optional[List[Diagnostic]]:
- if not path.endswith("debian/rules"):
+ if not is_valid_file(path):
return None
- return _lint_debian_rules(
+ return _lint_debian_rules_impl(
doc_reference,
path,
lines,
@@ -245,7 +230,7 @@ def iter_make_lines(
yield line_no, line
-def _lint_debian_rules(
+def _lint_debian_rules_impl(
_doc_reference: str,
path: str,
lines: List[str],
@@ -259,7 +244,8 @@ def _lint_debian_rules(
make_error = _run_make_dryrun(source_root, lines)
if make_error is not None:
diagnostics.append(make_error)
- all_dh_commands = _all_dh_commands(source_root)
+
+ all_dh_commands = _all_dh_commands(source_root, lines)
if all_dh_commands:
all_hook_targets = {ht for c in all_dh_commands for ht in _as_hook_targets(c)}
all_hook_targets.update(_KNOWN_TARGETS)
@@ -330,10 +316,15 @@ def _lint_debian_rules(
return diagnostics
-def _all_dh_commands(source_root: str) -> Optional[Sequence[str]]:
+def _all_dh_commands(source_root: str, lines: List[str]) -> Optional[Sequence[str]]:
+ drules_sequences = set()
+ parse_drules_for_addons(lines, drules_sequences)
+ cmd = ["dh_assistant", "list-commands", "--output-format=json"]
+ if drules_sequences:
+ cmd.append(f"--with={','.join(drules_sequences)}")
try:
output = subprocess.check_output(
- ["dh_assistant", "list-commands", "--output-format=json"],
+ cmd,
stderr=subprocess.DEVNULL,
cwd=source_root,
)
@@ -364,7 +355,7 @@ def _debian_rules_completions(
params: CompletionParams,
) -> Optional[Union[CompletionList, Sequence[CompletionItem]]]:
doc = ls.workspace.get_text_document(params.text_document.uri)
- if not doc.path.endswith("debian/rules"):
+ if not is_valid_file(doc.path):
return None
lines = doc.lines
server_position = doc.position_codec.position_from_client_units(
@@ -378,7 +369,7 @@ def _debian_rules_completions(
return None
source_root = os.path.dirname(os.path.dirname(doc.path))
- all_commands = _all_dh_commands(source_root)
+ all_commands = _all_dh_commands(source_root, lines)
items = [CompletionItem(ht) for c in all_commands for ht in _as_hook_targets(c)]
return items
diff --git a/src/debputy/lsp/lsp_dispatch.py b/src/debputy/lsp/lsp_dispatch.py
index 41e9111..b7b744c 100644
--- a/src/debputy/lsp/lsp_dispatch.py
+++ b/src/debputy/lsp/lsp_dispatch.py
@@ -1,5 +1,15 @@
import asyncio
-from typing import Dict, Sequence, Union, Optional
+from typing import (
+ Dict,
+ Sequence,
+ Union,
+ Optional,
+ Any,
+ TypeVar,
+ Callable,
+ Mapping,
+ List,
+)
from lsprotocol.types import (
DidOpenTextDocumentParams,
@@ -11,6 +21,19 @@ from lsprotocol.types import (
CompletionItem,
CompletionParams,
TEXT_DOCUMENT_HOVER,
+ TEXT_DOCUMENT_FOLDING_RANGE,
+ FoldingRange,
+ FoldingRangeParams,
+ TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL,
+ SemanticTokensParams,
+ SemanticTokens,
+ Hover,
+ TEXT_DOCUMENT_CODE_ACTION,
+ Command,
+ CodeAction,
+ TextDocumentCodeActionRequest,
+ CodeActionParams,
+ SemanticTokensRegistrationOptions,
)
from debputy import __version__
@@ -18,6 +41,9 @@ from debputy.lsp.lsp_features import (
DIAGNOSTIC_HANDLERS,
COMPLETER_HANDLERS,
HOVER_HANDLERS,
+ SEMANTIC_TOKENS_FULL_HANDLERS,
+ CODE_ACTION_HANDLERS,
+ SEMANTIC_TOKENS_LEGEND,
)
from debputy.util import _info
@@ -37,6 +63,10 @@ except ImportError:
DEBPUTY_LANGUAGE_SERVER = Mock()
+P = TypeVar("P")
+R = TypeVar("R")
+
+
def is_doc_at_version(uri: str, version: int) -> bool:
dv = _DOCUMENT_VERSION_TABLE.get(uri)
return dv == version
@@ -88,22 +118,12 @@ def _completions(
ls: "LanguageServer",
params: CompletionParams,
) -> Optional[Union[CompletionList, Sequence[CompletionItem]]]:
- doc_uri = params.text_document.uri
- doc = ls.workspace.get_text_document(doc_uri)
-
- handler = COMPLETER_HANDLERS.get(doc.language_id)
- if handler is None:
- _info(
- f"Complete request for document: {doc.path} ({doc.language_id}) - no handler"
- )
- return
- _info(
- f"Complete request for document: {doc.path} ({doc.language_id}) - delegating to handler"
- )
-
- return handler(
+ return _dispatch_standard_handler(
ls,
+ params.text_document.uri,
params,
+ COMPLETER_HANDLERS,
+ "Complete request",
)
@@ -111,18 +131,81 @@ def _completions(
def _hover(
ls: "LanguageServer",
params: CompletionParams,
-) -> Optional[Union[CompletionList, Sequence[CompletionItem]]]:
- doc_uri = params.text_document.uri
+) -> Optional[Hover]:
+ return _dispatch_standard_handler(
+ ls,
+ params.text_document.uri,
+ params,
+ HOVER_HANDLERS,
+ "Hover doc request",
+ )
+
+
+@DEBPUTY_LANGUAGE_SERVER.feature(TEXT_DOCUMENT_CODE_ACTION)
+def _code_actions(
+ ls: "LanguageServer",
+ params: CodeActionParams,
+) -> Optional[List[Union[Command, CodeAction]]]:
+ return _dispatch_standard_handler(
+ ls,
+ params.text_document.uri,
+ params,
+ CODE_ACTION_HANDLERS,
+ "Code action request",
+ )
+
+
+@DEBPUTY_LANGUAGE_SERVER.feature(TEXT_DOCUMENT_FOLDING_RANGE)
+def _folding_ranges(
+ ls: "LanguageServer",
+ params: FoldingRangeParams,
+) -> Optional[Sequence[FoldingRange]]:
+ return _dispatch_standard_handler(
+ ls,
+ params.text_document.uri,
+ params,
+ HOVER_HANDLERS,
+ "Folding range request",
+ )
+
+
+@DEBPUTY_LANGUAGE_SERVER.feature(
+ TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL,
+ SemanticTokensRegistrationOptions(
+ SEMANTIC_TOKENS_LEGEND,
+ full=True,
+ ),
+)
+def _semantic_tokens_full(
+ ls: "LanguageServer",
+ params: SemanticTokensParams,
+) -> Optional[SemanticTokens]:
+ return _dispatch_standard_handler(
+ ls,
+ params.text_document.uri,
+ params,
+ SEMANTIC_TOKENS_FULL_HANDLERS,
+ "Semantic tokens request",
+ )
+
+
+def _dispatch_standard_handler(
+ ls: "LanguageServer",
+ doc_uri: str,
+ params: P,
+ handler_table: Mapping[str, Callable[["LanguageServer", P], R]],
+ request_type: str,
+) -> R:
doc = ls.workspace.get_text_document(doc_uri)
- handler = HOVER_HANDLERS.get(doc.language_id)
+ handler = handler_table.get(doc.language_id)
if handler is None:
_info(
- f"Hover request for document: {doc.path} ({doc.language_id}) - no handler"
+ f"{request_type} for document: {doc.path} ({doc.language_id}) - no handler"
)
return
_info(
- f"Hover request for document: {doc.path} ({doc.language_id}) - delegating to handler"
+ f"{request_type} for document: {doc.path} ({doc.language_id}) - delegating to handler"
)
return handler(
diff --git a/src/debputy/lsp/lsp_features.py b/src/debputy/lsp/lsp_features.py
index b417dd3..8260675 100644
--- a/src/debputy/lsp/lsp_features.py
+++ b/src/debputy/lsp/lsp_features.py
@@ -8,6 +8,7 @@ from lsprotocol.types import (
DidChangeTextDocumentParams,
Diagnostic,
DidOpenTextDocumentParams,
+ SemanticTokensLegend,
)
try:
@@ -21,11 +22,20 @@ from debputy.lsp.text_util import on_save_trim_end_of_line_whitespace
C = TypeVar("C", bound=Callable)
+SEMANTIC_TOKENS_LEGEND = SemanticTokensLegend(
+ token_types=["keyword", "enumMember"],
+ token_modifiers=[],
+)
+SEMANTIC_TOKEN_TYPES_IDS = {
+ t: idx for idx, t in enumerate(SEMANTIC_TOKENS_LEGEND.token_types)
+}
DIAGNOSTIC_HANDLERS = {}
COMPLETER_HANDLERS = {}
HOVER_HANDLERS = {}
CODE_ACTION_HANDLERS = {}
+FOLDING_RANGE_HANDLERS = {}
+SEMANTIC_TOKENS_FULL_HANDLERS = {}
WILL_SAVE_WAIT_UNTIL_HANDLERS = {}
_ALIAS_OF = {}
@@ -111,6 +121,16 @@ def lsp_hover(file_formats: Union[str, Sequence[str]]) -> Callable[[C], C]:
return _registering_wrapper(file_formats, HOVER_HANDLERS)
+def lsp_folding_ranges(file_formats: Union[str, Sequence[str]]) -> Callable[[C], C]:
+ return _registering_wrapper(file_formats, FOLDING_RANGE_HANDLERS)
+
+
+def lsp_semantic_tokens_full(
+ file_formats: Union[str, Sequence[str]]
+) -> Callable[[C], C]:
+ return _registering_wrapper(file_formats, SEMANTIC_TOKENS_FULL_HANDLERS)
+
+
def lsp_standard_handler(file_formats: Union[str, Sequence[str]], topic: str) -> None:
res = _STANDARD_HANDLERS.get(topic)
if res is None:
@@ -171,6 +191,8 @@ def describe_lsp_features() -> None:
("code actions/quickfixes", CODE_ACTION_HANDLERS),
("completion suggestions", COMPLETER_HANDLERS),
("hover docs", HOVER_HANDLERS),
+ ("folding ranges", FOLDING_RANGE_HANDLERS),
+ ("semantic tokens", SEMANTIC_TOKENS_FULL_HANDLERS),
("on-save handler", WILL_SAVE_WAIT_UNTIL_HANDLERS),
]
print("LSP language IDs and their features:")
diff --git a/src/debputy/lsp/lsp_generic_deb822.py b/src/debputy/lsp/lsp_generic_deb822.py
index 245f3de..7a1f96f 100644
--- a/src/debputy/lsp/lsp_generic_deb822.py
+++ b/src/debputy/lsp/lsp_generic_deb822.py
@@ -8,6 +8,8 @@ from typing import (
Any,
Container,
List,
+ Iterable,
+ Iterator,
)
from lsprotocol.types import (
@@ -20,14 +22,27 @@ from lsprotocol.types import (
Hover,
MarkupKind,
HoverParams,
+ FoldingRangeParams,
+ FoldingRange,
+ FoldingRangeKind,
+ SemanticTokensParams,
+ SemanticTokens,
)
from debputy.lsp.lsp_debian_control_reference_data import (
Deb822FileMetadata,
Deb822KnownField,
StanzaMetadata,
+ FieldValueClass,
)
+from debputy.lsp.lsp_features import SEMANTIC_TOKEN_TYPES_IDS
from debputy.lsp.text_util import normalize_dctrl_field_name
+from debputy.lsp.vendoring._deb822_repro import parse_deb822_file
+from debputy.lsp.vendoring._deb822_repro.parsing import (
+ Deb822KeyValuePairElement,
+ LIST_SPACE_SEPARATED_INTERPRETATION,
+)
+from debputy.lsp.vendoring._deb822_repro.tokens import tokenize_deb822_file, Deb822Token
from debputy.util import _info
try:
@@ -175,6 +190,152 @@ def deb822_hover(
)
+def _deb822_token_iter(
+ tokens: Iterable[Deb822Token],
+) -> Iterator[Tuple[Deb822Token, int, int, int, int, int]]:
+ line_no = 0
+ line_offset = 0
+
+ for token in tokens:
+ start_line = line_no
+ start_line_offset = line_offset
+
+ newlines = token.text.count("\n")
+ line_no += newlines
+ text_len = len(token.text)
+ if newlines:
+ if token.text.endswith("\n"):
+ line_offset = 0
+ else:
+ # -2, one to remove the "\n" and one to get 0-offset
+ line_offset = text_len - token.text.rindex("\n") - 2
+ else:
+ line_offset += text_len
+
+ yield token, start_line, start_line_offset, line_no, line_offset
+
+
+def deb822_folding_ranges(
+ ls: "LanguageServer",
+ params: FoldingRangeParams,
+ # Unused for now: might be relevant for supporting folding for some fields
+ _file_metadata: Deb822FileMetadata[Any],
+) -> Optional[Sequence[FoldingRange]]:
+ doc = ls.workspace.get_text_document(params.text_document.uri)
+ comment_start = -1
+ folding_ranges = []
+ for (
+ token,
+ start_line,
+ start_offset,
+ end_line,
+ end_offset,
+ ) in _deb822_token_iter(tokenize_deb822_file(doc.lines)):
+ if token.is_comment:
+ if comment_start < 0:
+ comment_start = start_line
+ elif comment_start > -1:
+ comment_start = -1
+ folding_range = FoldingRange(
+ comment_start,
+ end_line,
+ kind=FoldingRangeKind.Comment,
+ )
+
+ folding_ranges.append(folding_range)
+
+ return folding_ranges
+
+
+def deb822_semantic_tokens_full(
+ ls: "LanguageServer",
+ request: SemanticTokensParams,
+ file_metadata: Deb822FileMetadata[Any],
+) -> Optional[SemanticTokens]:
+ doc = ls.workspace.get_text_document(request.text_document.uri)
+ lines = doc.lines
+ deb822_file = parse_deb822_file(
+ lines,
+ accept_files_with_duplicated_fields=True,
+ accept_files_with_error_tokens=True,
+ )
+ tokens = []
+ previous_line = 0
+ keyword_token_code = SEMANTIC_TOKEN_TYPES_IDS["keyword"]
+ known_value_token_code = SEMANTIC_TOKEN_TYPES_IDS["enumMember"]
+ no_modifiers = 0
+
+ # TODO: Add comment support; slightly complicated by how we parse the file.
+
+ for stanza_idx, stanza in enumerate(deb822_file):
+ stanza_position = stanza.position_in_file()
+ stanza_metadata = file_metadata.classify_stanza(stanza, stanza_idx=stanza_idx)
+ for kvpair in stanza.iter_parts_of_type(Deb822KeyValuePairElement):
+ kvpair_pos = kvpair.position_in_parent().relative_to(stanza_position)
+ # These two happen to be the same; the indirection is to make it explicit that the two
+ # positions for different tokens are the same.
+ field_position_without_comments = kvpair_pos
+ field_size = doc.position_codec.client_num_units(kvpair.field_name)
+ current_line = field_position_without_comments.line_position
+ line_delta = current_line - previous_line
+ previous_line = current_line
+ tokens.append(line_delta) # Line delta
+ tokens.append(0) # Token column delta
+ tokens.append(field_size) # Token length
+ tokens.append(keyword_token_code)
+ tokens.append(no_modifiers)
+
+ known_field: Optional[Deb822KnownField] = stanza_metadata.get(
+ kvpair.field_name
+ )
+ if (
+ known_field is None
+ or not known_field.known_values
+ or known_field.spellcheck_value
+ ):
+ continue
+
+ if known_field.field_value_class not in (
+ FieldValueClass.SINGLE_VALUE,
+ FieldValueClass.SPACE_SEPARATED_LIST,
+ ):
+ continue
+ value_element_pos = kvpair.value_element.position_in_parent().relative_to(
+ kvpair_pos
+ )
+
+ last_token_start_column = 0
+
+ for value_ref in kvpair.interpret_as(
+ LIST_SPACE_SEPARATED_INTERPRETATION
+ ).iter_value_references():
+ if value_ref.value not in known_field.known_values:
+ continue
+ value_loc = value_ref.locatable
+ value_range_te = value_loc.range_in_parent().relative_to(
+ value_element_pos
+ )
+ start_line = value_range_te.start_pos.line_position
+ line_delta = start_line - current_line
+ current_line = start_line
+ if line_delta:
+ last_token_start_column = 0
+
+ value_start_column = value_range_te.start_pos.cursor_position
+ column_delta = value_start_column - last_token_start_column
+ last_token_start_column = value_start_column
+
+ tokens.append(line_delta) # Line delta
+ tokens.append(column_delta) # Token column delta
+ tokens.append(field_size) # Token length
+ tokens.append(known_value_token_code)
+ tokens.append(no_modifiers)
+
+ if not tokens:
+ return None
+ return SemanticTokens(tokens)
+
+
def _should_complete_field_with_value(cand: Deb822KnownField) -> bool:
return cand.known_values is not None and (
len(cand.known_values) == 1
diff --git a/src/debputy/lsp/quickfixes.py b/src/debputy/lsp/quickfixes.py
index d911961..7ae0324 100644
--- a/src/debputy/lsp/quickfixes.py
+++ b/src/debputy/lsp/quickfixes.py
@@ -172,6 +172,7 @@ def _correct_value_code_action(
def provide_standard_quickfixes_from_diagnostics(
+ ls: "LanguageServer",
code_action_params: CodeActionParams,
) -> Optional[List[Union[Command, CodeAction]]]:
actions = []
diff --git a/src/debputy/lsp/spellchecking.py b/src/debputy/lsp/spellchecking.py
index 69dd119..f9027af 100644
--- a/src/debputy/lsp/spellchecking.py
+++ b/src/debputy/lsp/spellchecking.py
@@ -45,7 +45,7 @@ _LOOKS_LIKE_PROGRAMMING_TERM = re.compile(
# SCREAMING_SNAKE_CASE (environment variables plus -DVAR=B or $FOO)
| [-$%&*_]{0,2}[A-Z][A-Z0-9]*(_[A-Z0-9]+)+(?:=\S+)?
| \#[A-Z][A-Z0-9]*(_[A-Z0-9]+)+\#
- # Subcommand names. Require at least two "-" to avoid skipping hypenated words
+ # Subcommand names. Require at least two "-" to avoid skipping hyphenated words
| [a-z][a-z0-9]*(-[a-z0-9]+){2,}
# Short args
| -[a-z0-9]+
diff --git a/src/debputy/lsp/vendoring/_deb822_repro/__init__.py b/src/debputy/lsp/vendoring/_deb822_repro/__init__.py
index 72fe6dc..cc2b1de 100644
--- a/src/debputy/lsp/vendoring/_deb822_repro/__init__.py
+++ b/src/debputy/lsp/vendoring/_deb822_repro/__init__.py
@@ -53,7 +53,7 @@ Compared to debian.deb822
-------------------------
The round-trip safe API is primarily useful when your program is editing files
-and the file in question is (likely) to be hand-edited or formated directly by
+and the file in question is (likely) to be hand-edited or formatted directly by
human maintainers. This includes files like debian/control and the
debian/copyright using the "DEP-5" format.
diff --git a/src/debputy/lsp/vendoring/_deb822_repro/locatable.py b/src/debputy/lsp/vendoring/_deb822_repro/locatable.py
index 90bfa1c..afebf14 100644
--- a/src/debputy/lsp/vendoring/_deb822_repro/locatable.py
+++ b/src/debputy/lsp/vendoring/_deb822_repro/locatable.py
@@ -270,7 +270,7 @@ class Range:
:param base: The desired starting position
:param sizes: All the ranges that combined makes up the size of the
desired position. Note that order can affect the end result. Particularly
- the end character offset gets reset everytime a size spans a line.
+ the end character offset gets reset every time a size spans a line.
:returns: A range at the provided base position that has the size of
the provided range.
"""
diff --git a/src/debputy/lsp/vendoring/_deb822_repro/parsing.py b/src/debputy/lsp/vendoring/_deb822_repro/parsing.py
index 13e59b1..1a2da25 100644
--- a/src/debputy/lsp/vendoring/_deb822_repro/parsing.py
+++ b/src/debputy/lsp/vendoring/_deb822_repro/parsing.py
@@ -2076,7 +2076,7 @@ class Deb822ParagraphElement(Deb822Element, Deb822ParagraphToStrWrapperMixin, AB
:param discard_comments_on_read: When getting a field value from the dict,
this parameter decides how in-line comments are handled. When setting
the value, inline comments are still allowed and will be retained.
- However, keep in mind that this option makes getter and setter assymetric
+ However, keep in mind that this option makes getter and setter asymmetric
as a "get" following a "set" with inline comments will omit the comments
even if they are there (see the code example).
:param auto_map_initial_line_whitespace: Special-case the first value line
diff --git a/src/debputy/lsp/vendoring/_deb822_repro/tokens.py b/src/debputy/lsp/vendoring/_deb822_repro/tokens.py
index 4e5fa16..5db991a 100644
--- a/src/debputy/lsp/vendoring/_deb822_repro/tokens.py
+++ b/src/debputy/lsp/vendoring/_deb822_repro/tokens.py
@@ -45,7 +45,7 @@ _RE_WHITESPACE_SEPARATED_WORD_LIST = re.compile(
_RE_COMMA_SEPARATED_WORD_LIST = re.compile(
r"""
# This regex is slightly complicated by the fact that it should work with
- # finditer and comsume the entire value.
+ # finditer and consume the entire value.
#
# To do this, we structure the regex so it always starts on a comma (except
# for the first iteration, where we permit the absence of a comma)
diff --git a/src/debputy/manifest_parser/declarative_parser.py b/src/debputy/manifest_parser/declarative_parser.py
index 32e93fe..84e0230 100644
--- a/src/debputy/manifest_parser/declarative_parser.py
+++ b/src/debputy/manifest_parser/declarative_parser.py
@@ -720,7 +720,7 @@ class ParserGenerator:
```
While this is sufficient for programmers, it is a bit ridig for the packager writing the manifest. Therefore,
- you can also provide a TypedDict descriping the input, enabling more flexibility:
+ you can also provide a TypedDict describing the input, enabling more flexibility:
>>> class InstallDocsRule(DebputyParsedContent):
... sources: List[str]
@@ -737,7 +737,7 @@ class ParserGenerator:
In this case, the `sources` field can either come from a single `source` in the manifest (which must be a string)
or `sources` (which must be a list of strings). The parser also ensures that only one of `source` or `sources`
- is used to ensure the input is not ambigious. For the `into` parameter, the parser will accept it being a str
+ is used to ensure the input is not ambiguous. For the `into` parameter, the parser will accept it being a str
or a list of strings. Regardless of how the input was provided, the parser will normalize the input such that
both `sources` and `into` in the result is a list of strings. As an example, this parser can accept
both the previous input but also the following input:
diff --git a/src/debputy/plugin/api/impl.py b/src/debputy/plugin/api/impl.py
index e25713f..8b75322 100644
--- a/src/debputy/plugin/api/impl.py
+++ b/src/debputy/plugin/api/impl.py
@@ -783,7 +783,7 @@ class DebputyPluginInitializerProvider(DebputyPluginInitializer):
self._unloaders.append(_unload)
- def plugable_object_parser(
+ def pluggable_object_parser(
self,
rule_type: str,
rule_name: str,
@@ -821,12 +821,12 @@ class DebputyPluginInitializerProvider(DebputyPluginInitializer):
def _unload() -> None:
raise PluginInitializationError(
- "Cannot unload plugable_object_parser (not implemented)"
+ "Cannot unload pluggable_object_parser (not implemented)"
)
self._unloaders.append(_unload)
- def plugable_manifest_rule(
+ def pluggable_manifest_rule(
self,
rule_type: Union[TTP, str],
rule_name: Union[str, List[str]],
@@ -869,7 +869,7 @@ class DebputyPluginInitializerProvider(DebputyPluginInitializer):
def _unload() -> None:
raise PluginInitializationError(
- "Cannot unload plugable_manifest_rule (not implemented)"
+ "Cannot unload pluggable_manifest_rule (not implemented)"
)
self._unloaders.append(_unload)
diff --git a/src/debputy/plugin/api/test_api/test_spec.py b/src/debputy/plugin/api/test_api/test_spec.py
index b05f7ed..0c0c6bc 100644
--- a/src/debputy/plugin/api/test_api/test_spec.py
+++ b/src/debputy/plugin/api/test_api/test_spec.py
@@ -61,7 +61,7 @@ def build_virtual_file_system(
... )
True
- Any string provided will be pased to `virtual_path` using all defaults for other parameters, making `str`
+ Any string provided will be passed to `virtual_path` using all defaults for other parameters, making `str`
arguments a nice easy shorthand if you just want a path to exist, but do not really care about it otherwise
(or `virtual_path_def` defaults happens to work for you).
@@ -158,7 +158,7 @@ class RegisteredPackagerProvidedFile(metaclass=ABCMeta):
installed_path: str
"""The mode that debputy will give these files when installed (unless overridden)"""
default_mode: int
- """The default priority assigned to files unless overriden (if priories are assigned at all)"""
+ """The default priority assigned to files unless overridden (if priories are assigned at all)"""
default_priority: Optional[int]
"""The filename format to be used"""
filename_format: Optional[str]
diff --git a/src/debputy/plugin/debputy/binary_package_rules.py b/src/debputy/plugin/debputy/binary_package_rules.py
index 04a0fa1..686d71a 100644
--- a/src/debputy/plugin/debputy/binary_package_rules.py
+++ b/src/debputy/plugin/debputy/binary_package_rules.py
@@ -59,7 +59,7 @@ ACCEPTABLE_CLEAN_ON_REMOVAL_IF_EXACT_MATCH_OR_SUBDIR_OF = frozenset(
def register_binary_package_rules(api: DebputyPluginInitializerProvider) -> None:
- api.plugable_manifest_rule(
+ api.pluggable_manifest_rule(
OPARSER_PACKAGES,
"binary-version",
BinaryVersionParsedFormat,
@@ -93,7 +93,7 @@ def register_binary_package_rules(api: DebputyPluginInitializerProvider) -> None
),
)
- api.plugable_manifest_rule(
+ api.pluggable_manifest_rule(
OPARSER_PACKAGES,
"transformations",
ListOfTransformationRulesFormat,
@@ -131,7 +131,7 @@ def register_binary_package_rules(api: DebputyPluginInitializerProvider) -> None
),
)
- api.plugable_manifest_rule(
+ api.pluggable_manifest_rule(
OPARSER_PACKAGES,
"conffile-management",
ListOfDpkgMaintscriptHelperCommandFormat,
@@ -139,7 +139,7 @@ def register_binary_package_rules(api: DebputyPluginInitializerProvider) -> None
source_format=List[DpkgMaintscriptHelperCommand],
)
- api.plugable_manifest_rule(
+ api.pluggable_manifest_rule(
OPARSER_PACKAGES,
"clean-after-removal",
ListParsedFormat,
@@ -203,7 +203,7 @@ def register_binary_package_rules(api: DebputyPluginInitializerProvider) -> None
),
)
- api.plugable_manifest_rule(
+ api.pluggable_manifest_rule(
OPARSER_PACKAGES,
"installation-search-dirs",
InstallationSearchDirsParsedFormat,
diff --git a/src/debputy/plugin/debputy/manifest_root_rules.py b/src/debputy/plugin/debputy/manifest_root_rules.py
index cc2b1d4..86a1c27 100644
--- a/src/debputy/plugin/debputy/manifest_root_rules.py
+++ b/src/debputy/plugin/debputy/manifest_root_rules.py
@@ -33,7 +33,7 @@ if TYPE_CHECKING:
def register_manifest_root_rules(api: DebputyPluginInitializerProvider) -> None:
# Registration order matters. Notably, definitions must come before anything that can
# use definitions (variables), which is why it is second only to the manifest version.
- api.plugable_manifest_rule(
+ api.pluggable_manifest_rule(
OPARSER_MANIFEST_ROOT,
MK_MANIFEST_VERSION,
ManifestVersionFormat,
@@ -56,13 +56,13 @@ def register_manifest_root_rules(api: DebputyPluginInitializerProvider) -> None:
),
),
)
- api.plugable_object_parser(
+ api.pluggable_object_parser(
OPARSER_MANIFEST_ROOT,
MK_MANIFEST_DEFINITIONS,
object_parser_key=OPARSER_MANIFEST_DEFINITIONS,
on_end_parse_step=lambda _a, _b, _c, mp: mp._ensure_package_states_is_initialized(),
)
- api.plugable_manifest_rule(
+ api.pluggable_manifest_rule(
OPARSER_MANIFEST_DEFINITIONS,
MK_MANIFEST_VARIABLES,
ManifestVariablesParsedFormat,
@@ -105,7 +105,7 @@ def register_manifest_root_rules(api: DebputyPluginInitializerProvider) -> None:
),
),
)
- api.plugable_manifest_rule(
+ api.pluggable_manifest_rule(
OPARSER_MANIFEST_ROOT,
MK_INSTALLATIONS,
ListOfInstallRulesFormat,
@@ -156,7 +156,7 @@ def register_manifest_root_rules(api: DebputyPluginInitializerProvider) -> None:
),
),
)
- api.plugable_manifest_rule(
+ api.pluggable_manifest_rule(
OPARSER_MANIFEST_ROOT,
MK_PACKAGES,
DictFormat,
diff --git a/src/debputy/plugin/debputy/private_api.py b/src/debputy/plugin/debputy/private_api.py
index 2db2b56..b9aa043 100644
--- a/src/debputy/plugin/debputy/private_api.py
+++ b/src/debputy/plugin/debputy/private_api.py
@@ -781,7 +781,7 @@ def register_special_ppfs(api: DebputyPluginInitializerProvider) -> None:
def register_install_rules(api: DebputyPluginInitializerProvider) -> None:
- api.plugable_manifest_rule(
+ api.pluggable_manifest_rule(
InstallRule,
MK_INSTALLATIONS_INSTALL,
ParsedInstallRule,
@@ -868,7 +868,7 @@ def register_install_rules(api: DebputyPluginInitializerProvider) -> None:
reference_documentation_url=_manifest_format_doc("generic-install-install"),
),
)
- api.plugable_manifest_rule(
+ api.pluggable_manifest_rule(
InstallRule,
[
MK_INSTALLATIONS_INSTALL_DOCS,
@@ -977,7 +977,7 @@ def register_install_rules(api: DebputyPluginInitializerProvider) -> None:
),
),
)
- api.plugable_manifest_rule(
+ api.pluggable_manifest_rule(
InstallRule,
[
MK_INSTALLATIONS_INSTALL_EXAMPLES,
@@ -1059,7 +1059,7 @@ def register_install_rules(api: DebputyPluginInitializerProvider) -> None:
),
),
)
- api.plugable_manifest_rule(
+ api.pluggable_manifest_rule(
InstallRule,
MK_INSTALLATIONS_INSTALL_MAN,
ParsedInstallManpageRule,
@@ -1166,7 +1166,7 @@ def register_install_rules(api: DebputyPluginInitializerProvider) -> None:
),
),
)
- api.plugable_manifest_rule(
+ api.pluggable_manifest_rule(
InstallRule,
MK_INSTALLATIONS_DISCARD,
ParsedInstallDiscardRule,
@@ -1242,7 +1242,7 @@ def register_install_rules(api: DebputyPluginInitializerProvider) -> None:
),
),
)
- api.plugable_manifest_rule(
+ api.pluggable_manifest_rule(
InstallRule,
MK_INSTALLATIONS_MULTI_DEST_INSTALL,
ParsedMultiDestInstallRule,
@@ -1329,7 +1329,7 @@ def register_install_rules(api: DebputyPluginInitializerProvider) -> None:
def register_transformation_rules(api: DebputyPluginInitializerProvider) -> None:
- api.plugable_manifest_rule(
+ api.pluggable_manifest_rule(
TransformationRule,
"move",
TransformationMoveRuleSpec,
@@ -1381,7 +1381,7 @@ def register_transformation_rules(api: DebputyPluginInitializerProvider) -> None
),
),
)
- api.plugable_manifest_rule(
+ api.pluggable_manifest_rule(
TransformationRule,
"remove",
TransformationRemoveRuleSpec,
@@ -1451,7 +1451,7 @@ def register_transformation_rules(api: DebputyPluginInitializerProvider) -> None
),
),
)
- api.plugable_manifest_rule(
+ api.pluggable_manifest_rule(
TransformationRule,
"create-symlink",
CreateSymlinkRule,
@@ -1537,7 +1537,7 @@ def register_transformation_rules(api: DebputyPluginInitializerProvider) -> None
),
),
)
- api.plugable_manifest_rule(
+ api.pluggable_manifest_rule(
TransformationRule,
"path-metadata",
PathManifestRule,
@@ -1653,7 +1653,7 @@ def register_transformation_rules(api: DebputyPluginInitializerProvider) -> None
),
),
)
- api.plugable_manifest_rule(
+ api.pluggable_manifest_rule(
TransformationRule,
"create-directories",
EnsureDirectoryRule,
@@ -1824,7 +1824,7 @@ def register_manifest_condition_rules(api: DebputyPluginInitializerProvider) ->
),
)
- api.plugable_manifest_rule(
+ api.pluggable_manifest_rule(
ManifestCondition,
"not",
MCNot,
@@ -1868,7 +1868,7 @@ def register_manifest_condition_rules(api: DebputyPluginInitializerProvider) ->
),
),
)
- api.plugable_manifest_rule(
+ api.pluggable_manifest_rule(
ManifestCondition,
["any-of", "all-of"],
MCAnyOfAllOf,
@@ -1887,7 +1887,7 @@ def register_manifest_condition_rules(api: DebputyPluginInitializerProvider) ->
),
),
)
- api.plugable_manifest_rule(
+ api.pluggable_manifest_rule(
ManifestCondition,
"arch-matches",
MCArchMatches,
@@ -1997,7 +1997,7 @@ def register_manifest_condition_rules(api: DebputyPluginInitializerProvider) ->
),
)
- api.plugable_manifest_rule(
+ api.pluggable_manifest_rule(
ManifestCondition,
"source-context-arch-matches",
MCArchMatches,
@@ -2005,7 +2005,7 @@ def register_manifest_condition_rules(api: DebputyPluginInitializerProvider) ->
source_format=str,
inline_reference_documentation=context_arch_doc,
)
- api.plugable_manifest_rule(
+ api.pluggable_manifest_rule(
ManifestCondition,
"package-context-arch-matches",
MCArchMatches,
@@ -2013,7 +2013,7 @@ def register_manifest_condition_rules(api: DebputyPluginInitializerProvider) ->
source_format=str,
inline_reference_documentation=context_arch_doc,
)
- api.plugable_manifest_rule(
+ api.pluggable_manifest_rule(
ManifestCondition,
"build-profiles-matches",
MCBuildProfileMatches,
@@ -2043,7 +2043,7 @@ def register_manifest_condition_rules(api: DebputyPluginInitializerProvider) ->
def register_dpkg_conffile_rules(api: DebputyPluginInitializerProvider) -> None:
- api.plugable_manifest_rule(
+ api.pluggable_manifest_rule(
DpkgMaintscriptHelperCommand,
"remove",
DpkgRemoveConffileRule,
@@ -2051,7 +2051,7 @@ def register_dpkg_conffile_rules(api: DebputyPluginInitializerProvider) -> None:
inline_reference_documentation=None, # TODO: write and add
)
- api.plugable_manifest_rule(
+ api.pluggable_manifest_rule(
DpkgMaintscriptHelperCommand,
"rename",
DpkgRenameConffileRule,
diff --git a/src/debputy/transformation_rules.py b/src/debputy/transformation_rules.py
index 8d9caae..fdf9528 100644
--- a/src/debputy/transformation_rules.py
+++ b/src/debputy/transformation_rules.py
@@ -217,7 +217,7 @@ class MoveTransformationRule(TransformationRule):
self._error(
f"Could not rename {self._match_rule.describe_match_short()} to {self._dest_path}"
f" (from: {self._definition_source}). Multiple paths matched the pattern and the"
- " destination was not a directory. Either correct the pattern to only match ony source"
+ " destination was not a directory. Either correct the pattern to only match only source"
" OR define the destination to be a directory (E.g., add a trailing slash - example:"
f' "{self._dest_path}/")'
)