From c2c706d67ad44dd0f244239d6db30d22db4fd2f7 Mon Sep 17 00:00:00 2001 From: Jaap Joris Vens Date: Mon, 13 Feb 2023 18:04:17 +0100 Subject: [PATCH 01/45] DjHTML Version 3 For this release, a substantial part the codebase has been refactored. Here is a non-exhaustive summary of all the things that have changed: - Support for Python 3.6 and 3.7 has been dropped - A Django middleware class to indent HTML responses has been added - Ignore both opening and closing comment tags - New handling off both relative and absolute offsets - Return correct `repr()` strings with the `--debug` option - New token class "OpenDouble" and a revised indentation algorithm - Additional `line` argument to `create_token()` methods - Changed return value of `create_token()` methods - Changed constructor arguments of Token and Line classes - Refactored all `create_token()` methods to get rid of return statements - Simplified test suite runner that reindents the expected output - Don't indent the contents of template variables (closes #75) - Improved handling of Django tags inside HTML elements - Improved JavaScript `case` indentation (closes #76) - Improved JavaScript method chaining (closes #59) - Improved CSS multiline statements (closes #74) - New multiline HTML element indentation (closes #50) - New multiline HTML attribute value indentation - Extensive test coverage with lots of edge cases --- .github/workflows/main.yml | 4 +- README.md | 52 ++- djhtml/__main__.py | 9 +- djhtml/lines.py | 48 +- djhtml/middleware.py | 35 ++ djhtml/modes.py | 423 +++++++++--------- djhtml/tokens.py | 32 +- noxfile.py | 2 +- setup.cfg | 9 +- tests/suite/css.html | 53 +++ tests/suite/css.in | 38 -- tests/suite/css.out | 38 -- tests/suite/{django.out => django.html} | 26 ++ tests/suite/django.in | 112 ----- tests/suite/html.html | 330 ++++++++++++++ tests/suite/html.in | 173 ------- tests/suite/html.out | 173 ------- tests/suite/html_arrow_attr.in | 5 - tests/suite/html_arrow_attr.out | 5 - tests/suite/html_tag.in | 21 - tests/suite/html_tag.out | 21 - tests/suite/{jinja.out => jinja.html} | 7 + tests/suite/jinja.in | 47 -- tests/suite/{js.out => js.html} | 39 ++ tests/suite/js.in | 231 ---------- tests/suite/{paradoxes.out => paradoxes.html} | 10 - tests/suite/paradoxes.in | 74 --- tests/test_suite.py | 49 +- 28 files changed, 805 insertions(+), 1261 deletions(-) create mode 100644 djhtml/middleware.py create mode 100644 tests/suite/css.html delete mode 100644 tests/suite/css.in delete mode 100644 tests/suite/css.out rename tests/suite/{django.out => django.html} (84%) delete mode 100644 tests/suite/django.in create mode 100644 tests/suite/html.html delete mode 100644 tests/suite/html.in delete mode 100644 tests/suite/html.out delete mode 100644 tests/suite/html_arrow_attr.in delete mode 100644 tests/suite/html_arrow_attr.out delete mode 100644 tests/suite/html_tag.in delete mode 100644 tests/suite/html_tag.out rename tests/suite/{jinja.out => jinja.html} (84%) delete mode 100644 tests/suite/jinja.in rename tests/suite/{js.out => js.html} (84%) delete mode 100644 tests/suite/js.in rename tests/suite/{paradoxes.out => paradoxes.html} (90%) delete mode 100644 tests/suite/paradoxes.in diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4d80350..c1f874a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,13 +9,11 @@ on: jobs: tests: name: Python ${{ matrix.python-version }} - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: matrix: python-version: - - 3.6 - - 3.7 - 3.8 - 3.9 - '3.10' diff --git a/README.md b/README.md index e6daacd..7d9b2a0 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,7 @@ ***A pure-Python Django/Jinja template indenter without dependencies.*** -DjHTML is a fully automatic template indenter that works with mixed -HTML/CSS/Javascript templates that contain +DjHTML indents mixed HTML/CSS/Javascript templates that contain [Django](https://docs.djangoproject.com/en/stable/ref/templates/language/) or [Jinja](https://jinja.palletsprojects.com/templates/) template tags. It works similar to other code-formatting tools such as @@ -59,10 +58,10 @@ Install DjHTML with the following command: $ pip install djhtml -Note that [Windows still uses legacy code pages for the system -encoding](https://docs.python.org/3/using/windows.html#win-utf8-mode). -It is highly advised to set the environment variable `PYTHONUTF8` to -`1` to avoid issues with indenting UTF-8 files. You can do so with the +Note that +[Windows still uses legacy code pages](https://docs.python.org/3/using/windows.html#win-utf8-mode) +instead of UTF-8. It is highly advised to set the environment variable +`PYTHONUTF8` to `1` with the [setx](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/setx) command: @@ -78,12 +77,42 @@ command: reindented template.html 1 template has been reindented. +You can also run `djhtml .` to indent all HTML files beneath the +current directory. + An exit status of 0 means that everything went well, regardless of whether any files were changed. When the option `-c` / `--check` is used, the exit status is 1 when one or more files would have changed, -but no changes are actually made. The exit status of 123 means that -there was an error while indenting one or more files. All available -options are given by `djthml -h` / `djthml --help`. +but no changes are actually made. All available options are given by +`djthml -h` / `djthml --help`. + + +## New! Multi-line HTML tag alignment + +As of version 3, DjHTML indents multi-line HTML tags and multi-line +attribute values like this: + +```html +
+ It's weird not to be weird. +
+``` + + +## New! Django middleware + +To automatically indent all the HTML responses from your Django web +application, add the following to your settings file: + + TABWIDTH = 4 + MIDDLEWARE += ['djhtml.middleware.DjHTMLMiddleware'] + +(Caution: when used in production, it is advised to use some kind of +[caching](https://docs.djangoproject.com/en/stable/topics/cache/).) ## `fmt:off` and `fmt:on` @@ -98,7 +127,7 @@ You can exclude specific lines from being processed with the / .\ \_,--._/ {# fmt:on #} -"/> + "/> ``` Contents inside `
 ... 
`, ``, `/* ... */`, and @@ -124,7 +153,7 @@ The indenter operates in one of three different modes: ## pre-commit configuration -The best way to use DjHTML is as a [pre-commit](https://pre-commit.com/) +A great way to use DjHTML is as a [pre-commit](https://pre-commit.com/) hook, so all your HTML, CSS and JavaScript files will automatically be indented upon every commit. @@ -203,6 +232,7 @@ happy, please do the following: Your feedback for improving DjHTML is very welcome! + ## Development Use your preferred system for setting up a virtualenv, docker environment, diff --git a/djhtml/__main__.py b/djhtml/__main__.py index 5368a55..aa70951 100644 --- a/djhtml/__main__.py +++ b/djhtml/__main__.py @@ -53,9 +53,6 @@ def main(): # Indent input file try: - if options.debug: - print(Mode(source).debug()) - sys.exit() result = Mode(source).indent(options.tabwidth) except Exception: _error( @@ -66,8 +63,7 @@ def main(): ) raise - changed = _verify_changed(source, result) - if changed: + if changed := _verify_changed(source, result): changed_files += 1 else: unchanged_files += 1 @@ -102,6 +98,9 @@ def main(): f"{problematic_files} template{s} could not be processed due to an error." ) + if options.debug: + print(Mode(source).debug(), file=sys.stderr) + # Exit with appropriate exit status if problematic_files: sys.exit(123) diff --git a/djhtml/lines.py b/djhtml/lines.py index 6314562..0e67985 100644 --- a/djhtml/lines.py +++ b/djhtml/lines.py @@ -2,33 +2,25 @@ class Line: """ A single output line not including the final newline. - The behavior regarding final newlines has changed between DjHTML - v1.4.14 and v1.5.0. It used to always append the final newline, - but now this will only happen when the source file already - contains a final newline. - - See https://github.com/rtts/djhtml/issues/56 for the discussion - that led to this change. - """ - def __init__(self, nr=1): - self.nr = nr - self.tokens = [] - self.level = 0 + def __init__(self, tokens=None, level=0, offset=0, ignore=False): + self.tokens = tokens or [] + self.level = level + self.offset = offset + self.ignore = ignore def append(self, token): """ Append tokens to the line. """ - token.line_nr = self.nr self.tokens.append(token) @property def text(self): """ - The unindented text of this line without leading/trailing spaces. + The text of this line without leading/trailing spaces. """ return "".join([str(token) for token in self.tokens]).strip() @@ -39,20 +31,22 @@ def indent(self, tabwidth): and optionally offset before calling this method. """ - if self.tokens: - if self.tokens[0].ignore: - return "".join([str(token) for token in self.tokens]) - elif self.text: - offset = self.tokens[0].offset * tabwidth - spaces = tabwidth * self.level + offset - return " " * spaces + self.text + if self.ignore: + return "".join([str(token) for token in self.tokens]) + if self.text: + spaces = tabwidth * self.level + self.offset + return " " * spaces + self.text return "" - def __repr__(self): - return repr(self.tokens) - def __bool__(self): - return bool(self.tokens and self.text) + return bool(self.tokens) + + def __len__(self): + return len(self.text) - def __next__(self): - return Line(nr=self.nr + 1) + def __repr__(self): + kwargs = "" + for attr in ["level", "offset", "ignore"]: + if value := getattr(self, attr): + kwargs += f", {attr}={value}" + return f"Line({repr(self.tokens)}{kwargs})" diff --git a/djhtml/middleware.py b/djhtml/middleware.py new file mode 100644 index 0000000..0a04e5d --- /dev/null +++ b/djhtml/middleware.py @@ -0,0 +1,35 @@ +from django.conf import settings +from django.core.exceptions import ImproperlyConfigured + +from djhtml.modes import DjHTML + +try: + TABWIDTH = settings.TABWIDTH +except AttributeError: + TABWIDTH = 4 + + +class DjHTMLMiddleware: + """ + Django middleware class to indent HTML responses. + + """ + + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + response = self.get_response(request) + if ( + "Content-Type" in response + and response["Content-Type"] == "text/html; charset=utf-8" + ): + if "Content-Length" in response: + raise ImproperlyConfigured( + "Please load DjHTMLMiddleware _after_ CommonMiddleware (otherwise" + ' the "Content-Length" header will be incorrect)' + ) + response.content = ( + DjHTML(response.content.decode()).indent(TABWIDTH).encode() + ) + return response diff --git a/djhtml/modes.py b/djhtml/modes.py index d032a21..306f294 100644 --- a/djhtml/modes.py +++ b/djhtml/modes.py @@ -6,7 +6,7 @@ class DjTXT: """ - Mode for indenting text files that contain Django/Jinja template tags. + Mode for indenting Django/Jinja template tags. Also serves as the base class for the other modes: - DjHTML @@ -20,13 +20,19 @@ class DjTXT: r"{%[-\+]?.*?[-\+]?%}", r"{#.*?#}", r"{#", + r"{{.*?}}", ] - OPENING_AND_CLOSING_TAGS = [ + CLOSING_AND_OPENING_TAGS = [ "elif", "else", "empty", "plural", ] + COMMENT_TAGS = [ + "comment", + "verbatim", + "raw", + ] AMBIGUOUS_BLOCK_TAGS = { # token_name: (regex_if_block, regex_if_not_block) "set": (None, " = "), @@ -40,6 +46,8 @@ def __init__(self, source="", return_mode=None): self.source = source self.return_mode = return_mode or self self.token_re = compile_re(self.RAW_TOKENS) + self.offsets = {"relative": 0, "absolute": 0} + self.previous_offsets = [] def indent(self, tabwidth): """ @@ -60,61 +68,44 @@ def parse(self): first_token = True for token in line.tokens: opening_token = None - - # When a dedenting token is found, match it with the - # token at the top of the stack. - if token.dedents: - try: - if ( - stack[-1].kind == token.kind - and stack[-1].is_hard == token.is_hard - ): + if stack: + # When a dedenting token is found, match it with + # the token at the top of the stack. To find out + # what each statement does, uncomment it and see + # which unittests fail. + if token.dedents: + if stack[-1].mode is token.mode: opening_token = stack.pop() - elif token.kind == "django": + if stack and opening_token.is_double: + opening_token = stack.pop() + elif token.mode is DjTXT: opening_token = stack.pop() - while opening_token.kind != token.kind: + while opening_token.mode is not DjTXT: opening_token = stack.pop() elif first_token: - # This closing token could not be matched. - # Instead of erroring out, set the line level - # to what it would have been with a - # regular text token. - - # If there is any OpenHard token in the - # set and current token is CloseHard then - # let's move back to OpenHard. - if token.is_hard and any( - t.is_hard and t.indents for t in stack - ): - s = stack.pop() - while not s.is_hard or not s.indents: - s = stack.pop() + line.level = stack[-1].level + 1 + + # Dedent! + if first_token and opening_token: + line.level = opening_token.level + else: + # Indent! + if token.is_double and stack[-1].is_double: + opening_token = stack.pop() + if stack and first_token: line.level = stack[-1].level + 1 - except IndexError: - line.level = 0 - - # If this dedenting token is the first in line, - # set the line level to the line level of the - # corresponding opening token. - if first_token and opening_token: - line.level = opening_token.level - - # If the first token is not a dedenting token, the - # line level will be one higher than that of the token - # at the top of the stack. - elif first_token: - line.level = stack[-1].level + 1 if stack else 0 - - # Push indenting tokens onto the stack. Note that some - # tokens can be both indenting and dedenting (e.g., - # ``{% else %}``), hence the if instead of elif. + + if first_token: + line.level = line.level + token.relative + line.offset = token.absolute + line.ignore = token.ignore + + # Push indenting tokens onto the stack. if token.indents: token.level = opening_token.level if opening_token else line.level stack.append(token) - # Subsequent tokens have no effect on the line level - # (but tokens with only spaces don't count). if token.text.strip(): first_token = False @@ -137,72 +128,75 @@ def tokenize(self): except ValueError: # We've reached the final line! if src: - line.append(mode.create_token(src, "")) + token, _ = mode.create_token(src, "", line) + line.append(token) self.lines.append(line) break if head: - # Create a token from the head. This will always be a - # text token (and the next mode will always be the - # current mode), but we don't assume that here. - line.append(mode.create_token(head, raw_token + tail)) - mode = next(mode) + # Create a token from the head. + token, mode = mode.create_token(head, raw_token + tail, line) + line.append(token) if raw_token == "\n": self.lines.append(line) - line = next(line) + line = Line() else: - # Ask the mode to create a token and to provide a new - # mode for the next iteration of the loop. - line.append(mode.create_token(raw_token, tail)) - mode = next(mode) + # Create a token from the tail + token, mode = mode.create_token(raw_token, tail, line) + line.append(token) # Set the new source to the old tail for the next iteration. src = tail - def create_token(self, raw_token, src): + def create_token(self, raw_token, src, line): """ - Given a raw token string, return a single token (and internally - set the next mode). + Given a raw token string, return a single token and the + next mode. """ - kind = "django" - self.next_mode = self - token = Token.Text(raw_token) + mode = self - tag = re.match(self.OPENING_TAG, raw_token) - if tag: + if tag := re.match(self.OPENING_TAG, raw_token): name = tag.group(1) - if name in ["comment", "verbatim", "raw"]: - token = Token.Open(raw_token, kind) - self.next_mode = Comment( - r"{%[-\+]? *end" + name + r"(?: .*?|)%}", self, kind + if name in self.COMMENT_TAGS: + token, mode = Token.Open(raw_token, mode=DjTXT, ignore=True,), Comment( + r"{%[-\+]? *end" + name + r"(?: .*?|)%}", + mode=DjTXT, + return_mode=self, ) elif self._has_closing_token(name, raw_token, src): - token = Token.Open(raw_token, kind) - elif name in self.OPENING_AND_CLOSING_TAGS: - token = Token.OpenAndClose(raw_token, kind) + self.previous_offsets.append(self.offsets["relative"]) + token = Token.Open(raw_token, mode=DjTXT, **self.offsets) + self.offsets["relative"] = 0 + elif name in self.CLOSING_AND_OPENING_TAGS: + token = Token.CloseAndOpen(raw_token, mode=DjTXT, **self.offsets) elif name.startswith("end"): - token = Token.Close(raw_token, kind) - return token - - if re.match(self.FMT_OFF, raw_token): - token = Token.Open(raw_token, kind) - self.next_mode = Comment(self.FMT_ON, self, kind) - return token - - if raw_token == "{#": - token = Token.Open(raw_token, kind) - self.next_mode = Comment(r"#\}", self, kind) - - return token + token = Token.Close(raw_token, mode=DjTXT, **self.offsets) + try: + self.offsets["relative"] = self.previous_offsets.pop() + except IndexError: + self.offsets["relative"] = 0 + else: + token = Token.Text(raw_token, mode=DjTXT, **self.offsets) + elif re.match(self.FMT_OFF, raw_token): + token, mode = Token.Open(raw_token, mode=DjTXT, ignore=True), Comment( + self.FMT_ON, mode=DjTXT, return_mode=self + ) + elif raw_token == "{#": + token, mode = Token.Open(raw_token, mode=DjTXT, ignore=True), Comment( + r"#\}", mode=DjTXT, return_mode=self + ) + else: + token = Token.Text(raw_token, mode=self.__class__, **self.offsets) + + return token, mode def _has_closing_token(self, name, raw_token, src): if not re.search(f"{{%[-\\+]? *end{name}(?: .*?|)%}}", src): return False - regex = self.AMBIGUOUS_BLOCK_TAGS.get(name) - if regex: + if regex := self.AMBIGUOUS_BLOCK_TAGS.get(name): if regex[0]: return re.search(regex[0], raw_token) if regex[1]: @@ -211,12 +205,8 @@ def _has_closing_token(self, name, raw_token, src): def debug(self): self.tokenize() - return "\n".join( - [" ".join([repr(token) for token in line.tokens]) for line in self.lines] - ) - - def __next__(self): - return self.next_mode + self.parse() + return "\n".join([repr(line) for line in self.lines]) class DjHTML(DjTXT): @@ -253,35 +243,33 @@ class DjHTML(DjTXT): "wbr", ] - def create_token(self, raw_token, src): - kind = "html" - self.next_mode = self + def create_token(self, raw_token, src, line): + mode = self if raw_token == "<": - tag = re.match(r"(\w+)[^:]", src) - if tag: - token = Token.Open(raw_token, kind) - self.next_mode = InsideHTMLTag(tag[1], self) + if tag := re.match(r"([\w\-\.:]+)", src): + token, mode = Token.Open(raw_token, mode=DjHTML), InsideHTMLTag( + tag[1], line, self + ) else: - token = Token.Text(raw_token) - return token - - if raw_token == "", self, kind) - return Token.Open(raw_token, kind) - - if re.match("", raw_token): - self.next_mode = Comment("", self, kind) - return Token.Open(raw_token, kind) - - if raw_token.startswith("", mode=DjHTML, return_mode=self + ) + elif re.match("", raw_token): + token, mode = Token.Open(raw_token, mode=DjHTML, ignore=True), Comment( + "", mode=DjHTML, return_mode=self + ) + elif raw_token.startswith("", r"[\{\(\)\}]", r"/\*", + r"[\w-]+: ", + r";", ] - def create_token(self, raw_token, src): - kind = "css" - self.next_mode = self + def create_token(self, raw_token, src, line): + mode = self if raw_token in "{(": - return Token.Open(raw_token, kind) - if raw_token in "})": - return Token.Close(raw_token, kind) - if raw_token == "/*": - self.next_mode = Comment(r"\*/", self, kind) - return Token.Open(raw_token, kind) - if raw_token == "": - self.next_mode = self.return_mode - return Token.Close(raw_token, "html") - - return super().create_token(raw_token, src) + self.previous_offsets.append(self.offsets["relative"]) + token = Token.Open(raw_token, mode=DjCSS, **self.offsets) + self.offsets["relative"] = 0 + elif raw_token in "})": + token = Token.Close(raw_token, mode=DjCSS, **self.offsets) + try: + self.offsets["relative"] = self.previous_offsets.pop() + except IndexError: + self.offsets["relative"] = 0 + elif raw_token.endswith(": "): + token = Token.Open(raw_token, mode=DjCSS, **self.offsets) + self.offsets["relative"] = -1 + self.offsets["absolute"] = len(raw_token) + elif raw_token == ";": + self.offsets["relative"] = 0 + self.offsets["absolute"] = 0 + token = Token.Close(raw_token, mode=DjCSS, **self.offsets) + elif raw_token == "/*": + token, mode = Token.Open(raw_token, mode=DjCSS, ignore=True), Comment( + r"\*/", mode=DjCSS, return_mode=self + ) + elif raw_token == "": + token, mode = ( + Token.Close(raw_token, mode=self.return_mode.__class__), + self.return_mode, + ) + else: + token, mode = super().create_token(raw_token, src, line) + + return token, mode class DjJS(DjTXT): @@ -331,51 +339,40 @@ class DjJS(DjTXT): r"/\*", ] - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.hard_indents = 0 - self.opened_brackets = 0 - - def create_token(self, raw_token, src): - kind = "javascript" - self.next_mode = self - - if raw_token.strip() == "switch": - self.hard_indents = self.hard_indents + 1 - - if self.hard_indents: - if raw_token == "{": - self.opened_brackets += 1 - if self.opened_brackets == self.hard_indents: - return Token.OpenHard(raw_token, kind) - elif raw_token == "}": - if self.opened_brackets == self.hard_indents: - self.opened_brackets -= 1 - self.hard_indents -= 1 - return Token.CloseHard(raw_token, kind) - self.opened_brackets -= 1 + def create_token(self, raw_token, src, line): + mode = self if raw_token in "{[(": - return Token.Open(raw_token, kind) - if raw_token in ")]}": - return Token.Close(raw_token, kind) - if raw_token == "`": - self.next_mode = Comment("`", self, kind) - return Token.Open(raw_token, kind) - if raw_token == "/*": - self.next_mode = Comment(r"\*/", self, kind) - return Token.Open(raw_token, kind) - if raw_token.lstrip().startswith("."): - return Token.Text(raw_token, offset=1) - if raw_token.lstrip().startswith("case "): - return Token.OpenAndClose(raw_token, kind) - if raw_token.lstrip().startswith("default:"): - return Token.OpenAndClose(raw_token, kind) - if raw_token == "": - self.next_mode = self.return_mode - return Token.Close(raw_token, "html") - - return super().create_token(raw_token, src) + token = Token.Open(raw_token, mode=DjJS, **self.offsets) + elif raw_token in ")]}": + token = Token.Close(raw_token, mode=DjJS, **self.offsets) + elif raw_token == "`": + token, mode = Token.Open(raw_token, mode=DjJS, ignore=True), Comment( + "`", mode=DjJS, return_mode=self + ) + elif raw_token == "/*": + token, mode = Token.Open(raw_token, mode=DjJS, ignore=True), Comment( + r"\*/", mode=DjJS, return_mode=self + ) + elif not line and raw_token.lstrip().startswith("."): + self.offsets["relative"] = 1 + token = Token.Text(raw_token, mode=DjJS, **self.offsets) + elif raw_token.lstrip().startswith(("case ", "default:")): + token = Token.OpenDouble(raw_token, mode=DjJS) + return token, mode + elif raw_token == "": + token, mode = ( + Token.Close(raw_token, mode=self.return_mode.__class__), + self.return_mode, + ) + else: + token, mode = super().create_token(raw_token, src, line) + + # Reset relative offset after creating first token in line. + if not line: + self.offsets["relative"] = 0 + + return token, mode # The following are "special" modes with different constructors. @@ -387,18 +384,16 @@ class Comment(DjTXT): """ - def __init__(self, endtag, return_mode, kind): + def __init__(self, endtag, *, mode, return_mode): self.endtag = endtag + self.mode = mode self.return_mode = return_mode - self.kind = kind self.token_re = compile_re([r"\n", endtag]) - def create_token(self, raw_token, src): - self.next_mode = self + def create_token(self, raw_token, src, line): if re.match(self.endtag, raw_token): - self.next_mode = self.return_mode - return Token.Close(raw_token, self.kind) - return Token.Ignore(raw_token) + return Token.Close(raw_token, mode=self.mode, ignore=True), self.return_mode + return Token.Text(raw_token, mode=Comment, ignore=True), self class InsideHTMLTag(DjTXT): @@ -407,43 +402,63 @@ class InsideHTMLTag(DjTXT): """ - RAW_TOKENS = DjTXT.RAW_TOKENS + [r"/?>", r'"'] + RAW_TOKENS = DjTXT.RAW_TOKENS + [r"/?>", r"[^ ='\">/]+=", r'"'] - def __init__(self, tagname, return_mode): + def __init__(self, tagname, line, return_mode): self.tagname = tagname self.return_mode = return_mode self.token_re = compile_re(self.RAW_TOKENS) self.inside_attr = False + self.offsets = dict(relative=-1, absolute=len(line) + len(tagname) + 2) + self.previous_offsets = [] - def create_token(self, raw_token, src): - kind = "html" - self.next_mode = self + # Pff... + self.additional_offset = -len(tagname) - 1 + + def create_token(self, raw_token, src, line): + mode = self + + if line: + self.additional_offset += len(raw_token) + else: + self.additional_offset = 0 + + if "text/template" in raw_token: + self.tagname = "" if raw_token == '"': if self.inside_attr: self.inside_attr = False - return Token.Close(raw_token, kind) - self.inside_attr = True - return Token.Open(raw_token, kind) + self.offsets["absolute"] -= 1 + token = Token.Text(raw_token, mode=InsideHTMLTag, **self.offsets) + self.offsets["absolute"] = self.previous_offset + else: + self.inside_attr = True + self.previous_offset = self.offsets["absolute"] + self.offsets["absolute"] += self.additional_offset + token = Token.Text(raw_token, mode=InsideHTMLTag, **self.offsets) elif not self.inside_attr and raw_token == "/>": - self.next_mode = self.return_mode - return Token.Close(raw_token, kind) + token, mode = Token.Close(raw_token, mode=DjHTML), self.return_mode elif not self.inside_attr and raw_token == ">": if self.tagname.lower() in DjHTML.IGNORE_TAGS: - self.next_mode = self.return_mode - return Token.Close(raw_token, kind) + token, mode = Token.Close(raw_token, mode=DjHTML), self.return_mode + elif self.tagname == "style": + token, mode = Token.CloseAndOpen(raw_token, mode=DjHTML), DjCSS( + return_mode=self.return_mode + ) + elif self.tagname == "script": + token, mode = Token.CloseAndOpen(raw_token, mode=DjHTML), DjJS( + return_mode=self.return_mode + ) else: - if self.tagname == "style": - self.next_mode = DjCSS(return_mode=self.return_mode) - elif self.tagname == "script": - self.next_mode = DjJS(return_mode=self.return_mode) - else: - self.next_mode = self.return_mode - return Token.OpenAndClose(raw_token, kind) - elif "text/template" in raw_token: - self.tagname = "" + token, mode = ( + Token.CloseAndOpen(raw_token, mode=DjHTML), + self.return_mode, + ) + else: + token, mode = super().create_token(raw_token, src, line) - return super().create_token(raw_token, src) + return token, mode def compile_re(raw_tokens): diff --git a/djhtml/tokens.py b/djhtml/tokens.py index 308ef49..cb12aee 100644 --- a/djhtml/tokens.py +++ b/djhtml/tokens.py @@ -8,37 +8,41 @@ class _Base: indents = False dedents = False ignore = False - is_hard = False + is_double = False - def __init__(self, text, kind="", offset=0): + def __init__( + self, text, *, mode, level=0, relative=0, absolute=0, ignore=False + ): self.text = text - self.kind = kind - self.offset = offset + self.mode = mode + self.level = level + self.relative = relative + self.absolute = absolute + self.ignore = ignore def __str__(self): return self.text def __repr__(self): - return f"({self.__class__.__name__}:{repr(self.text)})" + kwargs = f", mode={self.mode.__name__}" + for attr in ["level", "relative", "absolute", "ignore"]: + if value := getattr(self, attr): + kwargs += f", {attr}={value}" + return f"{self.__class__.__name__}('{self.text}'{kwargs})" class Text(_Base): pass - class Ignore(_Base): - ignore = True - class Open(_Base): indents = True - class OpenHard(Open): - is_hard = True + class OpenDouble(_Base): + indents = True + is_double = True class Close(_Base): dedents = True - class CloseHard(Close): - is_hard = True - - class OpenAndClose(_Base): + class CloseAndOpen(_Base): indents = True dedents = True diff --git a/noxfile.py b/noxfile.py index 0ff90eb..0942baf 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,6 +1,6 @@ import nox -@nox.session(python=["3.6", "3.7", "3.8", "3.9", "3.10", "3.11"]) +@nox.session(python=["3.8", "3.9", "3.10", "3.11"]) def tests(session): session.run("python", "-m", "unittest", "discover", "-v") diff --git a/setup.cfg b/setup.cfg index ca589da..8a2419c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = djhtml -version = 2.0.0 +version = 3.0.0 description = Django/Jinja template indenter long_description = file: README.md long_description_content_type = text/markdown @@ -11,15 +11,13 @@ license = GPLv3+ classifiers = Programming Language :: Python :: 3 Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+) Operating System :: OS Independent -python_requires = '>=3.6' +python_requires = '>=3.8' [options] packages = find: @@ -27,9 +25,6 @@ packages = find: [options.extras_require] dev = nox - isort - black - flake8 pre-commit [options.packages.find] diff --git a/tests/suite/css.html b/tests/suite/css.html new file mode 100644 index 0000000..17427f2 --- /dev/null +++ b/tests/suite/css.html @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + diff --git a/tests/suite/css.in b/tests/suite/css.in deleted file mode 100644 index 4ee1693..0000000 --- a/tests/suite/css.in +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - diff --git a/tests/suite/css.out b/tests/suite/css.out deleted file mode 100644 index 359adf0..0000000 --- a/tests/suite/css.out +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - diff --git a/tests/suite/django.out b/tests/suite/django.html similarity index 84% rename from tests/suite/django.out rename to tests/suite/django.html index faf9e9b..80b8fba 100644 --- a/tests/suite/django.out +++ b/tests/suite/django.html @@ -1,3 +1,13 @@ + +
+ + {% load static %} {% block content %} @@ -110,3 +120,19 @@

