1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
import re
from typing import (
Optional,
List,
)
from lsprotocol.types import (
Diagnostic,
TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL,
Position,
Range,
DiagnosticSeverity,
)
from ruamel.yaml.error import MarkedYAMLError, YAMLError
from debputy.highlevel_manifest import MANIFEST_YAML
from debputy.lsp.lsp_features import (
lint_diagnostics,
lsp_standard_handler,
)
from debputy.lsp.text_util import (
LintCapablePositionCodec,
)
try:
from pygls.server import LanguageServer
except ImportError:
pass
_CONTAINS_TAB_OR_COLON = re.compile(r"[\t:]")
_WORDS_RE = re.compile("([a-zA-Z0-9_-]+)")
_MAKE_ERROR_RE = re.compile(r"^[^:]+:(\d+):\s*(\S.+)")
_LANGUAGE_IDS = [
"debian/debputy.manifest",
"debputy.manifest",
# LSP's official language ID for YAML files
"yaml",
]
# lsp_standard_handler(_LANGUAGE_IDS, TEXT_DOCUMENT_CODE_ACTION)
lsp_standard_handler(_LANGUAGE_IDS, TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL)
def is_valid_file(path: str) -> bool:
# For debian/debputy.manifest, the language ID is often set to makefile meaning we get random
# "non-debian/debputy.manifest" YAML files here. Skip those.
return path.endswith("debian/debputy.manifest")
def _word_range_at_position(
lines: List[str],
line_no: int,
char_offset: int,
) -> Range:
line = lines[line_no]
line_len = len(line)
start_idx = char_offset
end_idx = char_offset
while end_idx + 1 < line_len and not line[end_idx + 1].isspace():
end_idx += 1
while start_idx - 1 >= 0 and not line[start_idx - 1].isspace():
start_idx -= 1
return Range(
Position(line_no, start_idx),
Position(line_no, end_idx),
)
@lint_diagnostics(_LANGUAGE_IDS)
def _lint_debian_debputy_manifest(
_doc_reference: str,
path: str,
lines: List[str],
position_codec: LintCapablePositionCodec,
) -> Optional[List[Diagnostic]]:
if not is_valid_file(path):
return None
diagnostics = []
try:
MANIFEST_YAML.load("".join(lines))
except MarkedYAMLError as e:
error_range = position_codec.range_to_client_units(
lines,
_word_range_at_position(
lines,
e.problem_mark.line,
e.problem_mark.column,
),
)
diagnostics.append(
Diagnostic(
error_range,
f"YAML parse error: {e}",
DiagnosticSeverity.Error,
),
)
except YAMLError as e:
error_range = position_codec.range_to_client_units(
lines,
Range(
Position(0, 0),
Position(0, len(lines[0])),
),
)
diagnostics.append(
Diagnostic(
error_range,
f"Unknown YAML parse error: {e} [{e!r}]",
DiagnosticSeverity.Error,
),
)
return diagnostics
|