Coverage for src/debputy/lsp/languages/lsp_debian_copyright.py: 82%

71 statements  

« prev     ^ index     » next       coverage.py v7.8.2, created at 2026-02-28 21:56 +0000

1import re 

2from typing import ( 

3 Union, 

4 Optional, 

5 List, 

6) 

7from collections.abc import Sequence 

8 

9from debputy.linting.lint_util import LintState, with_range_in_continuous_parts 

10from debputy.lsp.debputy_ls import DebputyLanguageServer 

11from debputy.lsp.lsp_debian_control_reference_data import ( 

12 Dep5FileMetadata, 

13) 

14from debputy.lsp.lsp_features import ( 

15 lint_diagnostics, 

16 lsp_completer, 

17 lsp_hover, 

18 lsp_standard_handler, 

19 lsp_folding_ranges, 

20 lsp_semantic_tokens_full, 

21 lsp_will_save_wait_until, 

22 lsp_format_document, 

23 SecondaryLanguage, 

24 LanguageDispatchRule, 

25 lsp_cli_reformat_document, 

26) 

27from debputy.lsp.lsp_generic_deb822 import ( 

28 deb822_completer, 

29 deb822_hover, 

30 deb822_folding_ranges, 

31 deb822_semantic_tokens_full, 

32 deb822_format_file, 

33 scan_for_syntax_errors_and_token_level_diagnostics, 

34) 

35from debian._deb822_repro import ( 

36 Deb822ParagraphElement, 

37) 

38from debputy.lsprotocol.types import ( 

39 CompletionItem, 

40 CompletionList, 

41 CompletionParams, 

42 HoverParams, 

43 Hover, 

44 TEXT_DOCUMENT_CODE_ACTION, 

45 SemanticTokens, 

46 SemanticTokensParams, 

47 FoldingRangeParams, 

48 FoldingRange, 

49 WillSaveTextDocumentParams, 

50 TextEdit, 

51 DocumentFormattingParams, 

52) 

53 

54try: 

55 from debian._deb822_repro.locatable import ( 

56 Position as TEPosition, 

57 Range as TERange, 

58 ) 

59 

60 from pygls.server import LanguageServer 

61 from pygls.workspace import TextDocument 

62except ImportError: 

63 pass 

64 

65 

66_CONTAINS_SPACE_OR_COLON = re.compile(r"[\s:]") 

67 

68_DISPATCH_RULE = LanguageDispatchRule.new_rule( 

69 "debian/copyright", 

70 None, 

71 "debian/copyright", 

72 [ 

73 # emacs's name 

74 SecondaryLanguage("debian-copyright"), 

75 # vim's name 

76 SecondaryLanguage("debcopyright"), 

77 ], 

78) 

79 

80_DEP5_FILE_METADATA = Dep5FileMetadata() 

81 

82lsp_standard_handler(_DISPATCH_RULE, TEXT_DOCUMENT_CODE_ACTION) 

83 

84 

85@lsp_hover(_DISPATCH_RULE) 

86def _debian_copyright_hover( 

87 ls: "DebputyLanguageServer", 

88 params: HoverParams, 

89) -> Hover | None: 

90 return deb822_hover(ls, params, _DEP5_FILE_METADATA) 

91 

92 

93@lsp_completer(_DISPATCH_RULE) 

94def _debian_copyright_completions( 

95 ls: "DebputyLanguageServer", 

96 params: CompletionParams, 

97) -> CompletionList | Sequence[CompletionItem] | None: 

98 return deb822_completer(ls, params, _DEP5_FILE_METADATA) 

99 

100 

101@lsp_folding_ranges(_DISPATCH_RULE) 

102def _debian_copyright_folding_ranges( 

103 ls: "DebputyLanguageServer", 

104 params: FoldingRangeParams, 

105) -> Sequence[FoldingRange] | None: 

106 return deb822_folding_ranges(ls, params, _DEP5_FILE_METADATA) 

107 

108 

109@lint_diagnostics(_DISPATCH_RULE) 

110async def _lint_debian_copyright(lint_state: LintState) -> None: 

111 deb822_file = lint_state.parsed_deb822_file_content 

112 

113 if not _DEP5_FILE_METADATA.file_metadata_applies_to_file(deb822_file): 

114 return 

115 

116 first_error = await scan_for_syntax_errors_and_token_level_diagnostics( 

117 deb822_file, 

118 lint_state, 

119 ) 

120 header_stanza, files_stanza, _ = _DEP5_FILE_METADATA.stanza_types() 

121 stanza_no = 0 

122 

123 async for stanza_range, stanza in lint_state.slow_iter( 

124 with_range_in_continuous_parts(deb822_file.iter_parts()) 

125 ): 

126 if not isinstance(stanza, Deb822ParagraphElement): 

127 continue 

128 stanza_position = stanza_range.start_pos 

129 if stanza_position.line_position >= first_error: 129 ↛ 130line 129 didn't jump to line 130 because the condition on line 129 was never true

130 break 

131 stanza_no += 1 

132 is_files_or_license_stanza = stanza_no != 1 

133 if is_files_or_license_stanza: 

134 stanza_metadata = _DEP5_FILE_METADATA.classify_stanza( 

135 stanza, 

136 stanza_no, 

137 ) 

138 other_stanza_metadata = header_stanza 

139 other_stanza_name = "Header" 

140 elif "Format" in stanza: 140 ↛ 145line 140 didn't jump to line 145 because the condition on line 140 was always true

141 stanza_metadata = header_stanza 

142 other_stanza_metadata = files_stanza 

143 other_stanza_name = "Files/License" 

144 else: 

145 break 

146 

147 await stanza_metadata.stanza_diagnostics( 

148 deb822_file, 

149 stanza, 

150 stanza_position, 

151 lint_state, 

152 confusable_with_stanza_name=other_stanza_name, 

153 confusable_with_stanza_metadata=other_stanza_metadata, 

154 ) 

155 

156 

157@lsp_will_save_wait_until(_DISPATCH_RULE) 

158def _debian_copyright_on_save_formatting( 

159 ls: "DebputyLanguageServer", 

160 params: WillSaveTextDocumentParams, 

161) -> Sequence[TextEdit] | None: 

162 doc = ls.workspace.get_text_document(params.text_document.uri) 

163 lint_state = ls.lint_state(doc) 

164 return deb822_format_file(lint_state, _DEP5_FILE_METADATA) 

165 

166 

167@lsp_cli_reformat_document(_DISPATCH_RULE) 

168def _reformat_debian_copyright( 

169 lint_state: LintState, 

170) -> Sequence[TextEdit] | None: 

171 return deb822_format_file(lint_state, _DEP5_FILE_METADATA) 

172 

173 

174@lsp_format_document(_DISPATCH_RULE) 

175def _debian_copyright_format_document( 

176 ls: "DebputyLanguageServer", 

177 params: DocumentFormattingParams, 

178) -> Sequence[TextEdit] | None: 

179 doc = ls.workspace.get_text_document(params.text_document.uri) 

180 lint_state = ls.lint_state(doc) 

181 return deb822_format_file(lint_state, _DEP5_FILE_METADATA) 

182 

183 

184@lsp_semantic_tokens_full(_DISPATCH_RULE) 

185async def _debian_copyright_semantic_tokens_full( 

186 ls: "DebputyLanguageServer", 

187 request: SemanticTokensParams, 

188) -> SemanticTokens | None: 

189 return await deb822_semantic_tokens_full( 

190 ls, 

191 request, 

192 _DEP5_FILE_METADATA, 

193 )