Commits

Tim Hatch committed 5309201

Import liquid lexer from #977

Resolves #977

  • Participants
  • Parent commits 4af43be

Comments (0)

Files changed (3)

File pygments/lexers/_mapping.py

     'LassoXmlLexer': ('pygments.lexers.templates', 'XML+Lasso', ('xml+lasso',), (), ('application/xml+lasso',)),
     'LighttpdConfLexer': ('pygments.lexers.text', 'Lighttpd configuration file', ('lighty', 'lighttpd'), (), ('text/x-lighttpd-conf',)),
     'LimboLexer': ('pygments.lexers.inferno', 'Limbo', ('limbo',), ('*.b',), ('text/limbo',)),
+    'LiquidLexer': ('pygments.lexers.templates', 'liquid', ('liquid',), ('*.liquid',), ()),
     'LiterateAgdaLexer': ('pygments.lexers.functional', 'Literate Agda', ('lagda', 'literate-agda'), ('*.lagda',), ('text/x-literate-agda',)),
     'LiterateCryptolLexer': ('pygments.lexers.functional', 'Literate Cryptol', ('lcry', 'literate-cryptol', 'lcryptol'), ('*.lcry',), ('text/x-literate-cryptol',)),
     'LiterateHaskellLexer': ('pygments.lexers.functional', 'Literate Haskell', ('lhs', 'literate-haskell', 'lhaskell'), ('*.lhs',), ('text/x-literate-haskell',)),

File pygments/lexers/templates.py

 from pygments.lexers.jvm import TeaLangLexer
 from pygments.lexers.text import YamlLexer
 from pygments.lexer import Lexer, DelegatingLexer, RegexLexer, bygroups, \
-     include, using, this, default
+     include, using, this, default, combined
 from pygments.token import Error, Punctuation, \
-     Text, Comment, Operator, Keyword, Name, String, Number, Other, Token
+     Text, Comment, Operator, Keyword, Name, String, Number, Other, Token, \
+     Whitespace
 from pygments.util import html_doctype_matches, looks_like_xml
 
 __all__ = ['HtmlPhpLexer', 'XmlPhpLexer', 'CssPhpLexer',
            'VelocityHtmlLexer', 'VelocityXmlLexer', 'SspLexer',
            'TeaTemplateLexer', 'LassoHtmlLexer', 'LassoXmlLexer',
            'LassoCssLexer', 'LassoJavascriptLexer', 'HandlebarsLexer',
-           'HandlebarsHtmlLexer', 'YamlJinjaLexer']
+           'HandlebarsHtmlLexer', 'YamlJinjaLexer', 'LiquidLexer']
 
 
 class ErbLexer(Lexer):
 
     def __init__(self, **options):
         super(YamlJinjaLexer, self).__init__(YamlLexer, DjangoLexer, **options)