Welcome, {{ request.user }}

{% endwith %} {% endslot %} {% endcomponent_block %} + + + +{{ '' if condition }} +some text +{{ '' if condition }} +more text + + + +{{ '' + if condition }} + some text + {{ ' +if condition }} +more text diff --git a/tests/suite/django.in b/tests/suite/django.in deleted file mode 100644 index 65bd342..0000000 --- a/tests/suite/django.in +++ /dev/null @@ -1,112 +0,0 @@ - -{% load static %} -{% block content %} -{% if request.user.is_authenticated %} -

Welcome, {{ request.user }}

-{% else %} -Click here to login -{% endif %} -{% endblock %} - - - - - - - - - - - -{% comment %} - ,-._|\ - / .\ - \_,--._/ -{% endcomment %} - - - -{% verbatim %} - ,-._|\ - / .\ - \_,--._/ -{% endverbatim %} - - - -{% comment "Yo dawg" %} - I heard you like comments -{% endcomment %} - - - -{%comment %} - %} -{% endcomment%} -{%if%} -{%block content %} -Yuck! -{% endblock%} -{% endif %} - - - -{% block %} -1 -{% with %} -2 -{% endblock %} -1 -{% endwith %} - - - -{% blocktrans count count=queryset.count %} -There is 1 item. -{% plural %} -There are {{ count }} items. -{% endblocktrans %} - - - -{% if %} -{% elif %} -{% else %} -{% endif %} -{% for %} -{% empty %} -{% endfor %} - - - - -Click here! - - - - -{% video form.instance.video as my_video %} -{% video my_video '360 x 200' %} -{% endvideo %} - - - -{% component_block "instruction" border_top=True header="Login" %} -{% slot "body" %} -{% with "login --token="|add:token as content %} -{% component "copy_field" field_selector="token-field" content=content %} -{% endwith %} -{% endslot %} -{% endcomponent_block %} diff --git a/tests/suite/html.html b/tests/suite/html.html new file mode 100644 index 0000000..cf13b31 --- /dev/null +++ b/tests/suite/html.html @@ -0,0 +1,330 @@ + + + + + Plain + + + + +

Title

+ + + + + +
+ text +
+ text +
+ + + +
+
+
+ text +
+ +
+ text +
+
+
+ + + text2 + text3 + + text3 + + + + + + + + text3 + + text1 + + + + +
+
+ text +
+ + + +
+ + + + + +
+ + + + + + + + + + + + + + + + +
+   
+
+
+
+ + + + +
+    
+ {# + #} + + + +
+
+
+
+
+
+ +
+
+
+ +
+
+
+ + + + + + + + + + + +
bar %}class="baz"{% endif %}> + quux +
+ + + +
+ text +
+ +
+ text +
+ + + + + + + + Click me! + + + + +
+ > +
+
+ < +
+ + + +
+ +
+ + + + + text + + + text + + + text + + + text + + + + +
+
+
+
+
+
+
+
+
+
+
+
+ + + +
+ text +
+ + +
', mode=DjHTML)], level=3) +Line([Text(' ', mode=DjHTML), Close('
', mode=DjHTML)], level=2) +Line([Text(' ', mode=DjHTML), Close('', mode=DjHTML)], level=1) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('section', mode=InsideHTMLTag, absolute=9), Open('>', mode=DjHTML, level=1)], level=1) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('div', mode=InsideHTMLTag, absolute=5), Open('>', mode=DjHTML, level=2)], level=2) +Line([Text(' ', mode=DjHTML), Text('{% placeholder "placeholder3" %}', mode=DjTXT)], level=3) +Line([Text(' ', mode=DjHTML), Close('', mode=DjHTML)], level=2) +Line([Text(' ', mode=DjHTML), Close('', mode=DjHTML)], level=1) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('section', mode=InsideHTMLTag, absolute=9), Open('>', mode=DjHTML, level=1)], level=1) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('div', mode=InsideHTMLTag, absolute=5), Open('>', mode=DjHTML, level=2)], level=2) +Line([Text(' ', mode=DjHTML), Text('{% placeholder "placeholder4" %}', mode=DjTXT)], level=3) +Line([Text(' ', mode=DjHTML), Close('', mode=DjHTML)], level=2) +Line([Text(' ', mode=DjHTML), Close('', mode=DjHTML)], level=1) +Line([Close('{% endblock %}', mode=DjTXT)]) +Line([]) \ No newline at end of file diff --git a/tests/suite/html.tokens b/tests/suite/html.tokens new file mode 100644 index 0000000..e66c65f --- /dev/null +++ b/tests/suite/html.tokens @@ -0,0 +1,355 @@ +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('!doctype html>', mode=DjHTML)]) +Line([Text('<', mode=DjHTML), Text('html', mode=InsideHTMLTag, absolute=6), Open('>', mode=DjHTML)]) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('head', mode=InsideHTMLTag, absolute=6), Open('>', mode=DjHTML, level=1)], level=1) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('title', mode=InsideHTMLTag, absolute=7), Open('>', mode=DjHTML, level=2), Text('Plain', mode=DjHTML), Close('', mode=DjHTML)], level=2) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('script ', mode=InsideHTMLTag, absolute=8), Text('src=', mode=InsideHTMLTag, absolute=8), Text('"', mode=InsideHTMLTag, absolute=13), Text('test.js', mode=InsideHTMLTag, absolute=13), Text('"', mode=InsideHTMLTag, absolute=12), Open('>', mode=DjHTML, level=2), Close('', mode=DjHTML)], level=2) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('link ', mode=InsideHTMLTag, absolute=6), Text('rel=', mode=InsideHTMLTag, absolute=6), Text('"', mode=InsideHTMLTag, absolute=11), Text('stylesheet', mode=InsideHTMLTag, absolute=11), Text('"', mode=InsideHTMLTag, absolute=10), Text(' ', mode=InsideHTMLTag, absolute=6), Text('href=', mode=InsideHTMLTag, absolute=6), Text('"', mode=InsideHTMLTag, absolute=29), Text('test.css', mode=InsideHTMLTag, absolute=29), Text('"', mode=InsideHTMLTag, absolute=28), Text('>', mode=DjHTML)], level=2) +Line([Text(' ', mode=DjHTML), Close('', mode=DjHTML)], level=1) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('body', mode=InsideHTMLTag, absolute=6), Open('>', mode=DjHTML, level=1)], level=1) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('h1', mode=InsideHTMLTag, absolute=4), Open('>', mode=DjHTML, level=2), Text('Title', mode=DjHTML), Close('', mode=DjHTML)], level=2) +Line([Text(' ', mode=DjHTML), Close('', mode=DjHTML)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('div', mode=InsideHTMLTag, absolute=5), Open('>', mode=DjHTML)]) +Line([Text(' text', mode=DjHTML)], level=1) +Line([Close('', mode=DjHTML), Text('<', mode=DjHTML), Text('div', mode=InsideHTMLTag, absolute=11), Open('>', mode=DjHTML)]) +Line([Text(' text', mode=DjHTML)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('div', mode=InsideHTMLTag, absolute=5), Open('>', mode=DjHTML)]) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('div', mode=InsideHTMLTag, absolute=5), Open('>', mode=DjHTML, level=1)], level=1) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('div', mode=InsideHTMLTag, absolute=5), Open('>', mode=DjHTML, level=2)], level=2) +Line([Text(' text', mode=DjHTML)], level=3) +Line([Text(' ', mode=DjHTML), Close('', mode=DjHTML), Close('', mode=DjHTML), Close('', mode=DjHTML)], level=2) +Line([]) +Line([Text('<', mode=DjHTML), Text('div', mode=InsideHTMLTag, absolute=5), Open('>', mode=DjHTML), Text('<', mode=DjHTML), Text('div', mode=InsideHTMLTag, relative=-1, absolute=10), Open('>', mode=DjHTML), Text('<', mode=DjHTML), Text('div', mode=InsideHTMLTag, relative=-1, absolute=15), Open('>', mode=DjHTML)]) +Line([Text(' text', mode=DjHTML)], level=1) +Line([Close('', mode=DjHTML)]) +Line([Close('', mode=DjHTML)]) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([Text('<', mode=DjHTML), Text('div0', mode=InsideHTMLTag, absolute=6), Open('>', mode=DjHTML), Text('<', mode=DjHTML), Text('div1', mode=InsideHTMLTag, relative=-1, absolute=12), Open('>', mode=DjHTML)]) +Line([Text(' text2', mode=DjHTML), Close('', mode=DjHTML), Text('<', mode=DjHTML), Text('div1', mode=InsideHTMLTag, absolute=18), Open('>', mode=DjHTML, level=1), Text('<', mode=DjHTML), Text('div2', mode=InsideHTMLTag, absolute=24), Open('>', mode=DjHTML, level=1)], level=1) +Line([Text(' text3', mode=DjHTML)], level=2) +Line([Text(' ', mode=DjHTML), Close('', mode=DjHTML), Text('<', mode=DjHTML), Text('div2', mode=InsideHTMLTag, absolute=13), Open('>', mode=DjHTML, level=1)], level=1) +Line([Text(' text3', mode=DjHTML)], level=2) +Line([Text(' ', mode=DjHTML), Close('', mode=DjHTML)], level=1) +Line([Text(' ', mode=DjHTML), Close('', mode=DjHTML)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([Text('<', mode=DjHTML), Text('div0', mode=InsideHTMLTag, absolute=6), Open('>', mode=DjHTML)]) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('div1', mode=InsideHTMLTag, absolute=6), Open('>', mode=DjHTML, level=1)], level=1) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('div2', mode=InsideHTMLTag, absolute=6), Open('>', mode=DjHTML, level=2)], level=2) +Line([Text(' text3', mode=DjHTML)], level=3) +Line([Text(' ', mode=DjHTML), Close('', mode=DjHTML), Close('', mode=DjHTML), Close('', mode=DjHTML), Text('<', mode=DjHTML), Text('div0', mode=InsideHTMLTag, absolute=27), Open('>', mode=DjHTML, level=2)], level=2) +Line([Text(' text1', mode=DjHTML)], level=3) +Line([Text(' ', mode=DjHTML), Close('', mode=DjHTML)], level=2) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('div', mode=InsideHTMLTag, absolute=5), Open('>', mode=DjHTML)]) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('div', mode=InsideHTMLTag, absolute=5), Text('/>', mode=DjHTML)], level=1) +Line([Text(' text', mode=DjHTML)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('div', mode=InsideHTMLTag, absolute=5), Open('>', mode=DjHTML)]) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('img', mode=InsideHTMLTag, absolute=5), Text('>', mode=DjHTML)], level=1) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('input', mode=InsideHTMLTag, absolute=7), Text('>', mode=DjHTML)], level=1) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('input ', mode=InsideHTMLTag, absolute=7), Text('/>', mode=DjHTML)], level=1) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('input', mode=InsideHTMLTag, absolute=7), Text('>', mode=DjHTML), Text('', mode=DjHTML)], level=1) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('!doctype>', mode=DjHTML)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('script', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML)]) +Line([Text('
    ', mode=DjJS)], level=1) +Line([Text('
  • ', mode=DjJS)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([Text('<', mode=DjHTML), Text('style', mode=InsideHTMLTag, absolute=7), Open('>', mode=DjHTML)]) +Line([Text(' ', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('script ', mode=InsideHTMLTag, absolute=8), Text('id=', mode=InsideHTMLTag, absolute=8), Text('"', mode=InsideHTMLTag, absolute=12), Text('id', mode=InsideHTMLTag, absolute=12), Text('"', mode=InsideHTMLTag, absolute=11), Text(' ', mode=InsideHTMLTag, absolute=8), Text('type=', mode=InsideHTMLTag, absolute=8), Text('"', mode=InsideHTMLTag, absolute=22), Text('text/template', mode=InsideHTMLTag, absolute=22), Text('"', mode=InsideHTMLTag, absolute=21), Text(' ', mode=InsideHTMLTag, absolute=8), Text('class=', mode=InsideHTMLTag, absolute=8), Text('"', mode=InsideHTMLTag, absolute=44), Text('class', mode=InsideHTMLTag, absolute=44), Text('"', mode=InsideHTMLTag, absolute=43), Open('>', mode=DjHTML)]) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('ul', mode=InsideHTMLTag, absolute=4), Open('>', mode=DjHTML, level=1)], level=1) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('li', mode=InsideHTMLTag, absolute=4), Text('/>', mode=DjHTML)], level=2) +Line([Text(' ', mode=DjHTML), Close('
', mode=DjHTML)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Open('
', mode=DjHTML, ignore=True)], ignore=True)
+Line([Text('   
', mode=Comment, ignore=True)], level=1, ignore=True) +Line([Text('
', mode=Comment, ignore=True)], level=1, ignore=True) +Line([Text('
', mode=Comment, ignore=True)], level=1, ignore=True) +Line([Close('
', mode=DjHTML, ignore=True)], ignore=True) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text(' ', mode=DjHTML), Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text(' ', mode=DjHTML), Open('
', mode=DjHTML, ignore=True)], ignore=True)
+Line([Text('    ', mode=Comment, ignore=True), Close('
', mode=DjHTML, ignore=True)], ignore=True) +Line([Text(' ', mode=DjHTML), Open('{#', mode=DjTXT, ignore=True)], ignore=True) +Line([Text(' ', mode=Comment, ignore=True), Close('#}', mode=DjTXT, ignore=True)], ignore=True) +Line([Text(' ', mode=DjHTML), Open('{#', mode=DjTXT, ignore=True), Close('#}', mode=DjTXT, ignore=True)], ignore=True) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('div ', mode=InsideHTMLTag, absolute=5), Text('class=', mode=InsideHTMLTag, absolute=5), Text('"', mode=InsideHTMLTag, absolute=12), Text('row', mode=InsideHTMLTag, absolute=12), Text('"', mode=InsideHTMLTag, absolute=11), Open('>', mode=DjHTML)]) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('div ', mode=InsideHTMLTag, absolute=5), Text('class=', mode=InsideHTMLTag, absolute=5), Text('"', mode=InsideHTMLTag, absolute=12), Text('input-group date', mode=InsideHTMLTag, absolute=12), Text('"', mode=InsideHTMLTag, absolute=11), Text(' ', mode=InsideHTMLTag, absolute=5), Text('data-provide=', mode=InsideHTMLTag, absolute=5), Text('"', mode=InsideHTMLTag, absolute=44), Text('datepicker', mode=InsideHTMLTag, absolute=44), Text('"', mode=InsideHTMLTag, absolute=43), Text(' ', mode=InsideHTMLTag, absolute=5), Text('data-date-format=', mode=InsideHTMLTag, absolute=5), Text('"', mode=InsideHTMLTag, absolute=74), Text('yyyy/mm/dd', mode=InsideHTMLTag, absolute=74), Text('"', mode=InsideHTMLTag, absolute=73), Open('>', mode=DjHTML, level=1)], level=1) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('div ', mode=InsideHTMLTag, absolute=5), Text('class=', mode=InsideHTMLTag, absolute=5), Text('"', mode=InsideHTMLTag, absolute=12), Text('btn', mode=InsideHTMLTag, absolute=12), Text('"', mode=InsideHTMLTag, absolute=11), Open('>', mode=DjHTML, level=2)], level=2) +Line([Text(' ', mode=DjHTML), Close('', mode=DjHTML), Close('', mode=DjHTML)], level=2) +Line([Close('', mode=DjHTML), Text('<', mode=DjHTML), Text('div', mode=InsideHTMLTag, absolute=11), Open('>', mode=DjHTML)]) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('div ', mode=InsideHTMLTag, absolute=5), Text('class=', mode=InsideHTMLTag, absolute=5), Text('"', mode=InsideHTMLTag, absolute=12), Text('custom-control custom-switch', mode=InsideHTMLTag, absolute=12), Text('"', mode=InsideHTMLTag, absolute=11), Open('>', mode=DjHTML, level=1)], level=1) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('input ', mode=InsideHTMLTag, absolute=7), Text('type=', mode=InsideHTMLTag, absolute=7), Text('"', mode=InsideHTMLTag, absolute=13), Text('checkbox', mode=InsideHTMLTag, absolute=13), Text('"', mode=InsideHTMLTag, absolute=12), Text(' ', mode=InsideHTMLTag, absolute=7), Text('name=', mode=InsideHTMLTag, absolute=7), Text('"', mode=InsideHTMLTag, absolute=29), Text('filter-changes', mode=InsideHTMLTag, absolute=29), Text('"', mode=InsideHTMLTag, absolute=28), Text(' ', mode=InsideHTMLTag, absolute=7), Text('id=', mode=InsideHTMLTag, absolute=7), Text('"', mode=InsideHTMLTag, absolute=49), Text('filter-changes', mode=InsideHTMLTag, absolute=49), Text('"', mode=InsideHTMLTag, absolute=48), Text(' ', mode=InsideHTMLTag, absolute=7), Text('class=', mode=InsideHTMLTag, absolute=7), Text('"', mode=InsideHTMLTag, absolute=72), Text('custom-control-input fs-nano', mode=InsideHTMLTag, absolute=72), Text('"', mode=InsideHTMLTag, absolute=71), Text(' ', mode=InsideHTMLTag, absolute=7), Text('value=', mode=InsideHTMLTag, absolute=7), Text('"', mode=InsideHTMLTag, absolute=109), Text('0', mode=InsideHTMLTag, absolute=109), Text('"', mode=InsideHTMLTag, absolute=108), Text(' ', mode=InsideHTMLTag, absolute=7), Open('{% if request.session.manage_filter_changes %}', mode=DjTXT, level=2, absolute=7), Open('{% if request.session.manage_filter_changes == True %}', mode=DjTXT, level=2, absolute=7), Text('checked', mode=InsideHTMLTag, absolute=7), Close('{% endif %}', mode=DjTXT, absolute=7), Close('{% endif %}', mode=DjTXT, absolute=7), Text('>', mode=DjHTML)], level=2) +Line([Text(' ', mode=DjHTML), Close('', mode=DjHTML)], level=1) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('form ', mode=InsideHTMLTag, absolute=6), Text('class=', mode=InsideHTMLTag, absolute=6), Text('"', mode=InsideHTMLTag, absolute=13), Text('form-inline', mode=InsideHTMLTag, absolute=13), Text('"', mode=InsideHTMLTag, absolute=12), Open('>', mode=DjHTML, level=1)], level=1) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('div ', mode=InsideHTMLTag, absolute=5), Text('class=', mode=InsideHTMLTag, absolute=5), Text('"', mode=InsideHTMLTag, absolute=12), Text('custom-control custom-switch', mode=InsideHTMLTag, absolute=12), Text('"', mode=InsideHTMLTag, absolute=11), Open('>', mode=DjHTML, level=2)], level=2) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('input ', mode=InsideHTMLTag, absolute=7), Text('type=', mode=InsideHTMLTag, absolute=7), Text('"', mode=InsideHTMLTag, absolute=13), Text('checkbox', mode=InsideHTMLTag, absolute=13), Text('"', mode=InsideHTMLTag, absolute=12), Text(' ', mode=InsideHTMLTag, absolute=7), Text('name=', mode=InsideHTMLTag, absolute=7), Text('"', mode=InsideHTMLTag, absolute=29), Text('filter-status-new', mode=InsideHTMLTag, absolute=29), Text('"', mode=InsideHTMLTag, absolute=28), Text(' ', mode=InsideHTMLTag, absolute=7), Text('id=', mode=InsideHTMLTag, absolute=7), Text('"', mode=InsideHTMLTag, absolute=52), Text('filter-status-new', mode=InsideHTMLTag, absolute=52), Text('"', mode=InsideHTMLTag, absolute=51), Text(' ', mode=InsideHTMLTag, absolute=7), Text('class=', mode=InsideHTMLTag, absolute=7), Text('"', mode=InsideHTMLTag, absolute=78), Text('custom-control-input fs-nano', mode=InsideHTMLTag, absolute=78), Text('"', mode=InsideHTMLTag, absolute=77), Text(' ', mode=InsideHTMLTag, absolute=7), Text('value=', mode=InsideHTMLTag, absolute=7), Text('"', mode=InsideHTMLTag, absolute=115), Text('0', mode=InsideHTMLTag, absolute=115), Text('"', mode=InsideHTMLTag, absolute=114), Text(' ', mode=InsideHTMLTag, absolute=7), Open('{% if request.session.manage_filter_status_new %}', mode=DjTXT, level=3, absolute=7), Open('{% if request.session.manage_filter_status_new == True %}', mode=DjTXT, level=3, absolute=7), Text('checked', mode=InsideHTMLTag, absolute=7), Close('{% endif %}', mode=DjTXT, absolute=7), Close('{% endif %}', mode=DjTXT, absolute=7), Text('>', mode=DjHTML)], level=3) +Line([Text(' ', mode=DjHTML), Close('', mode=DjHTML)], level=2) +Line([Text(' ', mode=DjHTML), Close('', mode=DjHTML)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('svg ', mode=InsideHTMLTag, absolute=5), Text('width=', mode=InsideHTMLTag, absolute=5), Text('"', mode=InsideHTMLTag, absolute=12), Text('325', mode=InsideHTMLTag, absolute=12), Text('"', mode=InsideHTMLTag, absolute=11), Text(' ', mode=InsideHTMLTag, absolute=5), Text('height=', mode=InsideHTMLTag, absolute=5), Text('"', mode=InsideHTMLTag, absolute=25), Text('325', mode=InsideHTMLTag, absolute=25), Text('"', mode=InsideHTMLTag, absolute=24), Text(' ', mode=InsideHTMLTag, absolute=5), Text('xmlns=', mode=InsideHTMLTag, absolute=5), Text('"', mode=InsideHTMLTag, absolute=37), Text('http://www.w3.org/2000/svg', mode=InsideHTMLTag, absolute=37), Text('"', mode=InsideHTMLTag, absolute=36), Open('>', mode=DjHTML)]) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('path', mode=InsideHTMLTag, absolute=6)], level=1) +Line([Text(' ', mode=InsideHTMLTag, absolute=6), Text('d=', mode=InsideHTMLTag, absolute=6), Text('"', mode=InsideHTMLTag, absolute=9), Text('M 80 80', mode=InsideHTMLTag, absolute=9)], level=1, offset=6) +Line([Text(' A 45 45, 0, 0, 0, 125 125', mode=InsideHTMLTag, absolute=9)], level=1, offset=9) +Line([Text(' L 125 80 Z', mode=InsideHTMLTag, absolute=9), Text('"', mode=InsideHTMLTag, absolute=8)], level=1, offset=9) +Line([Text(' ', mode=InsideHTMLTag, absolute=6), Text('fill=', mode=InsideHTMLTag, absolute=6), Text('"', mode=InsideHTMLTag, absolute=12), Text('green', mode=InsideHTMLTag, absolute=12), Text('"', mode=InsideHTMLTag, absolute=11)], level=1, offset=6) +Line([Text(' ', mode=InsideHTMLTag, absolute=6), Text('/>', mode=DjHTML)], level=1) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('path', mode=InsideHTMLTag, absolute=6)], level=1) +Line([Text(' ', mode=InsideHTMLTag, absolute=6), Text('d=', mode=InsideHTMLTag, absolute=6), Text('"', mode=InsideHTMLTag, absolute=9)], level=1, offset=6) +Line([Text(' M 80 80', mode=InsideHTMLTag, absolute=9)], level=1, offset=9) +Line([Text(' A 45 45, 0, 0, 0, 125 125', mode=InsideHTMLTag, absolute=9)], level=1, offset=9) +Line([Text(' L 125 80 Z', mode=InsideHTMLTag, absolute=9)], level=1, offset=9) +Line([Text(' ', mode=InsideHTMLTag, absolute=9), Text('"', mode=InsideHTMLTag, absolute=8)], level=1, offset=8) +Line([Text(' ', mode=InsideHTMLTag, absolute=6), Text('fill=', mode=InsideHTMLTag, absolute=6), Text('"', mode=InsideHTMLTag, absolute=12), Text('green', mode=InsideHTMLTag, absolute=12), Text('"', mode=InsideHTMLTag, absolute=11)], level=1, offset=6) +Line([Text(' ', mode=InsideHTMLTag, absolute=6), Text('/>', mode=DjHTML)], level=1) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('path ', mode=InsideHTMLTag, absolute=6), Text('d=', mode=InsideHTMLTag, absolute=6), Text('"', mode=InsideHTMLTag, absolute=9), Text('M 80 80', mode=InsideHTMLTag, absolute=9)], level=1) +Line([Text(' A 45 45, 0, 0, 0, 125 125', mode=InsideHTMLTag, absolute=9)], level=1, offset=9) +Line([Text(' L 125 80 Z', mode=InsideHTMLTag, absolute=9), Text('"', mode=InsideHTMLTag, absolute=8)], level=1, offset=9) +Line([Text(' ', mode=InsideHTMLTag, absolute=6), Text('fill=', mode=InsideHTMLTag, absolute=6), Text('"', mode=InsideHTMLTag, absolute=12), Text('green', mode=InsideHTMLTag, absolute=12), Text('"', mode=InsideHTMLTag, absolute=11)], level=1, offset=6) +Line([Text(' ', mode=InsideHTMLTag, absolute=6), Text('/>', mode=DjHTML)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('div ', mode=InsideHTMLTag, absolute=5), Open('{% if foo > bar %}', mode=DjTXT, absolute=5), Text('class=', mode=InsideHTMLTag, absolute=5), Text('"', mode=InsideHTMLTag, absolute=30), Text('baz', mode=InsideHTMLTag, absolute=30), Text('"', mode=InsideHTMLTag, absolute=29), Close('{% endif %}', mode=DjTXT, absolute=5), Open('>', mode=DjHTML)]) +Line([Text(' quux', mode=DjHTML)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('div', mode=InsideHTMLTag, absolute=5)]) +Line([Text(' ', mode=InsideHTMLTag, absolute=5), Open('{% block class %}', mode=DjTXT, absolute=5)], offset=5) +Line([Text(' ', mode=InsideHTMLTag, absolute=5), Text('class=', mode=InsideHTMLTag, absolute=5), Text('"', mode=InsideHTMLTag, absolute=12), Text('foo', mode=InsideHTMLTag, absolute=12), Text('"', mode=InsideHTMLTag, absolute=11)], level=1, offset=5) +Line([Text(' ', mode=InsideHTMLTag, absolute=5), Close('{% endblock %}', mode=DjTXT, absolute=5)], offset=5) +Line([Open('>', mode=DjHTML)]) +Line([Text(' text', mode=DjHTML)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([Text('<', mode=DjHTML), Text('div', mode=InsideHTMLTag, absolute=5)]) +Line([Text(' ', mode=InsideHTMLTag, absolute=5), Open('{% block classes %}', mode=DjTXT, absolute=5)], offset=5) +Line([Text(' ', mode=InsideHTMLTag, absolute=5), Text('class=', mode=InsideHTMLTag, absolute=5), Text('"', mode=InsideHTMLTag, absolute=12)], level=1, offset=5) +Line([Text(' foo', mode=InsideHTMLTag, absolute=12)], level=1, offset=12) +Line([Text(' bar', mode=InsideHTMLTag, absolute=12)], level=1, offset=12) +Line([Text(' ', mode=InsideHTMLTag, absolute=12), Text('"', mode=InsideHTMLTag, absolute=11)], level=1, offset=11) +Line([Text(' ', mode=InsideHTMLTag, absolute=5), Close('{% endblock %}', mode=DjTXT, absolute=5)], offset=5) +Line([Open('>', mode=DjHTML)]) +Line([Text(' text', mode=DjHTML)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([Text('<', mode=DjHTML), Text('div ', mode=InsideHTMLTag, absolute=5), Text('id=', mode=InsideHTMLTag, absolute=5), Text('"', mode=InsideHTMLTag, absolute=9), Text('1', mode=InsideHTMLTag, absolute=9), Text('"', mode=InsideHTMLTag, absolute=8), Text(' ', mode=InsideHTMLTag, absolute=5), Text('class=', mode=InsideHTMLTag, absolute=5), Text('"', mode=InsideHTMLTag, absolute=19), Open('{% block classes %}', mode=DjTXT, absolute=19)]) +Line([Text(' foo', mode=InsideHTMLTag, absolute=19)], level=1, offset=19) +Line([Text(' bar', mode=InsideHTMLTag, absolute=19)], level=1, offset=19) +Line([Text(' ', mode=InsideHTMLTag, absolute=19), Open('{% if %}', mode=DjTXT, level=1, absolute=19)], level=1, offset=19) +Line([Text(' baz', mode=InsideHTMLTag, absolute=19)], level=2, offset=19) +Line([Text(' ', mode=InsideHTMLTag, absolute=19), CloseAndOpen('{% elif %}', mode=DjTXT, level=1, absolute=19)], level=1, offset=19) +Line([Text(' quux', mode=InsideHTMLTag, absolute=19)], level=2, offset=19) +Line([Text(' ', mode=InsideHTMLTag, absolute=19), Close('{% endif %}', mode=DjTXT, absolute=19)], level=1, offset=19) +Line([Text(' ', mode=InsideHTMLTag, absolute=19), Close('{% endblock %}', mode=DjTXT, absolute=19)], offset=19) +Line([Text(' ', mode=InsideHTMLTag, absolute=19), Text('"', mode=InsideHTMLTag, absolute=18)], offset=18) +Line([Text(' hidden', mode=InsideHTMLTag, absolute=5)], offset=5) +Line([Open('>', mode=DjHTML)]) +Line([Text(' text', mode=DjHTML)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('a ', mode=InsideHTMLTag, absolute=3), Text('href=', mode=InsideHTMLTag, absolute=3), Text('"', mode=InsideHTMLTag, absolute=9), Text('{% url %}', mode=DjTXT, absolute=9), Text('"', mode=InsideHTMLTag, absolute=8)]) +Line([Text(' ', mode=InsideHTMLTag, absolute=3), Text('class=', mode=InsideHTMLTag, absolute=3), Text('"', mode=InsideHTMLTag, absolute=10), Text('button', mode=InsideHTMLTag, absolute=10), Text('"', mode=InsideHTMLTag, absolute=9)], offset=3) +Line([Open('>', mode=DjHTML)]) +Line([Text(' Click me!', mode=DjHTML)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('div', mode=InsideHTMLTag, absolute=5), Open('>', mode=DjHTML)]) +Line([Text(' >', mode=DjHTML)], level=1) +Line([Close('', mode=DjHTML)]) +Line([Text('<', mode=DjHTML), Text('div', mode=InsideHTMLTag, absolute=5), Open('>', mode=DjHTML)]) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('div', mode=InsideHTMLTag, absolute=5), Open('>', mode=DjHTML)]) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('button ', mode=InsideHTMLTag, absolute=8), Text('action=', mode=InsideHTMLTag, absolute=8), Text('"', mode=InsideHTMLTag, absolute=16), Text('a-', mode=InsideHTMLTag, absolute=16), Text('>', mode=InsideHTMLTag, absolute=16), Text('b', mode=InsideHTMLTag, absolute=16), Text('"', mode=InsideHTMLTag, absolute=15), Open('>', mode=DjHTML, level=1)], level=1) +Line([Text(' hi', mode=DjHTML)], level=2) +Line([Text(' ', mode=DjHTML), Close('', mode=DjHTML)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('a ', mode=InsideHTMLTag, absolute=3), Text('id=', mode=InsideHTMLTag, absolute=3), Text('"', mode=InsideHTMLTag, absolute=7), Text('1', mode=InsideHTMLTag, absolute=7), Text('"', mode=InsideHTMLTag, absolute=6), Text(' ', mode=InsideHTMLTag, absolute=3), Text('href=', mode=InsideHTMLTag, absolute=3), Text('"', mode=InsideHTMLTag, absolute=16), Text('"', mode=InsideHTMLTag, absolute=15)]) +Line([Text(' ', mode=InsideHTMLTag, absolute=3), Text('class=', mode=InsideHTMLTag, absolute=3), Text('"', mode=InsideHTMLTag, absolute=10), Text('button', mode=InsideHTMLTag, absolute=10), Text('"', mode=InsideHTMLTag, absolute=9), Open('>', mode=DjHTML)], offset=3) +Line([Text(' text', mode=DjHTML)], level=1) +Line([Close('', mode=DjHTML)]) +Line([Text('<', mode=DjHTML), Text('a', mode=InsideHTMLTag, absolute=3)]) +Line([Text(' ', mode=InsideHTMLTag, absolute=3), Text('id=', mode=InsideHTMLTag, absolute=3), Text('"', mode=InsideHTMLTag, absolute=7), Text('2', mode=InsideHTMLTag, absolute=7), Text('"', mode=InsideHTMLTag, absolute=6), Text(' ', mode=InsideHTMLTag, absolute=3), Text('href=', mode=InsideHTMLTag, absolute=3), Text('"', mode=InsideHTMLTag, absolute=16), Text('"', mode=InsideHTMLTag, absolute=15)], offset=3) +Line([Text(' ', mode=InsideHTMLTag, absolute=3), Text('class=', mode=InsideHTMLTag, absolute=3), Text('"', mode=InsideHTMLTag, absolute=10), Text('button', mode=InsideHTMLTag, absolute=10), Text('"', mode=InsideHTMLTag, absolute=9), Open('>', mode=DjHTML)], offset=3) +Line([Text(' text', mode=DjHTML)], level=1) +Line([Close('', mode=DjHTML)]) +Line([Text('<', mode=DjHTML), Text('a ', mode=InsideHTMLTag, absolute=3), Text('id=', mode=InsideHTMLTag, absolute=3), Text('"', mode=InsideHTMLTag, absolute=7), Text('3', mode=InsideHTMLTag, absolute=7), Text('"', mode=InsideHTMLTag, absolute=6), Text(' ', mode=InsideHTMLTag, absolute=3), Text('href=', mode=InsideHTMLTag, absolute=3), Text('"', mode=InsideHTMLTag, absolute=16), Text('"', mode=InsideHTMLTag, absolute=15)]) +Line([Text(' ', mode=InsideHTMLTag, absolute=3), Text('class=', mode=InsideHTMLTag, absolute=3), Text('"', mode=InsideHTMLTag, absolute=10), Text('button', mode=InsideHTMLTag, absolute=10), Text('"', mode=InsideHTMLTag, absolute=9)], offset=3) +Line([Open('>', mode=DjHTML)]) +Line([Text(' text', mode=DjHTML)], level=1) +Line([Close('', mode=DjHTML)]) +Line([Text('<', mode=DjHTML), Text('a', mode=InsideHTMLTag, absolute=3)]) +Line([Text(' ', mode=InsideHTMLTag, absolute=3), Text('id=', mode=InsideHTMLTag, absolute=3), Text('"', mode=InsideHTMLTag, absolute=7), Text('4', mode=InsideHTMLTag, absolute=7), Text('"', mode=InsideHTMLTag, absolute=6), Text(' ', mode=InsideHTMLTag, absolute=3), Text('href=', mode=InsideHTMLTag, absolute=3), Text('"', mode=InsideHTMLTag, absolute=16), Text('"', mode=InsideHTMLTag, absolute=15)], offset=3) +Line([Text(' ', mode=InsideHTMLTag, absolute=3), Text('class=', mode=InsideHTMLTag, absolute=3), Text('"', mode=InsideHTMLTag, absolute=10), Text('button', mode=InsideHTMLTag, absolute=10), Text('"', mode=InsideHTMLTag, absolute=9)], offset=3) +Line([Open('>', mode=DjHTML)]) +Line([Text(' text', mode=DjHTML)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('img ', mode=InsideHTMLTag, absolute=5), Text('src=', mode=InsideHTMLTag, absolute=5), Text('"', mode=InsideHTMLTag, absolute=10), Text('/some/long/path', mode=InsideHTMLTag, absolute=10), Text('"', mode=InsideHTMLTag, absolute=9)]) +Line([Text(' ', mode=InsideHTMLTag, absolute=5), Text('id=', mode=InsideHTMLTag, absolute=5), Text('"', mode=InsideHTMLTag, absolute=9), Text('1', mode=InsideHTMLTag, absolute=9), Text('"', mode=InsideHTMLTag, absolute=8), Text('>', mode=DjHTML)], offset=5) +Line([Text('<', mode=DjHTML), Text('blockquote ', mode=InsideHTMLTag, absolute=12), Text('class=', mode=InsideHTMLTag, absolute=12), Text('"', mode=InsideHTMLTag, absolute=19), Text('dishy', mode=InsideHTMLTag, absolute=19), Text('"', mode=InsideHTMLTag, absolute=18)]) +Line([Text(' ', mode=InsideHTMLTag, absolute=12), Text('id=', mode=InsideHTMLTag, absolute=12), Text('"', mode=InsideHTMLTag, absolute=16), Text('2', mode=InsideHTMLTag, absolute=16), Text('"', mode=InsideHTMLTag, absolute=15), Text(' ', mode=InsideHTMLTag, absolute=12), Text('/>', mode=DjHTML)], offset=12) +Line([Text('<', mode=DjHTML), Text('a ', mode=InsideHTMLTag, absolute=3), Text('href=', mode=InsideHTMLTag, absolute=3), Text('"', mode=InsideHTMLTag, absolute=9), Text('/some/long/path', mode=InsideHTMLTag, absolute=9), Text('"', mode=InsideHTMLTag, absolute=8)]) +Line([Text(' ', mode=InsideHTMLTag, absolute=3), Text('id=', mode=InsideHTMLTag, absolute=3), Text('"', mode=InsideHTMLTag, absolute=7), Text('3', mode=InsideHTMLTag, absolute=7), Text('"', mode=InsideHTMLTag, absolute=6), Open('>', mode=DjHTML), Close('', mode=DjHTML)], offset=3) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('div ', mode=InsideHTMLTag, absolute=5), Text('id=', mode=InsideHTMLTag, absolute=5), Text('"', mode=InsideHTMLTag, absolute=9)]) +Line([Text(' 1', mode=InsideHTMLTag, absolute=9), Text('"', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML)], offset=9) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('div ', mode=InsideHTMLTag, absolute=5), Text('id=', mode=InsideHTMLTag, absolute=5), Text('"', mode=InsideHTMLTag, absolute=9), Text('2', mode=InsideHTMLTag, absolute=9)], level=1) +Line([Text(' ', mode=InsideHTMLTag, absolute=9), Text('"', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML, level=1)], level=1, offset=8) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('div ', mode=InsideHTMLTag, absolute=5), Text('id=', mode=InsideHTMLTag, absolute=5), Text('"', mode=InsideHTMLTag, absolute=9)], level=2) +Line([Text(' 3', mode=InsideHTMLTag, absolute=9)], level=2, offset=9) +Line([Text(' ', mode=InsideHTMLTag, absolute=9), Text('"', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML, level=2)], level=2, offset=8) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('div ', mode=InsideHTMLTag, absolute=5), Text('id=', mode=InsideHTMLTag, absolute=5), Text('"', mode=InsideHTMLTag, absolute=9)], level=3) +Line([Text(' 4', mode=InsideHTMLTag, absolute=9), Text('"', mode=InsideHTMLTag, absolute=8)], level=3, offset=9) +Line([Text(' ', mode=InsideHTMLTag, absolute=5), Open('>', mode=DjHTML, level=3)], level=3) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('div ', mode=InsideHTMLTag, absolute=5), Text('id=', mode=InsideHTMLTag, absolute=5), Text('"', mode=InsideHTMLTag, absolute=9), Text('5', mode=InsideHTMLTag, absolute=9)], level=4) +Line([Text(' ', mode=InsideHTMLTag, absolute=9), Text('"', mode=InsideHTMLTag, absolute=8)], level=4, offset=8) +Line([Text(' ', mode=InsideHTMLTag, absolute=5), Open('>', mode=DjHTML, level=4)], level=4) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('div ', mode=InsideHTMLTag, absolute=5), Text('id=', mode=InsideHTMLTag, absolute=5), Text('"', mode=InsideHTMLTag, absolute=9)], level=5) +Line([Text(' 6', mode=InsideHTMLTag, absolute=9)], level=5, offset=9) +Line([Text(' ', mode=InsideHTMLTag, absolute=9), Text('"', mode=InsideHTMLTag, absolute=8)], level=5, offset=8) +Line([Text(' ', mode=InsideHTMLTag, absolute=5), Open('>', mode=DjHTML, level=5)], level=5) +Line([Text(' ', mode=DjHTML), Close('', mode=DjHTML)], level=5) +Line([Text(' ', mode=DjHTML), Close('', mode=DjHTML)], level=4) +Line([Text(' ', mode=DjHTML), Close('', mode=DjHTML)], level=3) +Line([Text(' ', mode=DjHTML), Close('', mode=DjHTML)], level=2) +Line([Text(' ', mode=DjHTML), Close('', mode=DjHTML)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('div ', mode=InsideHTMLTag, absolute=5), Text('!click=', mode=InsideHTMLTag, absolute=5), Text('"', mode=InsideHTMLTag, absolute=13)]) +Line([Text(' ', mode=InsideHTMLTag, absolute=13), Text('"', mode=InsideHTMLTag, absolute=12)], offset=12) +Line([Text(' ', mode=InsideHTMLTag, absolute=5), Text('@click=', mode=InsideHTMLTag, absolute=5), Text('"', mode=InsideHTMLTag, absolute=13)], offset=5) +Line([Text(' ', mode=InsideHTMLTag, absolute=13), Text('"', mode=InsideHTMLTag, absolute=12)], offset=12) +Line([Text(' ', mode=InsideHTMLTag, absolute=5), Text('#click=', mode=InsideHTMLTag, absolute=5), Text('"', mode=InsideHTMLTag, absolute=13)], offset=5) +Line([Text(' ', mode=InsideHTMLTag, absolute=13), Text('"', mode=InsideHTMLTag, absolute=12)], offset=12) +Line([Text(' ', mode=InsideHTMLTag, absolute=5), Text('$click=', mode=InsideHTMLTag, absolute=5), Text('"', mode=InsideHTMLTag, absolute=13)], offset=5) +Line([Text(' ', mode=InsideHTMLTag, absolute=13), Text('"', mode=InsideHTMLTag, absolute=12)], offset=12) +Line([Text(' ', mode=InsideHTMLTag, absolute=5), Text('%click=', mode=InsideHTMLTag, absolute=5), Text('"', mode=InsideHTMLTag, absolute=13)], offset=5) +Line([Text(' ', mode=InsideHTMLTag, absolute=13), Text('"', mode=InsideHTMLTag, absolute=12)], offset=12) +Line([Text(' ', mode=InsideHTMLTag, absolute=5), Text('^click=', mode=InsideHTMLTag, absolute=5), Text('"', mode=InsideHTMLTag, absolute=13)], offset=5) +Line([Text(' ', mode=InsideHTMLTag, absolute=13), Text('"', mode=InsideHTMLTag, absolute=12)], offset=12) +Line([Text(' ', mode=InsideHTMLTag, absolute=5), Text('&click=', mode=InsideHTMLTag, absolute=5), Text('"', mode=InsideHTMLTag, absolute=13)], offset=5) +Line([Text(' ', mode=InsideHTMLTag, absolute=13), Text('"', mode=InsideHTMLTag, absolute=12)], offset=12) +Line([Text(' ', mode=InsideHTMLTag, absolute=5), Text('*click=', mode=InsideHTMLTag, absolute=5), Text('"', mode=InsideHTMLTag, absolute=13)], offset=5) +Line([Text(' ', mode=InsideHTMLTag, absolute=13), Text('"', mode=InsideHTMLTag, absolute=12)], offset=12) +Line([Text(' ', mode=InsideHTMLTag, absolute=5), Text('', mode=DjHTML)]) +Line([Text(' text', mode=DjHTML)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('my-component', mode=InsideHTMLTag, absolute=14)]) +Line([Text(' hidden', mode=InsideHTMLTag, absolute=14), Text('/>', mode=DjHTML)], offset=14) +Line([Text('<', mode=DjHTML), Text('my.component', mode=InsideHTMLTag, absolute=14)]) +Line([Text(' hidden', mode=InsideHTMLTag, absolute=14), Text('/>', mode=DjHTML)], offset=14) +Line([Text('<', mode=DjHTML), Text('my:component', mode=InsideHTMLTag, absolute=14)]) +Line([Text(' hidden', mode=InsideHTMLTag, absolute=14), Text('/>', mode=DjHTML)], offset=14) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('div', mode=InsideHTMLTag, absolute=5), Open('>', mode=DjHTML), Text('text', mode=DjHTML), Text('<', mode=DjHTML), Text('blockquote ', mode=InsideHTMLTag, relative=-1, absolute=21), Text('id=', mode=InsideHTMLTag, relative=-1, absolute=21), Text('"', mode=InsideHTMLTag, relative=-1, absolute=25), Text('1', mode=InsideHTMLTag, relative=-1, absolute=25), Text('"', mode=InsideHTMLTag, relative=-1, absolute=24), Text(' ', mode=InsideHTMLTag, relative=-1, absolute=21), Text('id=', mode=InsideHTMLTag, relative=-1, absolute=21), Text('"', mode=InsideHTMLTag, relative=-1, absolute=32), Text('2', mode=InsideHTMLTag, relative=-1, absolute=32), Text('"', mode=InsideHTMLTag, relative=-1, absolute=31)]) +Line([Text(' ', mode=InsideHTMLTag, relative=-1, absolute=21), Text('id=', mode=InsideHTMLTag, relative=-1, absolute=21), Text('"', mode=InsideHTMLTag, relative=-1, absolute=25), Text('3', mode=InsideHTMLTag, relative=-1, absolute=25), Text('"', mode=InsideHTMLTag, relative=-1, absolute=24), Text(' ', mode=InsideHTMLTag, relative=-1, absolute=21), Text('cite=', mode=InsideHTMLTag, relative=-1, absolute=21), Text('"', mode=InsideHTMLTag, relative=-1, absolute=34)], offset=21) +Line([Text(' Wikipedia', mode=InsideHTMLTag, relative=-1, absolute=34)], offset=34) +Line([Text(' ', mode=InsideHTMLTag, relative=-1, absolute=34), Text('"', mode=InsideHTMLTag, relative=-1, absolute=33)], offset=33) +Line([Text(' ', mode=InsideHTMLTag, relative=-1, absolute=21), Text('id=', mode=InsideHTMLTag, relative=-1, absolute=21), Text('"', mode=InsideHTMLTag, relative=-1, absolute=25), Text('4', mode=InsideHTMLTag, relative=-1, absolute=25), Text('"', mode=InsideHTMLTag, relative=-1, absolute=24), Text('/>', mode=DjHTML), Close('', mode=DjHTML)], offset=21) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('a ', mode=InsideHTMLTag, absolute=3), Text('href=', mode=InsideHTMLTag, absolute=3), Text('"', mode=InsideHTMLTag, absolute=9), Text('"', mode=InsideHTMLTag, absolute=8)]) +Line([Text(' ', mode=InsideHTMLTag, absolute=3), Text('{% include "attrs.html" %}', mode=DjTXT, absolute=3)], offset=3) +Line([Text(' ', mode=InsideHTMLTag, absolute=3), Text('class=', mode=InsideHTMLTag, absolute=3), Text('"', mode=InsideHTMLTag, absolute=10)], offset=3) +Line([Text(' ', mode=InsideHTMLTag, absolute=10), Open('{% block classes %}', mode=DjTXT, absolute=10)], offset=10) +Line([Text(' foo', mode=InsideHTMLTag, absolute=10)], level=1, offset=10) +Line([Text(' bar', mode=InsideHTMLTag, absolute=10)], level=1, offset=10) +Line([Text(' ', mode=InsideHTMLTag, absolute=10), Close('{% endblock %}', mode=DjTXT, absolute=10)], offset=10) +Line([Text(' ', mode=InsideHTMLTag, absolute=10), Text('"', mode=InsideHTMLTag, absolute=9)], offset=9) +Line([Text('/>', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('blockquote ', mode=InsideHTMLTag, absolute=12), Text('cite=', mode=InsideHTMLTag, absolute=12), Text('"', mode=InsideHTMLTag, absolute=18), Text('Guido Van Rossum', mode=InsideHTMLTag, absolute=18), Text('"', mode=InsideHTMLTag, absolute=17)]) +Line([Text(' ', mode=InsideHTMLTag, absolute=12), Text('style=', mode=InsideHTMLTag, absolute=12), Text('"', mode=InsideHTMLTag, absolute=19), Text('font-style: italic;', mode=InsideHTMLTag, absolute=19)], offset=12) +Line([Text(' ', mode=InsideHTMLTag, absolute=19), Open('{% if dark_mode %}', mode=DjTXT, absolute=19)], offset=19) +Line([Text(' background: black;', mode=InsideHTMLTag, absolute=19)], level=1, offset=19) +Line([Text(' ', mode=InsideHTMLTag, absolute=19), Close('{% endif %}', mode=DjTXT, absolute=19)], offset=19) +Line([Text(' ', mode=InsideHTMLTag, absolute=19), Text('"', mode=InsideHTMLTag, absolute=18), Open('>', mode=DjHTML)], offset=18) +Line([Text(" Don't you hate code that's not properly indented?", mode=DjHTML)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('blockquote ', mode=InsideHTMLTag, absolute=12), Text('class=', mode=InsideHTMLTag, absolute=12), Text("'", mode=InsideHTMLTag, absolute=19), Text('foo', mode=InsideHTMLTag, absolute=19)]) +Line([Text(' ', mode=InsideHTMLTag, absolute=19), Text("'", mode=InsideHTMLTag, absolute=18)], offset=18) +Line([Text(' ', mode=InsideHTMLTag, absolute=12), Text('class=', mode=InsideHTMLTag, absolute=12), Text("'", mode=InsideHTMLTag, absolute=19), Text('bar', mode=InsideHTMLTag, absolute=19), Text('"', mode=InsideHTMLTag, absolute=19)], offset=12) +Line([Text(' ', mode=InsideHTMLTag, absolute=19), Text("'", mode=InsideHTMLTag, absolute=18)], offset=18) +Line([Text(' ', mode=InsideHTMLTag, absolute=12), Text('class=', mode=InsideHTMLTag, absolute=12), Text('"', mode=InsideHTMLTag, absolute=19), Text('baz', mode=InsideHTMLTag, absolute=19), Text("'", mode=InsideHTMLTag, absolute=19)], offset=12) +Line([Text(' ', mode=InsideHTMLTag, absolute=19), Text('"', mode=InsideHTMLTag, absolute=18)], offset=18) +Line([Text('/>', mode=DjHTML)]) +Line([]) \ No newline at end of file diff --git a/tests/suite/jinja.tokens b/tests/suite/jinja.tokens new file mode 100644 index 0000000..744a4f2 --- /dev/null +++ b/tests/suite/jinja.tokens @@ -0,0 +1,55 @@ +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Open('{#', mode=DjTXT, ignore=True)], ignore=True) +Line([Text(' ,-._|\\', mode=Comment, ignore=True)], level=1, ignore=True) +Line([Text(' / .\\', mode=Comment, ignore=True)], level=1, ignore=True) +Line([Text(' \\_,--._/', mode=Comment, ignore=True)], level=1, ignore=True) +Line([Close('#}', mode=DjTXT, ignore=True)], ignore=True) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Open('{% raw %}', mode=DjTXT, ignore=True)], ignore=True) +Line([Text(' ,-._|\\', mode=Comment, ignore=True)], level=1, ignore=True) +Line([Text(' / .\\', mode=Comment, ignore=True)], level=1, ignore=True) +Line([Text(' \\_,--._/', mode=Comment, ignore=True)], level=1, ignore=True) +Line([Close('{% endraw %}', mode=DjTXT, ignore=True)], ignore=True) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('div', mode=InsideHTMLTag, absolute=5), Open('>', mode=DjHTML)]) +Line([Text(' ', mode=DjHTML), Open('{%+ if something %}', mode=DjTXT, level=1)], level=1) +Line([Text(' yay', mode=DjHTML)], level=2) +Line([Text(' ', mode=DjHTML), Close('{% endif %}', mode=DjTXT)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([Text('<', mode=DjHTML), Text('div', mode=InsideHTMLTag, absolute=5), Open('>', mode=DjHTML)]) +Line([Text(' ', mode=DjHTML), Open('{% if something +%}', mode=DjTXT, level=1)], level=1) +Line([Text(' yay', mode=DjHTML)], level=2) +Line([Text(' ', mode=DjHTML), Close('{% endif %}', mode=DjTXT)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([Open('{% for item in seq -%}', mode=DjTXT)]) +Line([Text(' ', mode=DjHTML), Text('{{ item }}', mode=DjHTML)], level=1) +Line([Close('{%- endfor %}', mode=DjTXT)]) +Line([]) +Line([Open('{%- macro render_currency(currency, amount) -%}', mode=DjTXT)]) +Line([Text(' ', mode=DjHTML), Text('{{ amount }}', mode=DjHTML)], level=1) +Line([Close('{%- endmacro -%}', mode=DjTXT)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('div', mode=InsideHTMLTag, absolute=5), Open('>', mode=DjHTML)]) +Line([Text(' ', mode=DjHTML), Text('{% set "five" = 5 %}', mode=DjTXT)], level=1) +Line([Text(' Five is ', mode=DjHTML), Text('{{ five }}', mode=DjHTML)], level=1) +Line([Text(' ', mode=DjHTML), Open('{% set blockdata %}', mode=DjTXT, level=1)], level=1) +Line([Text(' Contents', mode=DjHTML)], level=2) +Line([Text(' ', mode=DjHTML), Close('{% endset %}', mode=DjTXT)], level=1) +Line([Text(' Contents of block is block', mode=DjHTML)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('{% if ( condition1 and', mode=DjHTML)]) +Line([Text('condition2 ) %}', mode=DjHTML)]) +Line([Text('text', mode=DjHTML)]) +Line([Close('{% endif %}', mode=DjTXT)]) +Line([]) \ No newline at end of file diff --git a/tests/suite/js.tokens b/tests/suite/js.tokens new file mode 100644 index 0000000..d7a5f82 --- /dev/null +++ b/tests/suite/js.tokens @@ -0,0 +1,447 @@ +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('script', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML)]) +Line([Text(' function f', mode=DjJS), Open('(', mode=DjJS, level=1), Close(')', mode=DjJS), Text(' ', mode=DjJS), Open('{', mode=DjJS, level=1)], level=1) +Line([Text(' return 42;', mode=DjJS)], level=2) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('script', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML)]) +Line([Text(' array = ', mode=DjJS), Open('[', mode=DjJS, level=1)], level=1) +Line([Text(' 1,', mode=DjJS)], level=2) +Line([Text(' 2,', mode=DjJS)], level=2) +Line([Text(' 3,', mode=DjJS)], level=2) +Line([Text(' ', mode=DjJS), Close(']', mode=DjJS)], level=1) +Line([]) +Line([Text(' dictionary = ', mode=DjJS), Open('{', mode=DjJS, level=1)], level=1) +Line([Text(' ', mode=DjJS), Text('value1:', mode=DjJS), Text(' 1,', mode=DjJS, relative=1)], level=2) +Line([Text(' ', mode=DjJS), Text('value2:', mode=DjJS), Text(' 2,', mode=DjJS, relative=1)], level=2) +Line([Text(' ', mode=DjJS), Text('value3:', mode=DjJS), Text(' 3,', mode=DjJS, relative=1)], level=2) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS)], level=1) +Line([]) +Line([Text(' function_call', mode=DjJS), Open('(', mode=DjJS, level=1)], level=1) +Line([Text(' arg1,', mode=DjJS)], level=2) +Line([Text(' arg2,', mode=DjJS)], level=2) +Line([Text(' arg3,', mode=DjJS)], level=2) +Line([Text(' ', mode=DjJS), Close(')', mode=DjJS)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('script', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML)]) +Line([Text(' one', mode=DjJS), Open('(', mode=DjJS, level=1), Close(')', mode=DjJS)], level=1) +Line([Text(' .two', mode=DjJS, relative=1), Open('(', mode=DjJS, level=2), Close(')', mode=DjJS)], level=2) +Line([Text(' .three', mode=DjJS, relative=1), Open('(', mode=DjJS, level=2), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=2) +Line([Text(' four', mode=DjJS), Open('(', mode=DjJS, level=1), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([Text('<', mode=DjHTML), Text('script', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML)]) +Line([Text(' function f', mode=DjJS), Open('(', mode=DjJS, level=1), Close(')', mode=DjJS), Text(' ', mode=DjJS), Open('{', mode=DjJS, level=1)], level=1) +Line([Text(' return 42;', mode=DjJS)], level=2) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('script', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML)]) +Line([Text(' $', mode=DjJS), Open('(', mode=DjJS, level=1), Text('function', mode=DjJS), Open('(', mode=DjJS, level=1), Close(')', mode=DjJS), Text(' ', mode=DjJS), Open('{', mode=DjJS, level=1)], level=1) +Line([Text(' this.addEventListener', mode=DjJS), Open('(', mode=DjJS, level=2), Text("'change'", mode=DjJS), Text(', function ', mode=DjJS), Open('(', mode=DjJS, level=2), Close(')', mode=DjJS), Text(' ', mode=DjJS), Open('{', mode=DjJS, level=2)], level=2) +Line([Text(' doStuff', mode=DjJS), Open('(', mode=DjJS, level=3), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=3) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=2) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('script', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML)]) +Line([Text(' ', mode=DjJS), Open('/*', mode=DjJS, level=1, ignore=True)], level=1, ignore=True) +Line([Text(' * Hi mom!', mode=Comment, ignore=True)], level=2, ignore=True) +Line([Text(' *', mode=Comment, ignore=True)], level=2, ignore=True) +Line([Text(' ', mode=Comment, ignore=True), Close('*/', mode=DjJS, ignore=True)], level=1, ignore=True) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('script', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML)]) +Line([Text(' console.log', mode=DjJS), Open('(', mode=DjJS, level=1), Text('"$(function() {"', mode=DjJS), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=1) +Line([Text(' console.log', mode=DjJS), Open('(', mode=DjJS, level=1), Text("'$(function() {'", mode=DjJS), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=1) +Line([Text(' console.log', mode=DjJS), Open('(', mode=DjJS, level=1), Text('`$(function() {`', mode=DjJS), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=1) +Line([Text(' console.log', mode=DjJS), Open('(', mode=DjJS, level=1), Text('`\nfunction f() {\nreturn 42;\n}\n `', mode=DjJS), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('script', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML)]) +Line([Text(' ', mode=DjJS), Text('const ', mode=DjJS), Text('format_duration_value = ', mode=DjJS, absolute=6), Open('(', mode=DjJS, level=1), Text('a', mode=DjJS), Close(')', mode=DjJS), Text(' => ', mode=DjJS, relative=1, absolute=6), Open('{', mode=DjJS, level=1)], level=1) +Line([Text(' ', mode=DjJS), Text('let ', mode=DjJS), Text('secs = a * 3600; ', mode=DjJS, absolute=4), Text('// get total amount of seconds from your input', mode=DjJS, ignore=True)], level=2) +Line([Text(' ', mode=DjJS), Text('const ', mode=DjJS), Text('days = Math.floor', mode=DjJS, absolute=6), Open('(', mode=DjJS, level=2), Text('secs / ', mode=DjJS), Open('(', mode=DjJS, level=2), Text('3600 * 24', mode=DjJS), Close(')', mode=DjJS), Close(')', mode=DjJS), Text(';', mode=DjJS, absolute=6)], level=2) +Line([Text(' return ', mode=DjJS), Open('(', mode=DjJS, level=2), Text('`\n
\n ${days.toString().padStart(2,\'0\')}\n m\n
\n `', mode=DjJS), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=2) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS), Text(';', mode=DjJS, absolute=6)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('script', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML)]) +Line([Text(' $', mode=DjJS), Open('(', mode=DjJS, level=1), Text('function1', mode=DjJS), Open('(', mode=DjJS, level=1), Close(')', mode=DjJS)], level=1) +Line([Text(' ', mode=DjJS), Open('{', mode=DjJS, level=2)], level=2) +Line([Text(' ', mode=DjJS), Text('var ', mode=DjJS), Text('foo = ', mode=DjJS, absolute=4), Open('(', mode=DjJS, level=3), Text('function2', mode=DjJS), Open('(', mode=DjJS, level=3), Close(')', mode=DjJS), Open('{', mode=DjJS, level=3)], level=3) +Line([Text(' doStuff', mode=DjJS), Open('(', mode=DjJS, level=4), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=4) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS), Close(')', mode=DjJS), Open('(', mode=DjJS, level=3), Close(')', mode=DjJS), Text(';', mode=DjJS, relative=1, absolute=4)], level=3) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=2) +Line([Text(' ', mode=DjJS), Text('// Jeez', mode=DjJS, ignore=True)], level=1, ignore=True) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('script', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML)]) +Line([Text(' ', mode=DjJS), Text('let ', mode=DjJS), Text('time = 10;', mode=DjJS, absolute=4)], level=1) +Line([Text(' ', mode=DjJS), Text('if', mode=DjJS), Text(' ', mode=DjJS, relative=1), Open('(', mode=DjJS, level=1), Text('time < 10', mode=DjJS), Close(')', mode=DjJS), Text(' ', mode=DjJS, relative=1), Open('{', mode=DjJS, level=1)], level=1) +Line([Text(' console.log', mode=DjJS), Open('(', mode=DjJS, level=2), Text('"Good morning"', mode=DjJS), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=2) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS), Text(' else ', mode=DjJS), Text('if', mode=DjJS), Text(' ', mode=DjJS), Open('(', mode=DjJS, level=1), Text('time < 20', mode=DjJS), Close(')', mode=DjJS), Text(' ', mode=DjJS), Open('{', mode=DjJS, level=1)], level=1) +Line([Text(' console.log', mode=DjJS), Open('(', mode=DjJS, level=2), Text('"Good day"', mode=DjJS), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=2) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS), Text(' else ', mode=DjJS), Open('{', mode=DjJS, level=1)], level=1) +Line([Text(' console.log', mode=DjJS), Open('(', mode=DjJS, level=2), Text('"Good evening"', mode=DjJS), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=2) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('script', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML)]) +Line([Text(' ', mode=DjJS), Text('const ', mode=DjJS), Text('mode = ', mode=DjJS, absolute=6), Text("'approved'", mode=DjJS, relative=1, absolute=6)], level=1) +Line([Text(' switch ', mode=DjJS), Open('(', mode=DjJS, level=1), Text('mode', mode=DjJS), Close(')', mode=DjJS), Text(' ', mode=DjJS), Open('{', mode=DjJS, level=1)], level=1) +Line([OpenDouble(' case ', mode=DjJS, level=2), Text("'approved'", mode=DjJS), Text(':', mode=DjJS)], level=2) +Line([Text(' console.log', mode=DjJS), Open('(', mode=DjJS, level=3), Text("'Approved'", mode=DjJS), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=3) +Line([Text(' break;', mode=DjJS)], level=3) +Line([OpenDouble(' case ', mode=DjJS, level=2), Text("'rejected'", mode=DjJS), Text(':', mode=DjJS)], level=2) +Line([Text(' console.log', mode=DjJS), Open('(', mode=DjJS, level=3), Text("'Rejected'", mode=DjJS), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=3) +Line([Text(' break;', mode=DjJS)], level=3) +Line([Text(' ', mode=DjJS), OpenDouble('default:', mode=DjJS, level=2)], level=2) +Line([Text(' console.log', mode=DjJS), Open('(', mode=DjJS, level=3), Text("'Default'", mode=DjJS), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=3) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS)], level=1) +Line([]) +Line([Text(' switch ', mode=DjJS), Open('(', mode=DjJS, level=1), Text('mode', mode=DjJS), Close(')', mode=DjJS), Text(' ', mode=DjJS), Open('{', mode=DjJS, level=1)], level=1) +Line([OpenDouble(' case ', mode=DjJS, level=2), Text("'approved'", mode=DjJS), Text(':', mode=DjJS)], level=2) +Line([Text(' console.log', mode=DjJS), Open('(', mode=DjJS, level=3), Text("'Approved'", mode=DjJS), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=3) +Line([Text(' break;', mode=DjJS)], level=3) +Line([Text(' ', mode=DjJS), OpenDouble('default:', mode=DjJS, level=2)], level=2) +Line([Text(' console.log', mode=DjJS), Open('(', mode=DjJS, level=3), Text("'Default'", mode=DjJS), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=3) +Line([OpenDouble(' case ', mode=DjJS, level=2), Text("'rejected'", mode=DjJS), Text(':', mode=DjJS)], level=2) +Line([Text(' console.log', mode=DjJS), Open('(', mode=DjJS, level=3), Text("'Rejected'", mode=DjJS), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=3) +Line([Text(' break;', mode=DjJS)], level=3) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS)], level=1) +Line([]) +Line([Text(' switch ', mode=DjJS), Open('(', mode=DjJS, level=1), Text('mode', mode=DjJS), Close(')', mode=DjJS), Text(' ', mode=DjJS), Open('{', mode=DjJS, level=1)], level=1) +Line([OpenDouble(' case ', mode=DjJS, level=2), Text("'approved'", mode=DjJS), Text(':', mode=DjJS)], level=2) +Line([OpenDouble(' case ', mode=DjJS, level=2), Text("'rejected'", mode=DjJS), Text(':', mode=DjJS)], level=2) +Line([OpenDouble(' case ', mode=DjJS, level=2), Text("'invalid'", mode=DjJS), Text(':', mode=DjJS)], level=2) +Line([Text(' console.log', mode=DjJS), Open('(', mode=DjJS, level=3), Text("'Not default'", mode=DjJS), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=3) +Line([Text(' break;', mode=DjJS)], level=3) +Line([Text(' ', mode=DjJS), OpenDouble('default:', mode=DjJS, level=2)], level=2) +Line([Text(' console.log', mode=DjJS), Open('(', mode=DjJS, level=3), Text("'Default'", mode=DjJS), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=3) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS)], level=1) +Line([]) +Line([Text(' switch ', mode=DjJS), Open('(', mode=DjJS, level=1), Text('mode', mode=DjJS), Close(')', mode=DjJS), Text(' ', mode=DjJS), Open('{', mode=DjJS, level=1)], level=1) +Line([OpenDouble(' case ', mode=DjJS, level=2), Text("'approved'", mode=DjJS), Text(': ', mode=DjJS), Open('{', mode=DjJS, level=2)], level=2) +Line([Text(' console.log', mode=DjJS), Open('(', mode=DjJS, level=3), Text("'Approved'", mode=DjJS), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=3) +Line([Text(' break;', mode=DjJS)], level=3) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS)], level=2) +Line([OpenDouble(' case ', mode=DjJS, level=2), Text("'rejected'", mode=DjJS), Text(': ', mode=DjJS), Open('{', mode=DjJS, level=2)], level=2) +Line([Text(' console.log', mode=DjJS), Open('(', mode=DjJS, level=3), Text("'Rejected'", mode=DjJS), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=3) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS)], level=2) +Line([Text(' break;', mode=DjJS)], level=3) +Line([Text(' ', mode=DjJS), OpenDouble('default:', mode=DjJS, level=2), Text(' ', mode=DjJS), Open('{', mode=DjJS, level=2)], level=2) +Line([Text(' console.log', mode=DjJS), Open('(', mode=DjJS, level=3), Text("'Default'", mode=DjJS), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=3) +Line([Text(' break;', mode=DjJS)], level=3) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS)], level=2) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('script', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML)]) +Line([Text(' ', mode=DjJS), Text('var ', mode=DjJS), Text('year = 2016;', mode=DjJS, absolute=4)], level=1) +Line([Text(' ', mode=DjJS), Text('var ', mode=DjJS), Text('month = 2;', mode=DjJS, absolute=4)], level=1) +Line([Text(' ', mode=DjJS), Text('var ', mode=DjJS), Text('dayCount;', mode=DjJS, absolute=4)], level=1) +Line([Text(' switch ', mode=DjJS), Open('(', mode=DjJS, level=1), Text('month', mode=DjJS), Close(')', mode=DjJS), Text(' ', mode=DjJS), Open('{', mode=DjJS, level=1)], level=1) +Line([OpenDouble(' case ', mode=DjJS, level=2), Text('1:', mode=DjJS)], level=2) +Line([Text(' function switchFoo ', mode=DjJS), Open('(', mode=DjJS, level=3), Close(')', mode=DjJS), Text(' ', mode=DjJS), Open('{', mode=DjJS, level=3)], level=3) +Line([Text(' console.log', mode=DjJS), Open('(', mode=DjJS, level=4), Text('"Foo"', mode=DjJS), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=4) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS)], level=3) +Line([Text(' switchFoo', mode=DjJS), Open('(', mode=DjJS, level=3), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=3) +Line([OpenDouble(' case ', mode=DjJS, level=2), Text('12:', mode=DjJS)], level=2) +Line([Text(' dayCount = 31;', mode=DjJS)], level=3) +Line([Text(' break;', mode=DjJS)], level=3) +Line([OpenDouble(' case ', mode=DjJS, level=2), Text('4:', mode=DjJS)], level=2) +Line([Text(' switch', mode=DjJS), Open('(', mode=DjJS, level=3), Text('boo', mode=DjJS), Close(')', mode=DjJS), Open('{', mode=DjJS, level=3)], level=3) +Line([OpenDouble(' case ', mode=DjJS, level=4), Text('1:', mode=DjJS)], level=4) +Line([OpenDouble(' case ', mode=DjJS, level=4), Text('2:', mode=DjJS)], level=4) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS)], level=3) +Line([OpenDouble(' case ', mode=DjJS, level=2), Text('11:', mode=DjJS)], level=2) +Line([Text(' dayCount = 30;', mode=DjJS)], level=3) +Line([Text(' break;', mode=DjJS)], level=3) +Line([OpenDouble(' case ', mode=DjJS, level=2), Text('2:', mode=DjJS)], level=2) +Line([Text(' ', mode=DjJS), Text('if', mode=DjJS), Text(' ', mode=DjJS, relative=1), Open('(', mode=DjJS, level=3), Open('(', mode=DjJS, level=3), Open('(', mode=DjJS, level=3), Text('year % 4 == 0', mode=DjJS), Close(')', mode=DjJS), Text(' && !', mode=DjJS), Open('(', mode=DjJS, level=3), Text('year % 100 == 0', mode=DjJS), Close(')', mode=DjJS), Close(')', mode=DjJS), Text(' || ', mode=DjJS), Open('(', mode=DjJS, level=3), Text('year % 400 == 0', mode=DjJS), Close(')', mode=DjJS), Close(')', mode=DjJS), Text(' ', mode=DjJS, relative=1), Open('{', mode=DjJS, level=3)], level=3) +Line([Text(' dayCount = 29;', mode=DjJS)], level=4) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS), Text(' else ', mode=DjJS), Open('{', mode=DjJS, level=3)], level=3) +Line([Text(' dayCount = 28;', mode=DjJS)], level=4) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS)], level=3) +Line([Text(' break;', mode=DjJS)], level=3) +Line([Text(' ', mode=DjJS), OpenDouble('default:', mode=DjJS, level=2)], level=2) +Line([Text(' dayCount = -1; ', mode=DjJS), Text('// invalid month', mode=DjJS, ignore=True)], level=3) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS)], level=1) +Line([Text(' console.log', mode=DjJS), Open('(', mode=DjJS, level=1), Text('dayCount', mode=DjJS), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('script', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML)]) +Line([Text(' switch ', mode=DjJS), Open('(', mode=DjJS, level=1), Text('a', mode=DjJS), Close(')', mode=DjJS), Text(' ', mode=DjJS), Open('{', mode=DjJS, level=1)], level=1) +Line([OpenDouble(' case ', mode=DjJS, level=2), Text('b:', mode=DjJS)], level=2) +Line([Text(' switch ', mode=DjJS), Open('(', mode=DjJS, level=3), Text('c', mode=DjJS), Close(')', mode=DjJS), Text(' ', mode=DjJS), Open('{', mode=DjJS, level=3)], level=3) +Line([OpenDouble(' case ', mode=DjJS, level=4), Text('d:', mode=DjJS)], level=4) +Line([Text(' switch ', mode=DjJS), Open('(', mode=DjJS, level=5), Text('e', mode=DjJS), Close(')', mode=DjJS), Text(' ', mode=DjJS), Open('{', mode=DjJS, level=5)], level=5) +Line([Text(' ', mode=DjJS), OpenDouble('default:', mode=DjJS, level=6)], level=6) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS)], level=5) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS)], level=3) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('script', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML)]) +Line([Text(' switch ', mode=DjJS), Open('(', mode=DjJS, level=1), Text('foo', mode=DjJS), Close(')', mode=DjJS), Text(' ', mode=DjJS), Open('{', mode=DjJS, level=1)], level=1) +Line([OpenDouble(' case ', mode=DjJS, level=2), Text('bar:', mode=DjJS), Text(' break', mode=DjJS)], level=2) +Line([Text(' ', mode=DjJS), OpenDouble('default:', mode=DjJS, level=2), Text(' return', mode=DjJS)], level=2) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('script', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML)]) +Line([Text(' function switchBoo ', mode=DjJS), Open('(', mode=DjJS, level=1), Close(')', mode=DjJS), Text(' ', mode=DjJS), Open('{', mode=DjJS, level=1)], level=1) +Line([Text(' console.log', mode=DjJS), Open('(', mode=DjJS, level=2), Text('"Boo"', mode=DjJS), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=2) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS)], level=1) +Line([Text(' switchBoo', mode=DjJS), Open('(', mode=DjJS, level=1), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=1) +Line([]) +Line([Text(' ', mode=DjJS), Text('let ', mode=DjJS), Text('arr = ', mode=DjJS, absolute=4), Open('[', mode=DjJS, level=1)], level=1) +Line([Text(' ', mode=DjJS), Text("'switch'", mode=DjJS), Text(',', mode=DjJS)], level=2) +Line([Text(' ', mode=DjJS), Text("'default:'", mode=DjJS), Text(',', mode=DjJS)], level=2) +Line([Text(' ', mode=DjJS), Text("'case '", mode=DjJS), Text(',', mode=DjJS)], level=2) +Line([Text(' ', mode=DjJS), Close(']', mode=DjJS), Text(';', mode=DjJS, absolute=4)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('script', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML)]) +Line([Text(' switch ', mode=DjJS), Open('(', mode=DjJS, level=1), Text('foo', mode=DjJS), Close(')', mode=DjJS), Text(' ', mode=DjJS), Open('{', mode=DjJS, level=1)], level=1) +Line([OpenDouble(' case ', mode=DjJS, level=2), Text('bar:', mode=DjJS)], level=2) +Line([Text(' ', mode=DjJS), Text('if', mode=DjJS), Text(' ', mode=DjJS, relative=1), Open('(', mode=DjJS, level=3), Text('baz', mode=DjJS), Close(')', mode=DjJS), Text(' ', mode=DjJS, relative=1), Open('{', mode=DjJS, level=3)], level=3) +Line([Text(' switch ', mode=DjJS), Open('(', mode=DjJS, level=4), Text('foo', mode=DjJS), Close(')', mode=DjJS), Text(' ', mode=DjJS), Open('{', mode=DjJS, level=4)], level=4) +Line([OpenDouble(' case ', mode=DjJS, level=5), Text('bar:', mode=DjJS)], level=5) +Line([Text(' ', mode=DjJS), Text('if', mode=DjJS), Text(' ', mode=DjJS, relative=1), Open('(', mode=DjJS, level=6), Text('baz', mode=DjJS), Close(')', mode=DjJS), Text(' ', mode=DjJS, relative=1), Open('{', mode=DjJS, level=6)], level=6) +Line([Text(' xizzy', mode=DjJS), Open('(', mode=DjJS, level=7), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=7) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS)], level=6) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS)], level=4) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS)], level=3) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('script', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML)]) +Line([Text(' window.fetch', mode=DjJS), Open('(', mode=DjJS, level=1), Text("'/test.html'", mode=DjJS), Close(')', mode=DjJS)], level=1) +Line([Text(' .then', mode=DjJS, relative=1), Open('(', mode=DjJS, level=2), Open('(', mode=DjJS, level=2), Text('html', mode=DjJS), Close(')', mode=DjJS), Text(' => ', mode=DjJS), Open('{', mode=DjJS, level=2)], level=2) +Line([Text(' document.body.innerHTML = html;', mode=DjJS)], level=3) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=2) +Line([]) +Line([Text(' foo', mode=DjJS)], level=1) +Line([Text(' .bar', mode=DjJS, relative=1)], level=2) +Line([Text(' .baz', mode=DjJS, relative=1)], level=2) +Line([Text(' .then', mode=DjJS, relative=1), Open('(', mode=DjJS, level=2), Open('(', mode=DjJS, level=2), Close(')', mode=DjJS), Text(' => ', mode=DjJS), Open('{', mode=DjJS, level=2)], level=2) +Line([Text(' foo', mode=DjJS)], level=3) +Line([Text(' .bar', mode=DjJS, relative=1)], level=4) +Line([Text(' .baz', mode=DjJS, relative=1)], level=4) +Line([Text(' .then', mode=DjJS, relative=1), Open('(', mode=DjJS, level=4), Open('(', mode=DjJS, level=4), Close(')', mode=DjJS), Text(' => ', mode=DjJS), Open('{', mode=DjJS, level=4)], level=4) +Line([Text(' foo', mode=DjJS)], level=5) +Line([Text(' .bar', mode=DjJS, relative=1)], level=6) +Line([Text(' .baz', mode=DjJS, relative=1)], level=6) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=4) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=2) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('head', mode=InsideHTMLTag, absolute=6), Open('>', mode=DjHTML)]) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('script', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML, level=1)], level=1) +Line([Text(' function foo', mode=DjJS), Open('(', mode=DjJS, level=2), Close(')', mode=DjJS), Text(' ', mode=DjJS), Open('{', mode=DjJS, level=2)], level=2) +Line([Text(' ', mode=DjJS), Close('', mode=DjHTML)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('head', mode=InsideHTMLTag, absolute=6), Open('>', mode=DjHTML)]) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('script', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML, level=1)], level=1) +Line([Text(' function foo', mode=DjJS), Open('(', mode=DjJS, level=2), Close(')', mode=DjJS), Text(' ', mode=DjJS), Open('{', mode=DjJS, level=2)], level=2) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS)], level=2) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS)], level=2) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS)], level=2) +Line([Text(' ', mode=DjJS), Close('', mode=DjHTML)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('script', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML)]) +Line([Text(' ', mode=DjJS), Text('// The following does not contain a comment', mode=DjJS, ignore=True)], level=1, ignore=True) +Line([Text(' wsHost = ', mode=DjJS), Open('(', mode=DjJS, level=1), Text('/^wss?:\\/\\//', mode=DjJS), Text('i.test', mode=DjJS), Open('(', mode=DjJS, level=1), Text('me._host', mode=DjJS), Close(')', mode=DjJS), Text(' ? ', mode=DjJS), Text("''", mode=DjJS), Text(' : me._protocol', mode=DjJS), Close(')', mode=DjJS), Text(' + me._host;', mode=DjJS, relative=1)], level=1) +Line([]) +Line([Text(' foo; ', mode=DjJS), Text('// This is a comment', mode=DjJS, ignore=True)], level=1) +Line([Text(' bar ', mode=DjJS), Text('//This too', mode=DjJS, ignore=True)], level=1) +Line([Text(' baz', mode=DjJS), Text('//Even this', mode=DjJS, ignore=True)], level=1) +Line([]) +Line([Text(' foo ', mode=DjJS), Text('// function() {', mode=DjJS, ignore=True)], level=1) +Line([Text(' bar ', mode=DjJS), Text('// var a = 1;', mode=DjJS, ignore=True)], level=1) +Line([Text(' baz ', mode=DjJS), Text('// }', mode=DjJS, ignore=True)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('script', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML)]) +Line([Text(' ', mode=DjJS), Text('var ', mode=DjJS), Text('a = 1,', mode=DjJS, absolute=4)], level=1) +Line([Text(' b = 2,', mode=DjJS, absolute=4)], level=1, offset=4) +Line([Text(' c;', mode=DjJS, absolute=4)], level=1, offset=4) +Line([Text(' ', mode=DjJS), Text('var ', mode=DjJS), Text('foo = bar; ', mode=DjJS, absolute=4), Text('let ', mode=DjJS), Text('a = 1,', mode=DjJS, absolute=19)], level=1) +Line([Text(' b = 2,', mode=DjJS, absolute=19)], level=1, offset=19) +Line([Text(' c', mode=DjJS, absolute=19)], level=1, offset=19) +Line([]) +Line([Text(' ', mode=DjJS), Text('// Haskell-style indentation', mode=DjJS, ignore=True)], level=1, ignore=True) +Line([Text(' ', mode=DjJS), Text('const ', mode=DjJS), Text('a = 1', mode=DjJS, absolute=6)], level=1) +Line([Text(' , b = 2', mode=DjJS, absolute=4)], level=1, offset=4) +Line([Text(' , c', mode=DjJS, absolute=4)], level=1, offset=4) +Line([]) +Line([Text(' ', mode=DjJS), Text('// Notice the first line is the same', mode=DjJS, ignore=True)], level=1, ignore=True) +Line([Text(' ', mode=DjJS), Text('const ', mode=DjJS), Text('a = 1', mode=DjJS, absolute=6)], level=1) +Line([Text(' b = 2', mode=DjJS)], level=1) +Line([Text(' c = 3', mode=DjJS)], level=1) +Line([]) +Line([Text(' ', mode=DjJS), Text('// Yes, this makes sense', mode=DjJS, ignore=True)], level=1, ignore=True) +Line([Text(' ', mode=DjJS), Text('const ', mode=DjJS), Text('func1 = function', mode=DjJS, absolute=6), Open('(', mode=DjJS, level=1), Close(')', mode=DjJS), Text(' ', mode=DjJS, absolute=6), Open('{', mode=DjJS, level=1)], level=1) +Line([Text(' return foo', mode=DjJS)], level=2) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS), Text(',', mode=DjJS, absolute=6)], level=1) +Line([Text(' func2 = function', mode=DjJS, absolute=6), Open('(', mode=DjJS, level=1), Close(')', mode=DjJS), Text(' ', mode=DjJS, absolute=6), Open('{', mode=DjJS, level=1), Text(' return 2 ', mode=DjJS), Close('}', mode=DjJS), Text(',', mode=DjJS, absolute=6)], level=1, offset=6) +Line([Text(' func3 = function', mode=DjJS, absolute=6), Open('(', mode=DjJS, level=1), Close(')', mode=DjJS), Text(' ', mode=DjJS, absolute=6), Open('{', mode=DjJS, level=1), Text(' return 3 ', mode=DjJS), Close('}', mode=DjJS)], level=1, offset=6) +Line([]) +Line([Text(' ', mode=DjJS), Text('const ', mode=DjJS), Text('foo = bar,', mode=DjJS, absolute=6)], level=1) +Line([Text(' baz = ', mode=DjJS, absolute=6), Open('{', mode=DjJS, level=1)], level=1, offset=6) +Line([Text(' ...', mode=DjJS)], level=2) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS)], level=1) +Line([]) +Line([Text(' ', mode=DjJS), Text('// We are not supporting madness like this', mode=DjJS, ignore=True)], level=1, ignore=True) +Line([Text(' ', mode=DjJS), Text('const ', mode=DjJS), Text('a = 1,', mode=DjJS, absolute=6)], level=1) +Line([Text(' ', mode=DjJS), Open('{% if %}', mode=DjTXT, level=1)], level=1) +Line([Text(' b = 2,', mode=DjJS)], level=2) +Line([Text(' ', mode=DjJS), Close('{% endif %}', mode=DjTXT)], level=1) +Line([Text(' c = 3;', mode=DjJS)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('script', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML)]) +Line([Text(' ', mode=DjJS), Text('var ', mode=DjJS), Text('case = ', mode=DjJS, absolute=4), Text('"foo"', mode=DjJS, relative=1, absolute=4)], level=1) +Line([Text(' ', mode=DjJS), Text('var ', mode=DjJS), Text('dict = ', mode=DjJS, absolute=4), Open('{', mode=DjJS, level=1)], level=1) +Line([Text(' ', mode=DjJS), Text('case:', mode=DjJS), Text(' ', mode=DjJS, relative=1), Text('"bar"', mode=DjJS, relative=1), Text(',', mode=DjJS)], level=2) +Line([Text(' ', mode=DjJS), Text('"default:"', mode=DjJS), Text(': ', mode=DjJS), Text('"baz"', mode=DjJS, relative=1), Text(',', mode=DjJS)], level=2) +Line([Text(' ...params,', mode=DjJS)], level=2) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('script', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML)]) +Line([Text(' foo', mode=DjJS)], level=1) +Line([Text(' ? bar', mode=DjJS, relative=1)], level=2) +Line([Text(' : baz', mode=DjJS, relative=1)], level=2) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('script', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML)]) +Line([Text(' ', mode=DjJS), Text('var ', mode=DjJS), Text('foo = ', mode=DjJS, absolute=4), Text("'\\'{'", mode=DjJS, relative=1, absolute=4)], level=1) +Line([Text(' ', mode=DjJS), Text('var ', mode=DjJS), Text('bar = ', mode=DjJS, absolute=4), Text('"\\"{"', mode=DjJS, relative=1, absolute=4)], level=1) +Line([Text(' ', mode=DjJS), Text('var ', mode=DjJS), Text('baz = ', mode=DjJS, absolute=4), Text('`\\`{`', mode=DjJS, relative=1, absolute=4)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('script', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML)]) +Line([Text(' ', mode=DjJS), Text('if', mode=DjJS), Text(' ', mode=DjJS, relative=1), Open('(', mode=DjJS, level=1), Text('foo', mode=DjJS), Close(')', mode=DjJS)], level=1) +Line([Text(' bar', mode=DjJS, relative=1)], level=2) +Line([]) +Line([Text(' ', mode=DjJS), Text('if', mode=DjJS), Text(' ', mode=DjJS, relative=1), Open('(', mode=DjJS, level=1), Text('foo', mode=DjJS), Close(')', mode=DjJS), Text(' return', mode=DjJS, relative=1)], level=1) +Line([Text(' bar', mode=DjJS)], level=1) +Line([]) +Line([Text(' ', mode=DjJS), Text('if', mode=DjJS), Text(' ', mode=DjJS, relative=1), Open('(', mode=DjJS, level=1)], level=1) +Line([Text(' foo &&', mode=DjJS)], level=2) +Line([Text(' bar', mode=DjJS)], level=2) +Line([Text(' ', mode=DjJS), Close(')', mode=DjJS)], level=1) +Line([Text(' baz', mode=DjJS, relative=1)], level=2) +Line([Text(' ', mode=DjJS), Text('else', mode=DjJS)], level=1) +Line([Text(' return', mode=DjJS, relative=1)], level=2) +Line([]) +Line([Text(' ', mode=DjJS), Text('if', mode=DjJS), Text(' ', mode=DjJS, relative=1), Open('(', mode=DjJS, level=1), Text('foo', mode=DjJS), Close(')', mode=DjJS)], level=1) +Line([Text(' ', mode=DjJS, relative=1), Text('if', mode=DjJS, relative=1), Text(' ', mode=DjJS, relative=2), Open('(', mode=DjJS, level=2), Text('bar', mode=DjJS), Close(')', mode=DjJS)], level=2) +Line([Text(' ', mode=DjJS, relative=2), Text('if', mode=DjJS, relative=2), Text(' ', mode=DjJS, relative=3), Open('(', mode=DjJS, level=3), Text('baz', mode=DjJS), Close(')', mode=DjJS)], level=3) +Line([Text(' return', mode=DjJS, relative=3)], level=4) +Line([Text(' ', mode=DjJS), Text('else', mode=DjJS)], level=1) +Line([Text(' console.log', mode=DjJS, relative=1), Open('(', mode=DjJS, level=2), Text('"Footgun detected!"', mode=DjJS), Close(')', mode=DjJS)], level=2) +Line([]) +Line([Text(' leitmot', mode=DjJS), Text('if', mode=DjJS), Text(' ', mode=DjJS), Open('(', mode=DjJS, level=1), Close(')', mode=DjJS)], level=1) +Line([Text(' elsewhere ', mode=DjJS), Open('(', mode=DjJS, level=1), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=1) +Line([Text(' mean', mode=DjJS), Text('while', mode=DjJS), Text(' ', mode=DjJS), Open('(', mode=DjJS, level=1), Text('true', mode=DjJS), Close(')', mode=DjJS)], level=1) +Line([]) +Line([Text(' ', mode=DjJS), Text('while', mode=DjJS), Text(' ', mode=DjJS, relative=1), Open('(', mode=DjJS, level=1), Text('true', mode=DjJS), Close(')', mode=DjJS)], level=1) +Line([Text(' break', mode=DjJS, relative=1)], level=2) +Line([]) +Line([Text(' ', mode=DjJS), Text('for', mode=DjJS), Text(' ', mode=DjJS, relative=1), Open('(', mode=DjJS, level=1), Text('i=1, 1<10, i++', mode=DjJS), Close(')', mode=DjJS)], level=1) +Line([Text(' ', mode=DjJS, relative=1), Open('{', mode=DjJS, level=1)], level=1) +Line([Text(' break', mode=DjJS)], level=2) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('script', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML)]) +Line([Text(' ', mode=DjJS), Text('var ', mode=DjJS), Text('foo =', mode=DjJS, absolute=4)], level=1) +Line([Text(' bar', mode=DjJS, relative=1)], level=2) +Line([]) +Line([Text(' ', mode=DjJS), Text('const ', mode=DjJS), Text('func =', mode=DjJS, absolute=6)], level=1) +Line([Text(' function', mode=DjJS, relative=1), Open('(', mode=DjJS, level=2), Close(')', mode=DjJS), Text(' ', mode=DjJS), Open('{', mode=DjJS, level=2)], level=2) +Line([Text(' return', mode=DjJS)], level=3) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS), Close(')', mode=DjJS)], level=2) +Line([]) +Line([Text(' ', mode=DjJS), Text('// Note that unquoted "default" is a reserved keyword', mode=DjJS, ignore=True)], level=1, ignore=True) +Line([Text(' ', mode=DjJS), Text('let ', mode=DjJS), Text('dict = ', mode=DjJS, absolute=4), Open('{', mode=DjJS, level=1)], level=1) +Line([Text(' ', mode=DjJS), Text('"default"', mode=DjJS), Text(':', mode=DjJS)], level=2) +Line([Text(' foo', mode=DjJS, relative=1)], level=3) +Line([Text(' ', mode=DjJS), Text('bar:', mode=DjJS)], level=2) +Line([Text(' baz', mode=DjJS, relative=1)], level=3) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('script', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML)]) +Line([Text(' re = ', mode=DjJS), Text('/ab+c/', mode=DjJS, relative=1)], level=1) +Line([Text(' space_re = ', mode=DjJS), Text('/ foo /', mode=DjJS, relative=1)], level=1) +Line([Text(' single_re = ', mode=DjJS), Text('/foo\\/ + bar/', mode=DjJS, relative=1)], level=1) +Line([Text(' double_re = ', mode=DjJS), Text('/foo/', mode=DjJS, relative=1), Text(' + ', mode=DjJS), Text('/bar/', mode=DjJS)], level=1) +Line([Text(' look_like_comment = ', mode=DjJS), Text('/\\(/', mode=DjJS, relative=1), Text('/', mode=DjJS)], level=1) +Line([]) +Line([Text(' ', mode=DjJS), Text('// The following is known to be broken', mode=DjJS, ignore=True)], level=1, ignore=True) +Line([Text(' ', mode=DjJS), Text('const ', mode=DjJS), Text('x = 42', mode=DjJS, absolute=6)], level=1) +Line([Text(' ', mode=DjJS), Text('const ', mode=DjJS), Text('y = 7', mode=DjJS, absolute=6)], level=1) +Line([Text(' ', mode=DjJS), Text('const ', mode=DjJS), Text('six = x ', mode=DjJS, absolute=6), Text('/ (function() { /', mode=DjJS, absolute=6), Text('/ comment', mode=DjJS, absolute=6)], level=1) +Line([Text(' return y', mode=DjJS)], level=1) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS), Close(')', mode=DjJS), Open('(', mode=DjJS, level=1), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) \ No newline at end of file diff --git a/tests/suite/paradoxes.tokens b/tests/suite/paradoxes.tokens new file mode 100644 index 0000000..3fbcfab --- /dev/null +++ b/tests/suite/paradoxes.tokens @@ -0,0 +1,51 @@ +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('div', mode=InsideHTMLTag, absolute=5), Open('>', mode=DjHTML)]) +Line([Text(' ', mode=DjHTML), Open('{% if some_condition %}', mode=DjTXT, level=1)], level=1) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('a ', mode=InsideHTMLTag, absolute=3), Text('href=', mode=InsideHTMLTag, absolute=3), Text('"', mode=InsideHTMLTag, absolute=9), Text('{% url a %}', mode=DjTXT, absolute=9), Text('"', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML, level=2)], level=2) +Line([Text(' ', mode=DjHTML), CloseAndOpen('{% elif some_other condition %}', mode=DjTXT, level=1)], level=1) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('a ', mode=InsideHTMLTag, absolute=3), Text('href=', mode=InsideHTMLTag, absolute=3), Text('"', mode=InsideHTMLTag, absolute=9), Text('{% url b %}', mode=DjTXT, absolute=9), Text('"', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML, level=2)], level=2) +Line([Text(' ', mode=DjHTML), Close('{% endif %}', mode=DjTXT)], level=1) +Line([Text(' .', mode=DjHTML)], level=1) +Line([Text(' ', mode=DjHTML), Open('{% if some_condition or some_other_condition %}', mode=DjTXT, level=1)], level=1) +Line([Text(' ', mode=DjHTML), Close('', mode=DjHTML)], level=2) +Line([Text(' ', mode=DjHTML), Close('{% endif %}', mode=DjTXT)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Open('{% if some_condition %}', mode=DjTXT)]) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('a ', mode=InsideHTMLTag, absolute=3), Text('href=', mode=InsideHTMLTag, absolute=3), Text('"', mode=InsideHTMLTag, absolute=9), Text('{% url some_url', mode=InsideHTMLTag, absolute=9), Text('"', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML, level=1)], level=1) +Line([CloseAndOpen('{% elif some_other_condition %}', mode=DjTXT)]) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('a ', mode=InsideHTMLTag, absolute=3), Text('href=', mode=InsideHTMLTag, absolute=3), Text('"', mode=InsideHTMLTag, absolute=9), Text('{% url some_other_url', mode=InsideHTMLTag, absolute=9), Text('"', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML, level=1)], level=1) +Line([Close('{% endif %}', mode=DjTXT)]) +Line([Text('[a bunch of details go here]', mode=DjHTML)]) +Line([Open('{% if end_tag_needed %}', mode=DjTXT), Close('', mode=DjHTML), Close('{% endif %}', mode=DjTXT)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Open('{% block %}', mode=DjTXT)]) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('a', mode=InsideHTMLTag, absolute=3), Open('>', mode=DjHTML, level=1)], level=1) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('a', mode=InsideHTMLTag, absolute=3), Open('>', mode=DjHTML, level=2)], level=2) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('a', mode=InsideHTMLTag, absolute=3), Open('>', mode=DjHTML, level=3)], level=3) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('a', mode=InsideHTMLTag, absolute=3), Open('>', mode=DjHTML, level=4)], level=4) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('a', mode=InsideHTMLTag, absolute=3), Open('>', mode=DjHTML, level=5)], level=5) +Line([Close('{% endblock %}', mode=DjTXT)]) +Line([Open('{% for x in [1,2,3,4,5] %}', mode=DjTXT)]) +Line([Text(' ', mode=DjHTML), Close('', mode=DjHTML)], level=1) +Line([Close('{% endfor %}', mode=DjTXT)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Text('<', mode=DjHTML), Text('script', mode=InsideHTMLTag, absolute=8), Open('>', mode=DjHTML)]) +Line([Text(' ', mode=DjJS), Open('{% if jquery %}', mode=DjTXT, level=1)], level=1) +Line([Text(' $', mode=DjJS), Open('(', mode=DjJS, level=2), Text('function', mode=DjJS), Open('(', mode=DjJS, level=2), Close(')', mode=DjJS), Text(' ', mode=DjJS), Open('{', mode=DjJS, level=2)], level=2) +Line([Text(' ', mode=DjJS), CloseAndOpen('{% else %}', mode=DjTXT, level=1)], level=1) +Line([Text(' document.addEventListener', mode=DjJS), Open('(', mode=DjJS, level=2), Text('"DOMContentLoaded"', mode=DjJS), Text(', function', mode=DjJS), Open('(', mode=DjJS, level=2), Text('event', mode=DjJS), Close(')', mode=DjJS), Text(' ', mode=DjJS), Open('{', mode=DjJS, level=2)], level=2) +Line([Text(' ', mode=DjJS), Close('{% endif %}', mode=DjTXT)], level=1) +Line([Text(' console.log', mode=DjJS), Open('(', mode=DjJS, level=1), Text('"Time to ditch jQuery!"', mode=DjJS), Close(')', mode=DjJS)], level=1) +Line([Text(' ', mode=DjJS), Close('}', mode=DjJS), Close(')', mode=DjJS), Text(';', mode=DjJS)], level=1) +Line([Close('', mode=DjHTML)]) +Line([]) +Line([]) +Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([]) \ No newline at end of file diff --git a/tests/test_suite.py b/tests/test_suite.py index d1979e2..e7a5c1e 100644 --- a/tests/test_suite.py +++ b/tests/test_suite.py @@ -1,12 +1,12 @@ -import os import unittest +from pathlib import Path from djhtml.modes import DjHTML class TestSuite(unittest.TestCase): maxDiff = None - DIR = os.path.join(os.path.dirname(__file__), "suite") + DIR = Path(__file__).parent / "suite" def test_available_files(self): """ @@ -14,14 +14,18 @@ def test_available_files(self): expected output to the actual output. """ - for filename in os.listdir(self.DIR): - with self.subTest(filename): - self._test_file(filename) + for filename in self.DIR.iterdir(): + if filename.suffix == ".html": + with self.subTest(filename): + self._test_file(filename.stem) - def _test_file(self, filename): - with open(os.path.join(self.DIR, filename)) as f: + def _test_file(self, basename): + with open(self.DIR / (basename + ".html")) as f: expected_output = f.read() + with open(self.DIR / (basename + ".tokens")) as f: + expected_tokens = f.read() + # Indent the expected output to 0 (no indentation) unindented = DjHTML(expected_output).indent(0) self.assertNotEqual(unindented, expected_output) @@ -29,3 +33,7 @@ def _test_file(self, filename): # Re-indent the unindented output to 4 actual_output = DjHTML(unindented).indent(4) self.assertEqual(expected_output, actual_output) + + # Compare the tokenization + actual_tokens = DjHTML(actual_output).debug() + self.assertEqual(expected_tokens, actual_tokens)