Skip to content

SOURCE CODE liquid.exts.filter_colon DOCS

"""Provides an extension to use colon to separate filter and its arguments

Jinja uses `{{a | filter(arg)}}`, but liquid uses `{{a | filter: arg}}`
"""
from typing import TYPE_CHECKING, Iterable
from jinja2.ext import Extension
from jinja2.lexer import (
    TOKEN_ASSIGN,
    TOKEN_BLOCK_END,
    TOKEN_COLON,
    TOKEN_LPAREN,
    TOKEN_NAME,
    TOKEN_PIPE,
    TOKEN_RPAREN,
    TOKEN_VARIABLE_END,
    Token,
)

if TYPE_CHECKING:
    from jinja2.lexer import TokenStream


class FilterColonExtension(Extension):DOCS
    """This extension allows colon to be used to separate
    the filter and arguments, so that we can write django/liquid-style filters
    """

    def filter_stream(self, stream: "TokenStream") -> Iterable[Token]:DOCS
        """Modify the colon to lparen and rparen tokens"""
        # expect a colon
        # 0: don't expect to change any {{a | filter: arg}}
        #    to {{a | filter(arg)}}
        # 1: expect a filter
        # 2: expect the colon
        # 3: expect rparen
        flag = 0

        for token in stream:
            # print(token.value, token.type)
            if flag == 0 and token.type is TOKEN_PIPE:
                flag = 1
            elif token.type is TOKEN_NAME and flag == 1:
                flag = 2
            elif token.type is TOKEN_COLON and flag == 2:
                flag = 3
                token = Token(token.lineno, TOKEN_LPAREN, None)
            elif token.type is TOKEN_COLON and flag == 3:
                # {{ a | filter: 1, x: 2}} => {{ a | filter: 1, x=2}}
                token = Token(token.lineno, TOKEN_ASSIGN, None)
            elif (
                token.type in (TOKEN_VARIABLE_END, TOKEN_BLOCK_END, TOKEN_PIPE)
                and flag == 3
            ):
                flag = 1 if token.type is TOKEN_PIPE else 0
                yield Token(token.lineno, TOKEN_RPAREN, None)
            elif token.type in (TOKEN_VARIABLE_END, TOKEN_BLOCK_END):
                flag = 0

            yield token