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