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

1from typing import ( 

2 Optional, 

3 Any, 

4 Tuple, 

5 Union, 

6) 

7from collections.abc import Sequence, Container 

8 

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 

41 

42try: 

43 from pygls.server import LanguageServer 

44 from debputy.lsp.debputy_ls import DebputyLanguageServer 

45except ImportError: 

46 pass 

47 

48 

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) 

59 

60 

61lsp_standard_handler(_DISPATCH_RULE, TEXT_DOCUMENT_CODE_ACTION) 

62lsp_standard_handler(_DISPATCH_RULE, TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL) 

63 

64 

65class DebputyManifestLSPYAMLHelper(LSPYAMLHelper[DebputyMetadata]): 

66 

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 ) 

82 

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 

106 

107 return super()._type_based_value_check( 

108 target_attr_type, 

109 value, 

110 value_pos, 

111 key=key, 

112 ) 

113 

114 

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 ) 

121 

122 

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 ) 

132 

133 

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 ) 

153 

154 

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 ) 

167 

168 

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 )