Coverage for src/debputy/lsp/lsp_debian_copyright.py: 84%

75 statements  

« prev     ^ index     » next       coverage.py v7.6.0, created at 2025-03-24 16:38 +0000

1import re 

2from typing import ( 

3 Union, 

4 Sequence, 

5 Optional, 

6 List, 

7) 

8 

9from debputy.linting.lint_util import LintState 

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) 

26from debputy.lsp.lsp_generic_deb822 import ( 

27 deb822_completer, 

28 deb822_hover, 

29 deb822_folding_ranges, 

30 deb822_semantic_tokens_full, 

31 deb822_format_file, 

32 scan_for_syntax_errors_and_token_level_diagnostics, 

33) 

34from debputy.lsp.vendoring._deb822_repro import ( 

35 Deb822FileElement, 

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 debputy.lsp.vendoring._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 "debian/copyright", 

71 [ 

72 # emacs's name 

73 SecondaryLanguage("debian-copyright"), 

74 # vim's name 

75 SecondaryLanguage("debcopyright"), 

76 ], 

77) 

78 

79_DEP5_FILE_METADATA = Dep5FileMetadata() 

80 

81lsp_standard_handler(_DISPATCH_RULE, TEXT_DOCUMENT_CODE_ACTION) 

82 

83 

84@lsp_hover(_DISPATCH_RULE) 

85def _debian_copyright_hover( 

86 ls: "DebputyLanguageServer", 

87 params: HoverParams, 

88) -> Optional[Hover]: 

89 return deb822_hover(ls, params, _DEP5_FILE_METADATA) 

90 

91 

92@lsp_completer(_DISPATCH_RULE) 

93def _debian_copyright_completions( 

94 ls: "DebputyLanguageServer", 

95 params: CompletionParams, 

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

97 return deb822_completer(ls, params, _DEP5_FILE_METADATA) 

98 

99 

100@lsp_folding_ranges(_DISPATCH_RULE) 

101def _debian_copyright_folding_ranges( 

102 ls: "DebputyLanguageServer", 

103 params: FoldingRangeParams, 

104) -> Optional[Sequence[FoldingRange]]: 

105 return deb822_folding_ranges(ls, params, _DEP5_FILE_METADATA) 

106 

107 

108def _looks_like_a_dep5_file( 

109 deb822_file: Deb822FileElement, 

110 stanzas: List[Deb822ParagraphElement], 

111) -> bool: 

112 if not stanzas or "Format" not in stanzas[0]: 

113 # No parseable stanzas or the first one did not have a Format, which is necessary. 

114 return False 

115 

116 for part in deb822_file.iter_parts(): 116 ↛ 122line 116 didn't jump to line 122 because the loop on line 116 didn't complete

117 if part.is_error: 

118 # Error first, then it might just be a "Format:" in the middle of a free-text file. 

119 return False 

120 if isinstance(part, Deb822ParagraphElement): 120 ↛ 116line 120 didn't jump to line 116 because the condition on line 120 was always true

121 break 

122 return True 

123 

124 

125@lint_diagnostics(_DISPATCH_RULE) 

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

127 deb822_file = lint_state.parsed_deb822_file_content 

128 stanzas = list(deb822_file) 

129 

130 if not _looks_like_a_dep5_file(deb822_file, stanzas): 

131 return 

132 

133 first_error = await scan_for_syntax_errors_and_token_level_diagnostics( 

134 deb822_file, 

135 lint_state, 

136 ) 

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

138 

139 async for paragraph_no, paragraph in lint_state.slow_iter( 

140 enumerate(stanzas, start=1) 

141 ): 

142 paragraph_pos = paragraph.position_in_file() 

143 if paragraph_pos.line_position >= first_error: 143 ↛ 144line 143 didn't jump to line 144 because the condition on line 143 was never true

144 break 

145 is_files_or_license_stanza = paragraph_no != 1 

146 if is_files_or_license_stanza: 

147 stanza_metadata = _DEP5_FILE_METADATA.classify_stanza( 

148 paragraph, 

149 paragraph_no, 

150 ) 

151 other_stanza_metadata = header_stanza 

152 other_stanza_name = "Header" 

153 elif "Format" in paragraph: 153 ↛ 158line 153 didn't jump to line 158 because the condition on line 153 was always true

154 stanza_metadata = header_stanza 

155 other_stanza_metadata = files_stanza 

156 other_stanza_name = "Files/License" 

157 else: 

158 break 

159 

160 await stanza_metadata.stanza_diagnostics( 

161 deb822_file, 

162 paragraph, 

163 paragraph_pos, 

164 lint_state, 

165 confusable_with_stanza_name=other_stanza_name, 

166 confusable_with_stanza_metadata=other_stanza_metadata, 

167 ) 

168 

169 

170@lsp_will_save_wait_until(_DISPATCH_RULE) 

171def _debian_copyright_on_save_formatting( 

172 ls: "DebputyLanguageServer", 

173 params: WillSaveTextDocumentParams, 

174) -> Optional[Sequence[TextEdit]]: 

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

176 lint_state = ls.lint_state(doc) 

177 return deb822_format_file(lint_state, _DEP5_FILE_METADATA) 

178 

179 

180def _reformat_debian_copyright( 

181 lint_state: LintState, 

182) -> Optional[Sequence[TextEdit]]: 

183 return deb822_format_file(lint_state, _DEP5_FILE_METADATA) 

184 

185 

186@lsp_format_document(_DISPATCH_RULE) 

187def _debian_copyright_on_save_formatting( 

188 ls: "DebputyLanguageServer", 

189 params: DocumentFormattingParams, 

190) -> Optional[Sequence[TextEdit]]: 

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

192 lint_state = ls.lint_state(doc) 

193 return deb822_format_file(lint_state, _DEP5_FILE_METADATA) 

194 

195 

196@lsp_semantic_tokens_full(_DISPATCH_RULE) 

197async def _debian_copyright_semantic_tokens_full( 

198 ls: "DebputyLanguageServer", 

199 request: SemanticTokensParams, 

200) -> Optional[SemanticTokens]: 

201 return await deb822_semantic_tokens_full( 

202 ls, 

203 request, 

204 _DEP5_FILE_METADATA, 

205 )