From df6d04cb8c9afe6b067239555c570f4293499a78 Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Thu, 3 Feb 2022 15:55:13 +0000 Subject: [PATCH] Passing through token reference information --- src/textual/css/_styles_builder.py | 4 +++- src/textual/css/parse.py | 26 ++++++++++++++++---------- src/textual/css/styles.py | 5 ----- src/textual/css/stylesheet.py | 20 +++++++++++++++++--- src/textual/css/tokenizer.py | 22 +++++++++++++--------- 5 files changed, 49 insertions(+), 28 deletions(-) diff --git a/src/textual/css/_styles_builder.py b/src/textual/css/_styles_builder.py index c3ea4661d..e2f4a4ba0 100644 --- a/src/textual/css/_styles_builder.py +++ b/src/textual/css/_styles_builder.py @@ -305,7 +305,9 @@ class StylesBuilder: try: style = Style.parse(style_definition) except Exception as error: - self.error(name, tokens[0], f"failed to parse style; {error}") + self.error( + name, tokens[0], f"failed to parse style {style_definition!r}; {error}" + ) if important: self.styles.important.update( {"text_style", "text_background", "text_color"} diff --git a/src/textual/css/parse.py b/src/textual/css/parse.py index 0429b7953..78aef6bd5 100644 --- a/src/textual/css/parse.py +++ b/src/textual/css/parse.py @@ -5,6 +5,7 @@ from functools import lru_cache from typing import Iterator, Iterable from rich import print +from rich.cells import cell_len from textual.css.errors import UnresolvedVariableError from ._styles_builder import StylesBuilder, DeclarationError @@ -18,7 +19,7 @@ from .model import ( ) from .styles import Styles from .tokenize import tokenize, tokenize_declarations, Token -from .tokenizer import EOFError +from .tokenizer import EOFError, ReferencedAt SELECTOR_MAP: dict[str, tuple[SelectorType, tuple[int, int, int]]] = { "selector": (SelectorType.TYPE, (0, 0, 1)), @@ -260,12 +261,14 @@ def substitute_references(tokens: Iterator[Token]) -> Iterable[Token]: reference_tokens = variables[ref_name] variable_tokens.extend(reference_tokens) ref_location = token.location - ref_length = token.length + ref_length = cell_len(token.value) for token in reference_tokens: - - yield token.as_reference( - location=ref_location, - length=ref_length, + yield token.ref( + ReferencedAt( + name=ref_name, + location=ref_location, + length=ref_length, + ) ) else: raise _unresolved( @@ -279,11 +282,14 @@ def substitute_references(tokens: Iterator[Token]) -> Iterable[Token]: if variable_name in variables: variable_tokens = variables[variable_name] ref_location = token.location - ref_length = token.length + ref_length = cell_len(token.value) for token in variable_tokens: - yield token.as_reference( - location=ref_location, - length=ref_length, + yield token.ref( + ReferencedAt( + name=variable_name, + location=ref_location, + length=ref_length, + ) ) else: raise _unresolved(variable_name=variable_name, location=token.location) diff --git a/src/textual/css/styles.py b/src/textual/css/styles.py index 555e710ea..5edcca0b7 100644 --- a/src/textual/css/styles.py +++ b/src/textual/css/styles.py @@ -1,6 +1,5 @@ from __future__ import annotations -import sys from dataclasses import dataclass, field from functools import lru_cache from typing import Any, Iterable, NamedTuple, TYPE_CHECKING @@ -34,14 +33,10 @@ from .scalar import Scalar, ScalarOffset, Unit from .scalar_animation import ScalarAnimation from .transition import Transition from .types import Display, Edge, Visibility - - from .types import Specificity3, Specificity4 -from .. import log from .._animator import Animation, EasingFunction from ..geometry import Spacing - if TYPE_CHECKING: from ..layout import Layout from ..dom import DOMNode diff --git a/src/textual/css/stylesheet.py b/src/textual/css/stylesheet.py index c53745d39..a45587325 100644 --- a/src/textual/css/stylesheet.py +++ b/src/textual/css/stylesheet.py @@ -6,6 +6,7 @@ import os from typing import Iterable import rich.repr +from rich.cells import cell_len from rich.highlighter import ReprHighlighter from rich.panel import Panel from rich.text import Text @@ -46,9 +47,22 @@ class StylesheetErrors: append = errors.append for rule in self.stylesheet.rules: for token, message in rule.errors: - line_no, col_no = token.location - append(highlighter(f"{token.path or ''}:{line_no}")) - append(self._get_snippet(token.code, line_no, col_no, token.length + 1)) + if token.referenced_at: + line_no, col_no = token.referenced_at.location + append(highlighter(f"{token.path or ''}:{line_no}")) + append( + self._get_snippet( + token.code, line_no, col_no, token.referenced_at.length + 1 + ) + ) + else: + line_no, col_no = token.location + append(highlighter(f"{token.path or ''}:{line_no}")) + append( + self._get_snippet( + token.code, line_no, col_no, cell_len(token.value) + 1 + ) + ) append(highlighter(Text(message, "red"))) append("") return Group(*errors) diff --git a/src/textual/css/tokenizer.py b/src/textual/css/tokenizer.py index ffba26e4b..6260903c9 100644 --- a/src/textual/css/tokenizer.py +++ b/src/textual/css/tokenizer.py @@ -39,6 +39,12 @@ class Expect: yield from zip(self.names, self.regexes) +class ReferencedAt(NamedTuple): + name: str + location: tuple[int, int] + length: int + + @rich.repr.auto class Token(NamedTuple): name: str @@ -46,16 +52,16 @@ class Token(NamedTuple): path: str code: str location: tuple[int, int] - length: int + referenced_at: ReferencedAt | None - def as_reference(self, location: tuple[int, int], length: int) -> "Token": + def ref(self, at: ReferencedAt | None) -> "Token": return Token( name=self.name, value=self.value, path=self.path, code=self.code, - location=location, - length=length, + location=self.location, + referenced_at=at, ) def __str__(self) -> str: @@ -66,7 +72,7 @@ class Token(NamedTuple): yield "value", self.value yield "path", self.path yield "location", self.location - yield "length", self.length + yield "referenced_at", self.referenced_at class Tokenizer: @@ -82,9 +88,7 @@ class Tokenizer: col_no = self.col_no if line_no >= len(self.lines): if expect._expect_eof: - return Token( - "eof", "", self.path, self.code, (line_no, col_no), length=0 - ) + return Token("eof", "", self.path, self.code, (line_no, col_no), None) else: raise EOFError() line = self.lines[line_no] @@ -103,7 +107,7 @@ class Tokenizer: break token = Token( - name, value, self.path, self.code, (line_no, col_no), length=cell_len(value) + name, value, self.path, self.code, (line_no, col_no), referenced_at=None ) col_no += len(value) if col_no >= len(line):