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
« 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)
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)
54try:
55 from debputy.lsp.vendoring._deb822_repro.locatable import (
56 Position as TEPosition,
57 Range as TERange,
58 )
60 from pygls.server import LanguageServer
61 from pygls.workspace import TextDocument
62except ImportError:
63 pass
66_CONTAINS_SPACE_OR_COLON = re.compile(r"[\s:]")
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)
79_DEP5_FILE_METADATA = Dep5FileMetadata()
81lsp_standard_handler(_DISPATCH_RULE, TEXT_DOCUMENT_CODE_ACTION)
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)
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)
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)
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
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
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)
130 if not _looks_like_a_dep5_file(deb822_file, stanzas):
131 return
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()
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
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 )
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)
180def _reformat_debian_copyright(
181 lint_state: LintState,
182) -> Optional[Sequence[TextEdit]]:
183 return deb822_format_file(lint_state, _DEP5_FILE_METADATA)
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)
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 )