-
-
Notifications
You must be signed in to change notification settings - Fork 138
/
Copy pathprint_location.py
82 lines (63 loc) · 2.75 KB
/
print_location.py
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
"""Print location in GraphQL source"""
from __future__ import annotations
import re
from typing import TYPE_CHECKING, Tuple, cast
from .location import SourceLocation, get_location
if TYPE_CHECKING:
from .ast import Location
from .source import Source
__all__ = ["print_location", "print_source_location"]
def print_location(location: Location) -> str:
"""Render a helpful description of the location in the GraphQL Source document."""
return print_source_location(
location.source, get_location(location.source, location.start)
)
_re_newline = re.compile(r"\r\n|[\n\r]")
def print_source_location(source: Source, source_location: SourceLocation) -> str:
"""Render a helpful description of the location in the GraphQL Source document."""
first_line_column_offset = source.location_offset.column - 1
body = "".rjust(first_line_column_offset) + source.body
line_index = source_location.line - 1
line_offset = source.location_offset.line - 1
line_num = source_location.line + line_offset
column_offset = first_line_column_offset if source_location.line == 1 else 0
column_num = source_location.column + column_offset
location_str = f"{source.name}:{line_num}:{column_num}\n"
lines = _re_newline.split(body) # works a bit different from splitlines()
location_line = lines[line_index]
# Special case for minified documents
if len(location_line) > 120:
sub_line_index, sub_line_column_num = divmod(column_num, 80)
sub_lines = [
location_line[i : i + 80] for i in range(0, len(location_line), 80)
]
return location_str + print_prefixed_lines(
(f"{line_num} |", sub_lines[0]),
*[("|", sub_line) for sub_line in sub_lines[1 : sub_line_index + 1]],
("|", "^".rjust(sub_line_column_num)),
(
"|",
sub_lines[sub_line_index + 1]
if sub_line_index < len(sub_lines) - 1
else None,
),
)
return location_str + print_prefixed_lines(
(f"{line_num - 1} |", lines[line_index - 1] if line_index > 0 else None),
(f"{line_num} |", location_line),
("|", "^".rjust(column_num)),
(
f"{line_num + 1} |",
lines[line_index + 1] if line_index < len(lines) - 1 else None,
),
)
def print_prefixed_lines(*lines: tuple[str, str | None]) -> str:
"""Print lines specified like this: ("prefix", "string")"""
existing_lines = [
cast(Tuple[str, str], line) for line in lines if line[1] is not None
]
pad_len = max(len(line[0]) for line in existing_lines)
return "\n".join(
prefix.rjust(pad_len) + (" " + line if line else "")
for prefix, line in existing_lines
)