+
+
+class LiquidLexer(RegexLexer):
+    name = 'liquid'
+    aliases = ['liquid']
+    filenames = ['*.liquid']
+
+    tokens = {
+        'root': [
+            (r'[^\{]+', Text),
+            (r'(\{%)(\s*)', bygroups(Punctuation, Whitespace), 'tag-or-block'), # tags and block tags
+            (r'(\{\{)(\s*)([^\s(\}\})]+)', bygroups(Punctuation, Whitespace, using(this, state = 'generic')), 'output'), # output tags
+            (r'\{', Text)
+        ],
+
+        'tag-or-block': [
+            # builtin logic blocks
+            (r'(if|unless|elsif|case)(?=\s+)', Keyword.Reserved, 'condition'),
+            (r'(when)(\s+)', bygroups(Keyword.Reserved, Whitespace), combined('end-of-block', 'whitespace', 'generic')),
+            (r'(else)(\s*)(%\})', bygroups(Keyword.Reserved, Whitespace, Punctuation), '#pop'),
+
+            # other builtin blocks
+            (r'(capture)(\s+)([^\s%]+)(\s*)(%\})', bygroups(Name.Tag, Whitespace, using(this, state = 'variable'), Whitespace, Punctuation), '#pop'),
+            (r'(comment)(\s*)(%\})', bygroups(Name.Tag, Whitespace, Punctuation), 'comment'),
+            (r'(raw)(\s*)(%\})', bygroups(Name.Tag, Whitespace, Punctuation), 'raw'),
+
+            # end of block
+            (r'(end(case|unless|if))(\s*)(%\})', bygroups(Keyword.Reserved, None, Whitespace, Punctuation), '#pop'),
+            (r'(end([^\s%]+))(\s*)(%\})', bygroups(Name.Tag, None, Whitespace, Punctuation), '#pop'),
+
+            # builtin tags (assign and include are handled together with usual tags)
+            (r'(cycle)(\s+)(([^\s:]*)(:))?(\s*)', bygroups(Name.Tag, Whitespace, None, using(this, state = 'generic'), Punctuation, Whitespace), 'variable-tag-markup'),
+
+            # other tags or blocks
+            (r'([^\s%]+)(\s*)', bygroups(Name.Tag, Whitespace), 'tag-markup')
+        ],
+
+        'output': [
+            include('whitespace'),
+            ('\}\}', Punctuation, '#pop'), # end of output
+
+            (r'\|', Punctuation, 'filters')
+        ],
+
+        'filters': [
+            include('whitespace'),
+            (r'\}\}', Punctuation, ('#pop', '#pop')), # end of filters and output
+
+            (r'([^\s\|:]+)(:?)(\s*)', bygroups(Name.Function, Punctuation, Whitespace), 'filter-markup')
+        ],
+
+        'filter-markup': [
+            (r'\|', Punctuation, '#pop'),
+            include('end-of-tag'),
+            include('default-param-markup')
+        ],
+
+        'condition': [
+            include('end-of-block'),
+            include('whitespace'),
+            
+            (r'([^\s=!><]+)(\s*)([=!><]=?)(\s*)([^\s]+)(\s*)(%\})', bygroups(using(this, state = 'generic'), Whitespace, Operator, Whitespace, using(this, state = 'generic'), Whitespace, Punctuation)),
+            (r'\b((!)|(not\b))', bygroups(None, Operator, Operator.Word)),
+            (r'([\w\.\'"]+)(\s+)(contains)(\s+)([\w\.\'"]+)', bygroups(using(this, state = 'generic'), Whitespace, Operator.Word, Whitespace, using(this, state = 'generic'))),
+
+            include('generic'),
+            include('whitespace')
+        ],
+
+        'generic-value': [
+            include('generic'),
+            include('end-at-whitespace')
+        ],
+
+        'operator': [
+            (r'(\s*)((=|!|>|<)=?)(\s*)', bygroups(Whitespace, Operator, None, Whitespace), '#pop'),
+            (r'(\s*)(\bcontains\b)(\s*)', bygroups(Whitespace, Operator.Word, Whitespace), '#pop'),
+        ],
+
+        'end-of-tag': [
+            (r'\}\}', Punctuation, '#pop')
+        ],
+
+        'end-of-block': [
+            (r'%\}', Punctuation, ('#pop', '#pop'))
+        ],
+
+        'end-at-whitespace': [
+            (r'\s+', Whitespace, '#pop')
+        ],
+
+        # states for unknown markup
+        'param-markup': [
+            include('whitespace'),
+            (r'([^\s=:]+)(\s*)(=|:)', bygroups(Name.Attribute, Whitespace, Operator)), # params with colons or equals
+            (r'(\{\{)(\s*)([^\s\}])(\s*)(\}\})', bygroups(Punctuation, Whitespace, using(this, state = 'variable'), Whitespace, Punctuation)), # explicit variables
+            include('string'),
+            include('number'),
+            include('keyword'),
+            (r',', Punctuation)
+        ],
+
+        'default-param-markup': [
+            include('param-markup'),
+            (r'.', Text) # fallback for switches / variables / un-quoted strings / ...
+        ],
+
+        'variable-param-markup': [
+            include('param-markup'),
+            include('variable'),
+            (r'.', Text) # fallback
+        ],
+
+        'tag-markup': [
+            (r'%\}', Punctuation, ('#pop', '#pop')), # end of tag
+            include('default-param-markup')
+        ],
+
+        'variable-tag-markup': [
+            (r'%\}', Punctuation, ('#pop', '#pop')), # end of tag
+            include('variable-param-markup')
+        ],
+
+        # states for different values types
+        'keyword': [
+            (r'\b(false|true)\b', Keyword.Constant)
+        ],
+
+        'variable': [
+            (r'[a-zA-Z_]\w*', Name.Variable),
+            (r'(?<=\w)\.(?=\w)', Punctuation)
+        ],
+
+        'string': [
+            (r"'[^']*'", String.Single),
+            (r'"[^"]*"', String.Double)
+        ],
+
+        'number': [
+            (r'\d+\.\d+', Number.Float),
+            (r'\d+', Number.Integer)
+        ],
+
+        'generic': [ # decides for variable, string, keyword or number
+            include('keyword'),
+            include('string'),
+            include('number'),
+            include('variable')
+        ],
+
+        'whitespace': [
+            (r'[ \t]+', Whitespace)
+        ],
+
+        # states for builtin blocks
+        'comment': [
+            (r'(\{%)(\s*)(endcomment)(\s*)(%\})', bygroups(Punctuation, Whitespace, Name.Tag, Whitespace, Punctuation), ('#pop', '#pop')),
+            (r'.', Comment)
+        ],
+
+        'raw': [
+            (r'[^\{]+', Text),
+            (r'(\{%)(\s*)(endraw)(\s*)(%\})', bygroups(Punctuation, Whitespace, Name.Tag, Whitespace, Punctuation), '#pop'),
+            (r'\{', Text)
+        ]
+    }

File tests/examplefiles/example.liquid

+# This is an example file. Process it with `./pygmentize -O full -f html -o /liquid-example.html example.liquid`.
+
+{% raw %}
+some {{raw}} liquid syntax
+
+{% raw %}
+{% endraw %}
+
+Just regular text - what happens?
+
+{% comment %}My lovely {{comment}} {% comment %}{% endcomment %}
+
+{% custom_tag params: true %}
+{% custom_block my="abc" c = false %}
+	Just usual {{liquid}}.
+{% endcustom_block %}
+
+{% another_tag "my string param" %}
+
+{{ variable | upcase }}
+{{ var.field | textilize | markdownify }}
+{{ var.field.property | textilize | markdownify }}
+{{ 'string' | truncate: 100 param='df"g' }}
+
+{% cycle '1', 2, var %}
+{% cycle 'group1': '1', var, 2 %}
+{% cycle group2: '1', var, 2 %}
+
+{% if a == 'B' %}
+{% elsif a == 'C%}' %}
+{% else %}
+{% endif %}
+
+{% unless not a %}
+{% else %}
+{% endunless %}
+
+{% case a %}
+{% when 'B' %}
+{% when 'C' %}
+{% else %}
+{% endcase %}