Coverage for src/debputy/lsp/languages/lsp_debian_debputy_manifest.py: 80%
54 statements
« prev ^ index » next coverage.py v7.8.2, created at 2026-07-22 10:58 +0000
« prev ^ index » next coverage.py v7.8.2, created at 2026-07-22 10:58 +0000
1from typing import Any
2from collections.abc import Sequence, Container
4from debputy.linting.lint_util import LintState, DebputyMetadata
5from debputy.lsp.lsp_features import (
6 lint_diagnostics,
7 lsp_standard_handler,
8 lsp_hover,
9 lsp_completer,
10 SecondaryLanguage,
11 LanguageDispatchRule,
12)
13from debputy.lsp.lsp_generic_yaml import (
14 yaml_key_range,
15 generic_yaml_hover,
16 LSPYAMLHelper,
17 generic_yaml_lint,
18 generic_yaml_completer,
19 _is_empty_range,
20)
21from debputy.lsprotocol.types import (
22 TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL,
23 HoverParams,
24 Hover,
25 TEXT_DOCUMENT_CODE_ACTION,
26 CompletionParams,
27 CompletionList,
28 CompletionItem,
29)
30from debputy.plugin.api.impl_types import (
31 PluginProvidedParser,
32)
33from debputy.plugin.api.parser_tables import OPARSER_MANIFEST_ROOT
34from debputy.plugin.api.spec import DebputyIntegrationMode
35from debputy.plugins.debputy.private_api import Capability, load_libcap
37try:
38 from pygls.server import LanguageServer
39 from debputy.lsp.debputy_ls import DebputyLanguageServer
40except ImportError:
41 pass
44_DISPATCH_RULE = LanguageDispatchRule.new_rule(
45 "debian/debputy.manifest",
46 "debputy.manifest",
47 "debian/debputy.manifest",
48 [
49 SecondaryLanguage("debputy.manifest"),
50 # LSP's official language ID for YAML files
51 SecondaryLanguage("yaml", secondary_lookup="path-name"),
52 ],
53)
56lsp_standard_handler(_DISPATCH_RULE, TEXT_DOCUMENT_CODE_ACTION)
57lsp_standard_handler(_DISPATCH_RULE, TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL)
60class DebputyManifestLSPYAMLHelper(LSPYAMLHelper[DebputyMetadata]):
62 def _validate_subparser_is_valid_here(
63 self,
64 subparser: PluginProvidedParser,
65 orig_key: str,
66 line: int,
67 col: int,
68 ) -> None:
69 _integration_mode_allows_key(
70 self.lint_state,
71 self.custom_data.debputy_integration_mode,
72 subparser.parser.expected_debputy_integration_mode,
73 orig_key,
74 line,
75 col,
76 )
78 def _type_based_value_check(
79 self,
80 target_attr_type: type,
81 value: Any,
82 value_pos: tuple[int, int],
83 *,
84 key: str | int | None = None,
85 ) -> bool:
86 if issubclass(target_attr_type, Capability): 86 ↛ 87line 86 didn't jump to line 87 because the condition on line 86 was never true
87 has_libcap, _, is_valid_cap = load_libcap()
88 if has_libcap and not is_valid_cap(value):
89 line_no, cursor_pos = value_pos
90 cap_range = self._remaining_line(line_no, cursor_pos)
91 if _is_empty_range(cap_range):
92 # FIXME: We cannot report an empty range, but there is still a problem here.
93 return True
94 self.lint_state.emit_diagnostic(
95 cap_range,
96 "The value could not be parsed as a capability via cap_from_text on this system",
97 "warning",
98 "debputy",
99 )
100 return True
102 return super()._type_based_value_check(
103 target_attr_type,
104 value,
105 value_pos,
106 key=key,
107 )
110def _initialize_yaml_helper(lint_state: LintState) -> DebputyManifestLSPYAMLHelper:
111 return DebputyManifestLSPYAMLHelper(
112 lint_state,
113 lint_state.plugin_feature_set.manifest_parser_generator,
114 lint_state.debputy_metadata,
115 )
118@lint_diagnostics(_DISPATCH_RULE)
119async def _lint_debian_debputy_manifest(lint_state: LintState) -> None:
120 pg = lint_state.plugin_feature_set.manifest_parser_generator
121 root_parser = pg.dispatchable_object_parsers[OPARSER_MANIFEST_ROOT]
122 await generic_yaml_lint(
123 lint_state,
124 root_parser,
125 _initialize_yaml_helper,
126 )
129def _integration_mode_allows_key(
130 lint_state: LintState,
131 debputy_integration_mode: DebputyIntegrationMode | None,
132 expected_debputy_integration_modes: Container[DebputyIntegrationMode] | None,
133 key: str,
134 line: int,
135 col: int,
136) -> None:
137 if debputy_integration_mode is None or expected_debputy_integration_modes is None:
138 return
139 if debputy_integration_mode in expected_debputy_integration_modes:
140 return
141 key_range = yaml_key_range(key, line, col)
142 lint_state.emit_diagnostic(
143 key_range,
144 f'Feature "{key}" not supported in integration mode {debputy_integration_mode}',
145 "error",
146 "debputy",
147 )
150@lsp_completer(_DISPATCH_RULE)
151def debputy_manifest_completer(
152 ls: "DebputyLanguageServer",
153 params: CompletionParams,
154) -> CompletionList | Sequence[CompletionItem] | None:
155 pg = ls.plugin_feature_set.manifest_parser_generator
156 root_parser = pg.dispatchable_object_parsers[OPARSER_MANIFEST_ROOT]
157 return generic_yaml_completer(
158 ls,
159 params,
160 root_parser,
161 )
164@lsp_hover(_DISPATCH_RULE)
165def debputy_manifest_hover(
166 ls: "DebputyLanguageServer",
167 params: HoverParams,
168) -> Hover | None:
169 return generic_yaml_hover(
170 ls,
171 params,
172 lambda pg: pg.dispatchable_object_parsers[OPARSER_MANIFEST_ROOT],
173 show_integration_mode=True,
174 )