From 4f63fc086f3a15dedbccb999bd48795637b2e396 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Mon, 11 Apr 2022 15:25:21 +0100 Subject: [PATCH] require semi-colons --- docs/color_system.md | 2 +- sandbox/basic.css | 8 +- src/textual/app.py | 54 ++- src/textual/css/stylesheet.py | 9 +- src/textual/css/tokenize.py | 4 +- src/textual/css/tokenizer.py | 68 ++- src/textual/dom.py | 2 +- src/textual/message_pump.py | 2 +- src/textual/screen.py | 6 +- tests/css/test_tokenize.py | 768 ++++++++++++++++++++++++++++++---- 10 files changed, 804 insertions(+), 119 deletions(-) diff --git a/docs/color_system.md b/docs/color_system.md index 48ff1d374..7915c39f9 100644 --- a/docs/color_system.md +++ b/docs/color_system.md @@ -34,7 +34,7 @@ The _panel_ color is typically used as a background to emphasize text on the def The default panel color is derived from the surface color by blending it towards either white or black text (depending on mode). -Unlike background and surface, the panel color is automatically selected so that it can always be lightened or darkened by the full degree. +Unlike background and surface, the panel color is automatically selected so that it can always be lightened or darkened by the full range. ### Accent diff --git a/sandbox/basic.css b/sandbox/basic.css index 904e4ca27..575122d26 100644 --- a/sandbox/basic.css +++ b/sandbox/basic.css @@ -86,8 +86,8 @@ Tweet { TweetHeader { - height:1 - background: $accent + height:1; + background: $accent; color: $text-accent } @@ -103,7 +103,7 @@ TweetBody { background: $accent; color: $text-accent; width:20; - height: 3 + height: 3; /* border-top: hidden $accent-darken-3; */ border: tall $accent-darken-2; /* border-left: tall $accent-darken-1; */ @@ -119,7 +119,7 @@ TweetBody { background: $accent-lighten-1; color: $text-accent-lighten-1; width: 20; - height: 3 + height: 3; border: tall $accent-darken-1; /* border-left: tall $accent-darken-3; */ diff --git a/src/textual/app.py b/src/textual/app.py index 54efbde55..74ed6b872 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -393,20 +393,12 @@ class App(DOMNode): await widget.post_message(events.MouseCapture(self, self.mouse_position)) def panic(self, *renderables: RenderableType) -> None: - """Exits the app with a traceback. + """Exits the app after displaying a message. Args: - *renderables (RenderableType, optional): A rich renderable, such as a Traceback to - display on exit. + *renderables (RenderableType, optional): Rich renderables to display on exit. """ - if not renderables: - renderables = ( - Traceback( - show_locals=True, width=None, locals_max_length=5, suppress=[rich] - ), - ) - prerendered = [ Segments(self.console.render(renderable, self.console.options)) for renderable in renderables @@ -415,6 +407,29 @@ class App(DOMNode): self._exit_renderables.extend(prerendered) self.close_messages_no_wait() + def on_exception(self, error: Exception) -> None: + """Called with an unhandled exception. + + Args: + error (Exception): An exception instance. + """ + if hasattr(error, "__rich__"): + # Exception has a rich method, so we can defer to that for the rendering + self.panic(error) + else: + # Use default exception rendering + self.fatal_error() + + def fatal_error(self) -> None: + """Exits the app after an unhandled exception.""" + traceback = Traceback( + show_locals=True, width=None, locals_max_length=5, suppress=[rich] + ) + self._exit_renderables.append( + Segments(self.console.render(traceback, self.console.options)) + ) + self.close_messages_no_wait() + def _print_error_renderables(self) -> None: for renderable in self._exit_renderables: self.error_console.print(renderable) @@ -430,12 +445,9 @@ class App(DOMNode): self.stylesheet.read(self.css_file) if self.css is not None: self.stylesheet.parse(self.css, path=f"<{self.__class__.__name__}>") - except StylesheetParseError as error: - self.panic(error) - self._print_error_renderables() - return + except Exception as error: - self.panic() + self.on_exception(error) self._print_error_renderables() return @@ -468,8 +480,8 @@ class App(DOMNode): await self.close_all() finally: driver.stop_application_mode() - except: - self.panic() + except Exception as error: + self.on_exception(error) finally: self._running = False if self._exit_renderables: @@ -567,8 +579,8 @@ class App(DOMNode): if sync_available: console.file.write("\x1bP=2s\x1b\\") console.file.flush() - except Exception: - self.panic() + except Exception as error: + self.on_exception(error) def refresh_css(self, animate: bool = True) -> None: """Refresh CSS. @@ -589,8 +601,8 @@ class App(DOMNode): console = self.console try: console.print(renderable) - except Exception: - self.panic() + except Exception as error: + self.on_exception(error) def measure(self, renderable: RenderableType, max_width=100_000) -> int: """Get the optimal width for a widget or renderable. diff --git a/src/textual/css/stylesheet.py b/src/textual/css/stylesheet.py index 04b21dabe..6703c71c8 100644 --- a/src/textual/css/stylesheet.py +++ b/src/textual/css/stylesheet.py @@ -22,6 +22,7 @@ from .model import RuleSet from .parse import parse from .styles import RulesMap from .tokenize import tokenize_values, Token +from .tokenizer import TokenizeError from .types import Specificity3, Specificity4 from ..dom import DOMNode from .. import log @@ -53,7 +54,7 @@ class StylesheetErrors: theme="ansi_light", line_numbers=True, indent_guides=True, - line_range=(max(0, line_no - 2), line_no + 1), + line_range=(max(0, line_no - 2), line_no + 2), highlight_lines={line_no}, ) return Panel(syntax, border_style="red") @@ -147,8 +148,10 @@ class Stylesheet: raise StylesheetError(f"unable to read {filename!r}; {error}") try: rules = list(parse(css, path, variables=self.variables)) + except TokenizeError: + raise except Exception as error: - raise StylesheetError(f"failed to parse {filename!r}; {error}") + raise StylesheetError(f"failed to parse {filename!r}; {error!r}") else: self.source.append((css, path)) self.rules.extend(rules) @@ -168,6 +171,8 @@ class Stylesheet: """ try: rules = list(parse(css, path, variables=self.variables)) + except TokenizeError: + raise except Exception as error: raise StylesheetError(f"failed to parse css; {error}") else: diff --git a/src/textual/css/tokenize.py b/src/textual/css/tokenize.py index 996c4b6ba..3ccc8166f 100644 --- a/src/textual/css/tokenize.py +++ b/src/textual/css/tokenize.py @@ -87,7 +87,7 @@ expect_declaration_solo = Expect( # The value(s)/content from a rule declaration e.g. "text: red;" # ^---^ expect_declaration_content = Expect( - declaration_end=r"\n|;", + declaration_end=r";", whitespace=r"\s+", comment_start=COMMENT_START, **DECLARATION_VALUES, @@ -97,7 +97,7 @@ expect_declaration_content = Expect( ) expect_declaration_content_solo = Expect( - declaration_end=r"\n|;", + declaration_end=r";", whitespace=r"\s+", comment_start=COMMENT_START, **DECLARATION_VALUES, diff --git a/src/textual/css/tokenizer.py b/src/textual/css/tokenizer.py index 4e7fa4e53..659c72252 100644 --- a/src/textual/css/tokenizer.py +++ b/src/textual/css/tokenizer.py @@ -3,19 +3,62 @@ from __future__ import annotations import re from typing import NamedTuple +from rich.console import Group, RenderableType +from rich.highlighter import ReprHighlighter +from rich.padding import Padding +from rich.panel import Panel import rich.repr +from rich.syntax import Syntax +from rich.text import Text - -class EOFError(Exception): - pass +from .._loop import loop_last class TokenizeError(Exception): - def __init__(self, col_no: int, row_no: int, message: str) -> None: + def __init__( + self, path: str, code: str, line_no: int, col_no: int, message: str + ) -> None: + self.path = path + self.code = code + self.line_no = line_no self.col_no = col_no - self.row_no = row_no super().__init__(message) + @classmethod + def _get_snippet(cls, code: str, line_no: int) -> Panel: + syntax = Syntax( + code, + lexer="scss", + theme="ansi_light", + line_numbers=True, + indent_guides=True, + line_range=(max(0, line_no - 2), line_no + 2), + highlight_lines={line_no}, + ) + return Panel(syntax, border_style="red") + + def __rich__(self) -> RenderableType: + highlighter = ReprHighlighter() + errors: list[RenderableType] = [] + append = errors.append + + message = str(self) + append(Text(" Tokenizer error in stylesheet:", style="bold red")) + + append(highlighter(f" {self.path or ''}:{self.line_no}:{self.col_no}")) + append(self._get_snippet(self.code, self.line_no)) + final_message = "" + for is_last, message_part in loop_last(message.split(";")): + end = "" if is_last else "\n" + final_message += f"• {message_part.strip()};{end}" + append(Padding(highlighter(Text(final_message, "red")), pad=(0, 1))) + + return Group(*errors) + + +class EOFError(TokenizeError): + pass + class Expect: def __init__(self, **tokens: str) -> None: @@ -51,7 +94,7 @@ class Token(NamedTuple): path: str code: str location: tuple[int, int] - referenced_by: ReferencedBy | None + referenced_by: ReferencedBy | None = None def with_reference(self, by: ReferencedBy | None) -> "Token": """Return a copy of the Token, with reference information attached. @@ -76,8 +119,9 @@ class Token(NamedTuple): yield "name", self.name yield "value", self.value yield "path", self.path + yield "code", self.code yield "location", self.location - yield "referenced_by", self.referenced_by + yield "referenced_by", self.referenced_by, None class Tokenizer: @@ -95,11 +139,15 @@ class Tokenizer: if expect._expect_eof: return Token("eof", "", self.path, self.code, (line_no, col_no), None) else: - raise EOFError() + raise EOFError( + self.path, self.code, line_no, col_no, "Unexpected end of file" + ) line = self.lines[line_no] match = expect.match(line, col_no) if match is None: raise TokenizeError( + self.path, + self.code, line_no, col_no, "expected " + ", ".join(name.upper() for name in expect.names), @@ -136,7 +184,9 @@ class Tokenizer: while True: if line_no >= len(self.lines): - raise EOFError() + raise EOFError( + self.path, self.code, line_no, col_no, "Unexpected end of file" + ) line = self.lines[line_no] match = expect.search(line, col_no) diff --git a/src/textual/dom.py b/src/textual/dom.py index 6a5f5ba63..7c0c0ba54 100644 --- a/src/textual/dom.py +++ b/src/textual/dom.py @@ -53,7 +53,7 @@ class DOMNode(MessagePump): self.INLINE_STYLES, repr(self), node=self ) self.styles = RenderStyles(self, self._css_styles, self._inline_styles) - self._default_styles = Styles.parse(self.DEFAULT_STYLES, repr(self)) + self._default_styles = Styles.parse(self.DEFAULT_STYLES, f"{self.__class__}") self._default_rules = self._default_styles.extract_rules((0, 0, 0)) super().__init__() diff --git a/src/textual/message_pump.py b/src/textual/message_pump.py index c016045ac..db3a79335 100644 --- a/src/textual/message_pump.py +++ b/src/textual/message_pump.py @@ -232,7 +232,7 @@ class MessagePump: except CancelledError: raise except Exception as error: - self.app.panic() + self.app.panic(error) break finally: if self._message_queue.empty(): diff --git a/src/textual/screen.py b/src/textual/screen.py index 8e457c0fe..96b0825d0 100644 --- a/src/textual/screen.py +++ b/src/textual/screen.py @@ -20,7 +20,7 @@ class Screen(Widget): DEFAULT_STYLES = """ - layout: dock + layout: dock; docks: _default=top; """ @@ -131,8 +131,8 @@ class Screen(Widget): self, unclipped_region.size, virtual_size, container_size ) ) - except Exception: - self.app.panic() + except Exception as error: + self.app.panic(error) self.app.refresh() self._dirty_widgets.clear() diff --git a/tests/css/test_tokenize.py b/tests/css/test_tokenize.py index 2d3155382..96fa0cfa0 100644 --- a/tests/css/test_tokenize.py +++ b/tests/css/test_tokenize.py @@ -20,65 +20,310 @@ VALID_VARIABLE_NAMES = [ def test_variable_declaration_valid_names(name): css = f"${name}: black on red;" assert list(tokenize(css, "")) == [ - Token(name='variable_name', value=f'${name}:', path='', code=css, location=(0, 0), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 14), referenced_by=None), - Token(name='token', value='black', path='', code=css, location=(0, 15), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 20), referenced_by=None), - Token(name='token', value='on', path='', code=css, location=(0, 21), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 23), referenced_by=None), - Token(name='token', value='red', path='', code=css, location=(0, 24), referenced_by=None), - Token(name='variable_value_end', value=';', path='', code=css, location=(0, 27), referenced_by=None), + Token( + name="variable_name", + value=f"${name}:", + path="", + code=css, + location=(0, 0), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 14), + referenced_by=None, + ), + Token( + name="token", + value="black", + path="", + code=css, + location=(0, 15), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 20), + referenced_by=None, + ), + Token( + name="token", + value="on", + path="", + code=css, + location=(0, 21), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 23), + referenced_by=None, + ), + Token( + name="token", + value="red", + path="", + code=css, + location=(0, 24), + referenced_by=None, + ), + Token( + name="variable_value_end", + value=";", + path="", + code=css, + location=(0, 27), + referenced_by=None, + ), ] def test_variable_declaration_multiple_values(): css = "$x: 2vw\t4% 6s red;" assert list(tokenize(css, "")) == [ - Token(name='variable_name', value='$x:', path='', code=css, location=(0, 0), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 3), referenced_by=None), - Token(name='scalar', value='2vw', path='', code=css, location=(0, 4), referenced_by=None), - Token(name='whitespace', value='\t', path='', code=css, location=(0, 7), referenced_by=None), - Token(name='scalar', value='4%', path='', code=css, location=(0, 8), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 10), referenced_by=None), - Token(name='duration', value='6s', path='', code=css, location=(0, 11), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 13), referenced_by=None), - Token(name='token', value='red', path='', code=css, location=(0, 15), referenced_by=None), - Token(name='variable_value_end', value=';', path='', code=css, location=(0, 18), referenced_by=None), + Token( + name="variable_name", + value="$x:", + path="", + code=css, + location=(0, 0), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 3), + referenced_by=None, + ), + Token( + name="scalar", + value="2vw", + path="", + code=css, + location=(0, 4), + referenced_by=None, + ), + Token( + name="whitespace", + value="\t", + path="", + code=css, + location=(0, 7), + referenced_by=None, + ), + Token( + name="scalar", + value="4%", + path="", + code=css, + location=(0, 8), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 10), + referenced_by=None, + ), + Token( + name="duration", + value="6s", + path="", + code=css, + location=(0, 11), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 13), + referenced_by=None, + ), + Token( + name="token", + value="red", + path="", + code=css, + location=(0, 15), + referenced_by=None, + ), + Token( + name="variable_value_end", + value=";", + path="", + code=css, + location=(0, 18), + referenced_by=None, + ), ] def test_variable_declaration_comment_ignored(): css = "$x: red; /* comment */" assert list(tokenize(css, "")) == [ - Token(name='variable_name', value='$x:', path='', code=css, location=(0, 0), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 3), referenced_by=None), - Token(name='token', value='red', path='', code=css, location=(0, 4), referenced_by=None), - Token(name='variable_value_end', value=';', path='', code=css, location=(0, 7), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 8), referenced_by=None), + Token( + name="variable_name", + value="$x:", + path="", + code=css, + location=(0, 0), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 3), + referenced_by=None, + ), + Token( + name="token", + value="red", + path="", + code=css, + location=(0, 4), + referenced_by=None, + ), + Token( + name="variable_value_end", + value=";", + path="", + code=css, + location=(0, 7), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 8), + referenced_by=None, + ), ] def test_variable_declaration_comment_interspersed_ignored(): css = "$x: re/* comment */d;" assert list(tokenize(css, "")) == [ - Token(name='variable_name', value='$x:', path='', code=css, location=(0, 0), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 3), referenced_by=None), - Token(name='token', value='re', path='', code=css, location=(0, 4), referenced_by=None), - Token(name='token', value='d', path='', code=css, location=(0, 19), referenced_by=None), - Token(name='variable_value_end', value=';', path='', code=css, location=(0, 20), referenced_by=None), + Token( + name="variable_name", + value="$x:", + path="", + code=css, + location=(0, 0), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 3), + referenced_by=None, + ), + Token( + name="token", + value="re", + path="", + code=css, + location=(0, 4), + referenced_by=None, + ), + Token( + name="token", + value="d", + path="", + code=css, + location=(0, 19), + referenced_by=None, + ), + Token( + name="variable_value_end", + value=";", + path="", + code=css, + location=(0, 20), + referenced_by=None, + ), ] def test_variable_declaration_no_semicolon(): css = "$x: 1\n$y: 2" assert list(tokenize(css, "")) == [ - Token(name='variable_name', value='$x:', path='', code=css, location=(0, 0), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 3), referenced_by=None), - Token(name='number', value='1', path='', code=css, location=(0, 4), referenced_by=None), - Token(name='variable_value_end', value='\n', path='', code=css, location=(0, 5), referenced_by=None), - Token(name='variable_name', value='$y:', path='', code=css, location=(1, 0), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(1, 3), referenced_by=None), - Token(name='number', value='2', path='', code=css, location=(1, 4), referenced_by=None), + Token( + name="variable_name", + value="$x:", + path="", + code=css, + location=(0, 0), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 3), + referenced_by=None, + ), + Token( + name="number", + value="1", + path="", + code=css, + location=(0, 4), + referenced_by=None, + ), + Token( + name="variable_value_end", + value="\n", + path="", + code=css, + location=(0, 5), + referenced_by=None, + ), + Token( + name="variable_name", + value="$y:", + path="", + code=css, + location=(1, 0), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(1, 3), + referenced_by=None, + ), + Token( + name="number", + value="2", + path="", + code=css, + location=(1, 4), + referenced_by=None, + ), ] @@ -92,68 +337,441 @@ def test_variables_declarations_amongst_rulesets(): css = "$x:1; .thing{text:red;} $y:2;" tokens = list(tokenize(css, "")) assert tokens == [ - Token(name='variable_name', value='$x:', path='', code=css, location=(0, 0), referenced_by=None), - Token(name='number', value='1', path='', code=css, location=(0, 3), referenced_by=None), - Token(name='variable_value_end', value=';', path='', code=css, location=(0, 4), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 5), referenced_by=None), - Token(name='selector_start_class', value='.thing', path='', code=css, location=(0, 6), referenced_by=None), - Token(name='declaration_set_start', value='{', path='', code=css, location=(0, 12), referenced_by=None), - Token(name='declaration_name', value='text:', path='', code=css, location=(0, 13), referenced_by=None), - Token(name='token', value='red', path='', code=css, location=(0, 18), referenced_by=None), - Token(name='declaration_end', value=';', path='', code=css, location=(0, 21), referenced_by=None), - Token(name='declaration_set_end', value='}', path='', code=css, location=(0, 22), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 23), referenced_by=None), - Token(name='variable_name', value='$y:', path='', code=css, location=(0, 24), referenced_by=None), - Token(name='number', value='2', path='', code=css, location=(0, 27), referenced_by=None), - Token(name='variable_value_end', value=';', path='', code=css, location=(0, 28), referenced_by=None), + Token( + name="variable_name", + value="$x:", + path="", + code=css, + location=(0, 0), + referenced_by=None, + ), + Token( + name="number", + value="1", + path="", + code=css, + location=(0, 3), + referenced_by=None, + ), + Token( + name="variable_value_end", + value=";", + path="", + code=css, + location=(0, 4), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 5), + referenced_by=None, + ), + Token( + name="selector_start_class", + value=".thing", + path="", + code=css, + location=(0, 6), + referenced_by=None, + ), + Token( + name="declaration_set_start", + value="{", + path="", + code=css, + location=(0, 12), + referenced_by=None, + ), + Token( + name="declaration_name", + value="text:", + path="", + code=css, + location=(0, 13), + referenced_by=None, + ), + Token( + name="token", + value="red", + path="", + code=css, + location=(0, 18), + referenced_by=None, + ), + Token( + name="declaration_end", + value=";", + path="", + code=css, + location=(0, 21), + referenced_by=None, + ), + Token( + name="declaration_set_end", + value="}", + path="", + code=css, + location=(0, 22), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 23), + referenced_by=None, + ), + Token( + name="variable_name", + value="$y:", + path="", + code=css, + location=(0, 24), + referenced_by=None, + ), + Token( + name="number", + value="2", + path="", + code=css, + location=(0, 27), + referenced_by=None, + ), + Token( + name="variable_value_end", + value=";", + path="", + code=css, + location=(0, 28), + referenced_by=None, + ), ] def test_variables_reference_in_rule_declaration_value(): css = ".warn{text: $warning;}" assert list(tokenize(css, "")) == [ - Token(name='selector_start_class', value='.warn', path='', code=css, location=(0, 0), referenced_by=None), - Token(name='declaration_set_start', value='{', path='', code=css, location=(0, 5), referenced_by=None), - Token(name='declaration_name', value='text:', path='', code=css, location=(0, 6), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 11), referenced_by=None), - Token(name='variable_ref', value='$warning', path='', code=css, location=(0, 12), referenced_by=None), - Token(name='declaration_end', value=';', path='', code=css, location=(0, 20), referenced_by=None), - Token(name='declaration_set_end', value='}', path='', code=css, location=(0, 21), referenced_by=None), + Token( + name="selector_start_class", + value=".warn", + path="", + code=css, + location=(0, 0), + referenced_by=None, + ), + Token( + name="declaration_set_start", + value="{", + path="", + code=css, + location=(0, 5), + referenced_by=None, + ), + Token( + name="declaration_name", + value="text:", + path="", + code=css, + location=(0, 6), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 11), + referenced_by=None, + ), + Token( + name="variable_ref", + value="$warning", + path="", + code=css, + location=(0, 12), + referenced_by=None, + ), + Token( + name="declaration_end", + value=";", + path="", + code=css, + location=(0, 20), + referenced_by=None, + ), + Token( + name="declaration_set_end", + value="}", + path="", + code=css, + location=(0, 21), + referenced_by=None, + ), ] def test_variables_reference_in_rule_declaration_value_multiple(): css = ".card{padding: $pad-y $pad-x;}" assert list(tokenize(css, "")) == [ - Token(name='selector_start_class', value='.card', path='', code=css, location=(0, 0), referenced_by=None), - Token(name='declaration_set_start', value='{', path='', code=css, location=(0, 5), referenced_by=None), - Token(name='declaration_name', value='padding:', path='', code=css, location=(0, 6), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 14), referenced_by=None), - Token(name='variable_ref', value='$pad-y', path='', code=css, location=(0, 15), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 21), referenced_by=None), - Token(name='variable_ref', value='$pad-x', path='', code=css, location=(0, 22), referenced_by=None), - Token(name='declaration_end', value=';', path='', code=css, location=(0, 28), referenced_by=None), - Token(name='declaration_set_end', value='}', path='', code=css, location=(0, 29), referenced_by=None) + Token( + name="selector_start_class", + value=".card", + path="", + code=css, + location=(0, 0), + referenced_by=None, + ), + Token( + name="declaration_set_start", + value="{", + path="", + code=css, + location=(0, 5), + referenced_by=None, + ), + Token( + name="declaration_name", + value="padding:", + path="", + code=css, + location=(0, 6), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 14), + referenced_by=None, + ), + Token( + name="variable_ref", + value="$pad-y", + path="", + code=css, + location=(0, 15), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 21), + referenced_by=None, + ), + Token( + name="variable_ref", + value="$pad-x", + path="", + code=css, + location=(0, 22), + referenced_by=None, + ), + Token( + name="declaration_end", + value=";", + path="", + code=css, + location=(0, 28), + referenced_by=None, + ), + Token( + name="declaration_set_end", + value="}", + path="", + code=css, + location=(0, 29), + referenced_by=None, + ), ] def test_variables_reference_in_variable_declaration(): css = "$x: $y;" assert list(tokenize(css, "")) == [ - Token(name='variable_name', value='$x:', path='', code=css, location=(0, 0), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 3), referenced_by=None), - Token(name='variable_ref', value='$y', path='', code=css, location=(0, 4), referenced_by=None), - Token(name='variable_value_end', value=';', path='', code=css, location=(0, 6), referenced_by=None) + Token( + name="variable_name", + value="$x:", + path="", + code=css, + location=(0, 0), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 3), + referenced_by=None, + ), + Token( + name="variable_ref", + value="$y", + path="", + code=css, + location=(0, 4), + referenced_by=None, + ), + Token( + name="variable_value_end", + value=";", + path="", + code=css, + location=(0, 6), + referenced_by=None, + ), ] def test_variable_references_in_variable_declaration_multiple(): css = "$x: $y $z\n" assert list(tokenize(css, "")) == [ - Token(name='variable_name', value='$x:', path='', code=css, location=(0, 0), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 3), referenced_by=None), - Token(name='variable_ref', value='$y', path='', code=css, location=(0, 4), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 6), referenced_by=None), - Token(name='variable_ref', value='$z', path='', code=css, location=(0, 8), referenced_by=None), - Token(name='variable_value_end', value='\n', path='', code=css, location=(0, 10), referenced_by=None) + Token( + name="variable_name", + value="$x:", + path="", + code=css, + location=(0, 0), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 3), + referenced_by=None, + ), + Token( + name="variable_ref", + value="$y", + path="", + code=css, + location=(0, 4), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 6), + referenced_by=None, + ), + Token( + name="variable_ref", + value="$z", + path="", + code=css, + location=(0, 8), + referenced_by=None, + ), + Token( + name="variable_value_end", + value="\n", + path="", + code=css, + location=(0, 10), + referenced_by=None, + ), ] + + +def test_allow_new_lines(): + css = ".foo{margin: 1\n1 0 0}" + tokens = list(tokenize(css, "")) + print(repr(tokens)) + expected = [ + Token( + name="selector_start_class", + value=".foo", + path="", + code=".foo{margin: 1\n1 0 0}", + location=(0, 0), + ), + Token( + name="declaration_set_start", + value="{", + path="", + code=".foo{margin: 1\n1 0 0}", + location=(0, 4), + ), + Token( + name="declaration_name", + value="margin:", + path="", + code=".foo{margin: 1\n1 0 0}", + location=(0, 5), + ), + Token( + name="whitespace", + value=" ", + path="", + code=".foo{margin: 1\n1 0 0}", + location=(0, 12), + ), + Token( + name="number", + value="1", + path="", + code=".foo{margin: 1\n1 0 0}", + location=(0, 13), + ), + Token( + name="whitespace", + value="\n", + path="", + code=".foo{margin: 1\n1 0 0}", + location=(0, 14), + ), + Token( + name="number", + value="1", + path="", + code=".foo{margin: 1\n1 0 0}", + location=(1, 0), + ), + Token( + name="whitespace", + value=" ", + path="", + code=".foo{margin: 1\n1 0 0}", + location=(1, 1), + ), + Token( + name="number", + value="0", + path="", + code=".foo{margin: 1\n1 0 0}", + location=(1, 2), + ), + Token( + name="whitespace", + value=" ", + path="", + code=".foo{margin: 1\n1 0 0}", + location=(1, 3), + ), + Token( + name="number", + value="0", + path="", + code=".foo{margin: 1\n1 0 0}", + location=(1, 4), + ), + Token( + name="declaration_set_end", + value="}", + path="", + code=".foo{margin: 1\n1 0 0}", + location=(1, 5), + ), + ] + assert list(tokenize(css, "")) == expected