Skip to content

Commit

Permalink
Merge pull request #3659 from Textualize/off-by-one
Browse files Browse the repository at this point in the history
Be consistent with line/col numbers.
  • Loading branch information
rodrigogiraoserrao authored Nov 15, 2023
2 parents 7a25bb1 + 9c89524 commit e819e97
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 15 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Fixed outline not rendering correctly in some scenarios (e.g. on Button widgets) https://github.com/Textualize/textual/issues/3628
- Fixed live-reloading of screen CSS https://github.com/Textualize/textual/issues/3454
- `Select.value` could be in an invalid state https://github.com/Textualize/textual/issues/3612
- Off-by-one in CSS error reporting https://github.com/Textualize/textual/issues/3625

### Added

Expand Down
29 changes: 17 additions & 12 deletions src/textual/css/tokenizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ def __init__(
Args:
read_from: The location where the CSS was read from.
code: The code being parsed.
start: Line number of the error.
start: Line and column number of the error (1-indexed).
message: A message associated with the error.
end: End location of token, or None if not known.
end: End location of token (1-indexed), or None if not known.
"""

self.read_from = read_from
Expand All @@ -60,9 +60,13 @@ def _get_snippet(self) -> Panel:
line_numbers=True,
indent_guides=True,
line_range=(max(0, line_no - 2), line_no + 2),
highlight_lines={line_no + 1},
highlight_lines={line_no},
)
syntax.stylize_range(
"reverse bold",
(self.start[0], self.start[1] - 1),
(self.end[0], self.end[1] - 1),
)
syntax.stylize_range("reverse bold", self.start, self.end)
return Panel(syntax, border_style="red")

def __rich__(self) -> RenderableType:
Expand Down Expand Up @@ -136,19 +140,20 @@ class Token(NamedTuple):
read_from: CSSLocation
code: str
location: tuple[int, int]
"""Token starting location, 0-indexed."""
referenced_by: ReferencedBy | None = None

@property
def start(self) -> tuple[int, int]:
"""Start line and column (1 indexed)."""
"""Start line and column (1-indexed)."""
line, offset = self.location
return (line + 1, offset)
return (line + 1, offset + 1)

@property
def end(self) -> tuple[int, int]:
"""End line and column (1 indexed)."""
"""End line and column (1-indexed)."""
line, offset = self.location
return (line + 1, offset + len(self.value))
return (line + 1, offset + len(self.value) + 1)

def with_reference(self, by: ReferencedBy | None) -> "Token":
"""Return a copy of the Token, with reference information attached.
Expand Down Expand Up @@ -199,7 +204,7 @@ def get_token(self, expect: Expect) -> Token:
"",
self.read_from,
self.code,
(line_no + 1, col_no + 1),
(line_no, col_no),
None,
)
else:
Expand All @@ -217,7 +222,7 @@ def get_token(self, expect: Expect) -> Token:
raise TokenError(
self.read_from,
self.code,
(line_no, col_no),
(line_no + 1, col_no + 1),
message,
)
iter_groups = iter(match.groups())
Expand Down Expand Up @@ -251,14 +256,14 @@ def get_token(self, expect: Expect) -> Token:
raise TokenError(
self.read_from,
self.code,
(line_no, col_no),
(line_no + 1, col_no + 1),
f"unknown pseudo-class {pseudo_class!r}; did you mean {suggestion!r}?; {all_valid}",
)
else:
raise TokenError(
self.read_from,
self.code,
(line_no, col_no),
(line_no + 1, col_no + 1),
f"unknown pseudo-class {pseudo_class!r}; {all_valid}",
)

Expand Down
24 changes: 21 additions & 3 deletions tests/css/test_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -1238,7 +1238,7 @@ def test_combined_type_starts_with_number(self):
stylesheet.parse()


def test_parse_bad_psuedo_selector():
def test_parse_bad_pseudo_selector():
"""Check unknown selector raises a token error."""

bad_selector = """\
Expand All @@ -1248,9 +1248,27 @@ def test_parse_bad_psuedo_selector():
"""

stylesheet = Stylesheet()
stylesheet.add_source(bad_selector, "foo")
stylesheet.add_source(bad_selector, None)

with pytest.raises(TokenError) as error:
stylesheet.parse()

assert error.value.start == (0, 6)
assert error.value.start == (1, 7)


def test_parse_bad_pseudo_selector_with_suggestion():
"""Check unknown pseudo selector raises token error with correct position."""

bad_selector = """
Widget:blu {
border: red;
}
"""

stylesheet = Stylesheet()
stylesheet.add_source(bad_selector, None)

with pytest.raises(TokenError) as error:
stylesheet.parse()

assert error.value.start == (2, 7)

0 comments on commit e819e97

Please sign in to comment.