Coverage for src/debputy/lsp/languages/lsp_debian_debputy_manifest.py: 79%

53 statements  

« prev     ^ index     » next       coverage.py v7.8.2, created at 2025-09-07 09:27 +0000

1from typing import ( 

2 Optional, 

3 Any, 

4 Tuple, 

5 Union, 

6 Sequence, 

7 Container, 

8) 

9 

10from debputy.linting.lint_util import LintState, DebputyMetadata 

11from debputy.lsp.lsp_features import ( 

12 lint_diagnostics, 

13 lsp_standard_handler, 

14 lsp_hover, 

15 lsp_completer, 

16 SecondaryLanguage, 

17 LanguageDispatchRule, 

18) 

19from debputy.lsp.lsp_generic_yaml import ( 

20 yaml_key_range, 

21 generic_yaml_hover, 

22 LSPYAMLHelper, 

23 generic_yaml_lint, 

24 generic_yaml_completer, 

25 _is_empty_range, 

26) 

27from debputy.lsprotocol.types import ( 

28 TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL, 

29 HoverParams, 

30 Hover, 

31 TEXT_DOCUMENT_CODE_ACTION, 

32 CompletionParams, 

33 CompletionList, 

34 CompletionItem, 

35) 

36from debputy.plugin.api.impl_types import ( 

37 PluginProvidedParser, 

38) 

39from debputy.plugin.api.parser_tables import OPARSER_MANIFEST_ROOT 

40from debputy.plugin.api.spec import DebputyIntegrationMode 

41from debputy.plugins.debputy.private_api import Capability, load_libcap 

42 

43try: 

44 from pygls.server import LanguageServer 

45 from debputy.lsp.debputy_ls import DebputyLanguageServer 

46except ImportError: 

47 pass 

48 

49 

50_DISPATCH_RULE = LanguageDispatchRule.new_rule( 

51 "debian/debputy.manifest", 

52 "debputy.manifest", 

53 "debian/debputy.manifest", 

54 [ 

55 SecondaryLanguage("debputy.manifest"), 

56 # LSP's official language ID for YAML files 

57 SecondaryLanguage("yaml", secondary_lookup="path-name"), 

58 ], 

59) 

60 

61 

62lsp_standard_handler(_DISPATCH_RULE, TEXT_DOCUMENT_CODE_ACTION) 

63lsp_standard_handler(_DISPATCH_RULE, TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL) 

64 

65 

66class DebputyManifestLSPYAMLHelper(LSPYAMLHelper[DebputyMetadata]): 

67 

68 def _validate_subparser_is_valid_here( 

69 self, 

70 subparser: PluginProvidedParser, 

71 orig_key: str, 

72 line: int, 

73 col: int, 

74 ) -> None: 

75 _integration_mode_allows_key( 

76 self.lint_state, 

77 self.custom_data.debputy_integration_mode, 

78 subparser.parser.expected_debputy_integration_mode, 

79 orig_key, 

80 line, 

81 col, 

82 ) 

83 

84 def _type_based_value_check( 

85 self, 

86 target_attr_type: type, 

87 value: Any, 

88 value_pos: Tuple[int, int], 

89 *, 

90 key: Optional[Union[str, int]] = None, 

91 ) -> bool: 

92 if issubclass(target_attr_type, Capability): 92 ↛ 93line 92 didn't jump to line 93 because the condition on line 92 was never true

93 has_libcap, _, is_valid_cap = load_libcap() 

94 if has_libcap and not is_valid_cap(value): 

95 line_no, cursor_pos = value_pos 

96 cap_range = self._remaining_line(line_no, cursor_pos) 

97 if _is_empty_range(cap_range): 

98 # FIXME: We cannot report an empty range, but there is still a problem here. 

99 return True 

100 self.lint_state.emit_diagnostic( 

101 cap_range, 

102 "The value could not be parsed as a capability via cap_from_text on this system", 

103 "warning", 

104 "debputy", 

105 ) 

106 return True 

107 

108 return super()._type_based_value_check( 

109 target_attr_type, 

110 value, 

111 value_pos, 

112 key=key, 

113 ) 

114 

115 

116def _initialize_yaml_helper(lint_state: LintState) -> DebputyManifestLSPYAMLHelper: 

117 return DebputyManifestLSPYAMLHelper( 

118 lint_state, 

119 lint_state.plugin_feature_set.manifest_parser_generator, 

120 lint_state.debputy_metadata, 

121 ) 

122 

123 

124@lint_diagnostics(_DISPATCH_RULE) 

125async def _lint_debian_debputy_manifest(lint_state: LintState) -> None: 

126 pg = lint_state.plugin_feature_set.manifest_parser_generator 

127 root_parser = pg.dispatchable_object_parsers[OPARSER_MANIFEST_ROOT] 

128 await generic_yaml_lint( 

129 lint_state, 

130 root_parser, 

131 _initialize_yaml_helper, 

132 ) 

133 

134 

135def _integration_mode_allows_key( 

136 lint_state: LintState, 

137 debputy_integration_mode: Optional[DebputyIntegrationMode], 

138 expected_debputy_integration_modes: Optional[Container[DebputyIntegrationMode]], 

139 key: str, 

140 line: int, 

141 col: int, 

142) -> None: 

143 if debputy_integration_mode is None or expected_debputy_integration_modes is None: 

144 return 

145 if debputy_integration_mode in expected_debputy_integration_modes: 

146 return 

147 key_range = yaml_key_range(key, line, col) 

148 lint_state.emit_diagnostic( 

149 key_range, 

150 f'Feature "{key}" not supported in integration mode {debputy_integration_mode}', 

151 "error", 

152 "debputy", 

153 ) 

154 

155 

156@lsp_completer(_DISPATCH_RULE) 

157def debputy_manifest_completer( 

158 ls: "DebputyLanguageServer", 

159 params: CompletionParams, 

160) -> Optional[Union[CompletionList, Sequence[CompletionItem]]]: 

161 pg = ls.plugin_feature_set.manifest_parser_generator 

162 root_parser = pg.dispatchable_object_parsers[OPARSER_MANIFEST_ROOT] 

163 return generic_yaml_completer( 

164 ls, 

165 params, 

166 root_parser, 

167 ) 

168 

169 

170@lsp_hover(_DISPATCH_RULE) 

171def debputy_manifest_hover( 

172 ls: "DebputyLanguageServer", 

173 params: HoverParams, 

174) -> Optional[Hover]: 

175 return generic_yaml_hover( 

176 ls, 

177 params, 

178 lambda pg: pg.dispatchable_object_parsers[OPARSER_MANIFEST_ROOT], 

179 show_integration_mode=True, 

180 )