mirror of
https://github.com/microsoft/monaco-editor.git
synced 2025-12-22 20:52:56 +01:00
Initial Commit - Jinja support
This commit is contained in:
parent
e03e965994
commit
a1e38a4104
5 changed files with 752 additions and 0 deletions
25
src/basic-languages/jinja/jinja.contribution.ts
Normal file
25
src/basic-languages/jinja/jinja.contribution.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import { registerLanguage } from '../_.contribution';
|
||||||
|
|
||||||
|
declare var AMD: any;
|
||||||
|
declare var require: any;
|
||||||
|
|
||||||
|
registerLanguage({
|
||||||
|
id: 'jinja',
|
||||||
|
extensions: ['.jinja', '.j2'],
|
||||||
|
aliases: ['Jinja', 'Jinja2', 'jinja'],
|
||||||
|
mimetypes: ['text/jinja', 'text/x-jinja-template'],
|
||||||
|
loader: () => {
|
||||||
|
if (AMD) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
require(['vs/basic-languages/jinja/jinja'], resolve, reject);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return import('./jinja');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
434
src/basic-languages/jinja/jinja.test.ts
Normal file
434
src/basic-languages/jinja/jinja.test.ts
Normal file
|
|
@ -0,0 +1,434 @@
|
||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import { testTokenization } from '../test/testRunner';
|
||||||
|
|
||||||
|
testTokenization('jinja', [
|
||||||
|
// Comments
|
||||||
|
[
|
||||||
|
{
|
||||||
|
line: '{# This is a comment #}',
|
||||||
|
tokens: [{ startIndex: 0, type: 'comment.block.jinja' }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
line: 'Some text {#- comment -#} More text',
|
||||||
|
tokens: [
|
||||||
|
{ startIndex: 0, type: '' }, // Some text
|
||||||
|
{ startIndex: 10, type: 'comment.block.jinja' }, // {#- comment -#}
|
||||||
|
{ startIndex: 25, type: '' } // More text (Adjusted expectation)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
// Variables
|
||||||
|
[
|
||||||
|
{
|
||||||
|
line: '{{ variable_name }}',
|
||||||
|
tokens: [
|
||||||
|
{ startIndex: 0, type: 'delimiter.variable.jinja' }, // {{
|
||||||
|
{ startIndex: 2, type: 'white.jinja' },
|
||||||
|
{ startIndex: 3, type: 'variable.other.jinja' }, // variable_name
|
||||||
|
{ startIndex: 16, type: 'white.jinja' }, // Corrected index
|
||||||
|
{ startIndex: 17, type: 'delimiter.variable.jinja' } // Corrected index
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
line: '{{- variable | filter -}}',
|
||||||
|
tokens: [
|
||||||
|
{ startIndex: 0, type: 'delimiter.variable.jinja' }, // {{-
|
||||||
|
{ startIndex: 3, type: 'white.jinja' },
|
||||||
|
{ startIndex: 4, type: 'variable.other.jinja' }, // variable
|
||||||
|
{ startIndex: 12, type: 'white.jinja' },
|
||||||
|
{ startIndex: 13, type: 'operators.filter.jinja' }, // |
|
||||||
|
{ startIndex: 14, type: 'white.jinja' },
|
||||||
|
{ startIndex: 15, type: 'variable.other.filter.jinja' }, // filter
|
||||||
|
{ startIndex: 21, type: 'white.jinja' },
|
||||||
|
{ startIndex: 22, type: 'delimiter.variable.jinja' } // -}}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
// Blocks
|
||||||
|
[
|
||||||
|
{
|
||||||
|
line: '{% if condition %}',
|
||||||
|
tokens: [
|
||||||
|
{ startIndex: 0, type: 'delimiter.tag.jinja' }, // {%
|
||||||
|
{ startIndex: 2, type: 'white.jinja' },
|
||||||
|
{ startIndex: 3, type: 'keyword.control.jinja' }, // if
|
||||||
|
{ startIndex: 5, type: 'white.jinja' },
|
||||||
|
{ startIndex: 6, type: 'variable.other.jinja' }, // condition
|
||||||
|
{ startIndex: 15, type: 'white.jinja' },
|
||||||
|
{ startIndex: 16, type: 'delimiter.tag.jinja' } // %}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
line: '{% set my_var = "value" %}',
|
||||||
|
tokens: [
|
||||||
|
{ startIndex: 0, type: 'delimiter.tag.jinja' }, // {%
|
||||||
|
{ startIndex: 2, type: 'white.jinja' },
|
||||||
|
{ startIndex: 3, type: 'keyword.control.jinja' }, // set
|
||||||
|
{ startIndex: 6, type: 'white.jinja' },
|
||||||
|
{ startIndex: 7, type: 'variable.other.jinja' }, // my_var
|
||||||
|
{ startIndex: 13, type: 'white.jinja' },
|
||||||
|
{ startIndex: 14, type: 'keyword.operator.jinja' }, // =
|
||||||
|
{ startIndex: 15, type: 'white.jinja' },
|
||||||
|
{ startIndex: 16, type: 'string.quote.double.jinja' }, // "
|
||||||
|
{ startIndex: 17, type: 'string.jinja' }, // value
|
||||||
|
{ startIndex: 22, type: 'string.quote.double.jinja' }, // "
|
||||||
|
{ startIndex: 23, type: 'white.jinja' },
|
||||||
|
{ startIndex: 24, type: 'delimiter.tag.jinja' } // %}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
// Raw Block
|
||||||
|
[
|
||||||
|
{
|
||||||
|
line: '{% raw %}This {{ is not processed }} {% endraw %}',
|
||||||
|
tokens: [
|
||||||
|
// Actual tokens produced by the simpler tokenizer rules:
|
||||||
|
// Adjusted to match actual output reported by test runner
|
||||||
|
{ startIndex: 0, type: 'delimiter.tag.jinja' }, // {%
|
||||||
|
{ startIndex: 3, type: 'keyword.control.jinja' }, // raw
|
||||||
|
{ startIndex: 6, type: 'delimiter.tag.jinja' }, // %}
|
||||||
|
{ startIndex: 9, type: 'comment.block.raw.jinja' }, // This {{ is not processed }}
|
||||||
|
{ startIndex: 37, type: 'delimiter.tag.jinja' }, // {%
|
||||||
|
{ startIndex: 40, type: 'keyword.control.jinja' }, // endraw
|
||||||
|
{ startIndex: 46, type: 'delimiter.tag.jinja' } // %}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
// Strings and Numbers within expressions
|
||||||
|
[
|
||||||
|
{
|
||||||
|
line: "{{ 'string' + 123 }}",
|
||||||
|
tokens: [
|
||||||
|
{ startIndex: 0, type: 'delimiter.variable.jinja' }, // {{
|
||||||
|
{ startIndex: 2, type: 'white.jinja' },
|
||||||
|
{ startIndex: 3, type: 'string.quote.single.jinja' }, // '
|
||||||
|
{ startIndex: 4, type: 'string.jinja' }, // string
|
||||||
|
{ startIndex: 10, type: 'string.quote.single.jinja' }, // '
|
||||||
|
{ startIndex: 11, type: 'white.jinja' },
|
||||||
|
{ startIndex: 12, type: 'keyword.operator.jinja' }, // +
|
||||||
|
{ startIndex: 13, type: 'white.jinja' },
|
||||||
|
{ startIndex: 14, type: 'number.jinja' }, // 123
|
||||||
|
{ startIndex: 17, type: 'white.jinja' },
|
||||||
|
{ startIndex: 18, type: 'delimiter.variable.jinja' } // }}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
// For loop with loop variable and else
|
||||||
|
[
|
||||||
|
{
|
||||||
|
line: '{% for item in items %}{{ loop.index }}: {{ item }}{% else %}No items.{% endfor %}',
|
||||||
|
tokens: [
|
||||||
|
{ startIndex: 0, type: 'delimiter.tag.jinja' }, // {%
|
||||||
|
{ startIndex: 2, type: 'white.jinja' },
|
||||||
|
{ startIndex: 3, type: 'keyword.control.jinja' }, // for
|
||||||
|
{ startIndex: 6, type: 'white.jinja' },
|
||||||
|
{ startIndex: 7, type: 'variable.other.jinja' }, // item
|
||||||
|
{ startIndex: 11, type: 'white.jinja' },
|
||||||
|
{ startIndex: 12, type: 'keyword.control.jinja' }, // in
|
||||||
|
{ startIndex: 14, type: 'white.jinja' },
|
||||||
|
{ startIndex: 15, type: 'variable.other.jinja' }, // items
|
||||||
|
{ startIndex: 20, type: 'white.jinja' },
|
||||||
|
{ startIndex: 21, type: 'delimiter.tag.jinja' }, // %}
|
||||||
|
{ startIndex: 23, type: 'delimiter.variable.jinja' }, // {{
|
||||||
|
{ startIndex: 25, type: 'white.jinja' },
|
||||||
|
{ startIndex: 26, type: 'variable.language.jinja' }, // loop
|
||||||
|
{ startIndex: 30, type: 'delimiter.accessor.jinja' }, // .
|
||||||
|
{ startIndex: 31, type: 'variable.other.jinja' }, // index
|
||||||
|
{ startIndex: 36, type: 'white.jinja' },
|
||||||
|
{ startIndex: 37, type: 'delimiter.variable.jinja' }, // }}
|
||||||
|
{ startIndex: 39, type: '' }, // ': ' (colon and space are plain text)
|
||||||
|
{ startIndex: 41, type: 'delimiter.variable.jinja' }, // {{ (starts at index 41 now)
|
||||||
|
{ startIndex: 43, type: 'white.jinja' },
|
||||||
|
{ startIndex: 44, type: 'variable.other.jinja' }, // item
|
||||||
|
{ startIndex: 48, type: 'white.jinja' },
|
||||||
|
{ startIndex: 49, type: 'delimiter.variable.jinja' }, // }}
|
||||||
|
{ startIndex: 51, type: 'delimiter.tag.jinja' }, // {%
|
||||||
|
{ startIndex: 53, type: 'white.jinja' },
|
||||||
|
{ startIndex: 54, type: 'keyword.control.jinja' }, // else
|
||||||
|
{ startIndex: 58, type: 'white.jinja' },
|
||||||
|
{ startIndex: 59, type: 'delimiter.tag.jinja' }, // %}
|
||||||
|
{ startIndex: 61, type: '' }, // No items.
|
||||||
|
{ startIndex: 70, type: 'delimiter.tag.jinja' }, // {%
|
||||||
|
{ startIndex: 72, type: 'white.jinja' },
|
||||||
|
{ startIndex: 73, type: 'keyword.control.jinja' }, // endfor
|
||||||
|
{ startIndex: 79, type: 'white.jinja' },
|
||||||
|
{ startIndex: 80, type: 'delimiter.tag.jinja' } // %}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
// Complex Expressions: attr access, subscript, func call, comparison, logic, test
|
||||||
|
[
|
||||||
|
{
|
||||||
|
line: "{{ obj.attr + my_dict['key'] | func(1 > 0 and not False) is defined }}",
|
||||||
|
tokens: [
|
||||||
|
{ startIndex: 0, type: 'delimiter.variable.jinja' }, // {{
|
||||||
|
{ startIndex: 2, type: 'white.jinja' },
|
||||||
|
{ startIndex: 3, type: 'variable.other.jinja' }, // obj
|
||||||
|
{ startIndex: 6, type: 'delimiter.accessor.jinja' }, // .
|
||||||
|
{ startIndex: 7, type: 'variable.other.jinja' }, // attr
|
||||||
|
{ startIndex: 11, type: 'white.jinja' },
|
||||||
|
{ startIndex: 12, type: 'keyword.operator.jinja' }, // +
|
||||||
|
{ startIndex: 13, type: 'white.jinja' },
|
||||||
|
{ startIndex: 14, type: 'variable.other.jinja' }, // my_dict
|
||||||
|
{ startIndex: 21, type: 'delimiter.jinja' }, // [
|
||||||
|
{ startIndex: 22, type: 'string.quote.single.jinja' }, // '
|
||||||
|
{ startIndex: 23, type: 'string.jinja' }, // key
|
||||||
|
{ startIndex: 26, type: 'string.quote.single.jinja' }, // '
|
||||||
|
{ startIndex: 27, type: 'delimiter.jinja' }, // ]
|
||||||
|
{ startIndex: 28, type: 'white.jinja' },
|
||||||
|
{ startIndex: 29, type: 'operators.filter.jinja' }, // |
|
||||||
|
{ startIndex: 30, type: 'white.jinja' },
|
||||||
|
{ startIndex: 31, type: 'variable.other.filter.jinja' }, // func
|
||||||
|
{ startIndex: 35, type: 'delimiter.jinja' }, // (
|
||||||
|
{ startIndex: 36, type: 'number.jinja' }, // 1
|
||||||
|
{ startIndex: 37, type: 'white.jinja' },
|
||||||
|
{ startIndex: 38, type: 'keyword.operator.jinja' }, // >
|
||||||
|
{ startIndex: 39, type: 'white.jinja' },
|
||||||
|
{ startIndex: 40, type: 'number.jinja' }, // 0
|
||||||
|
{ startIndex: 41, type: 'white.jinja' },
|
||||||
|
{ startIndex: 42, type: 'keyword.control.jinja' }, // and
|
||||||
|
{ startIndex: 45, type: 'white.jinja' },
|
||||||
|
{ startIndex: 46, type: 'keyword.control.jinja' }, // not
|
||||||
|
{ startIndex: 49, type: 'white.jinja' },
|
||||||
|
{ startIndex: 50, type: 'constant.language.jinja' }, // False
|
||||||
|
{ startIndex: 55, type: 'delimiter.jinja' }, // )
|
||||||
|
{ startIndex: 56, type: 'white.jinja' },
|
||||||
|
{ startIndex: 57, type: 'keyword.control.jinja' }, // is
|
||||||
|
{ startIndex: 59, type: 'white.jinja' },
|
||||||
|
{ startIndex: 60, type: 'variable.other.jinja' }, // defined (common test treated as variable here, which is acceptable)
|
||||||
|
{ startIndex: 67, type: 'white.jinja' },
|
||||||
|
{ startIndex: 68, type: 'delimiter.variable.jinja' } // }}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
// Block and Extends
|
||||||
|
[
|
||||||
|
{
|
||||||
|
line: '{% extends "base.html" %}',
|
||||||
|
tokens: [
|
||||||
|
{ startIndex: 0, type: 'delimiter.tag.jinja' }, // {%
|
||||||
|
{ startIndex: 2, type: 'white.jinja' },
|
||||||
|
{ startIndex: 3, type: 'keyword.control.jinja' }, // extends
|
||||||
|
{ startIndex: 10, type: 'white.jinja' },
|
||||||
|
{ startIndex: 11, type: 'string.quote.double.jinja' }, // "
|
||||||
|
{ startIndex: 12, type: 'string.jinja' }, // base.html
|
||||||
|
{ startIndex: 21, type: 'string.quote.double.jinja' }, // "
|
||||||
|
{ startIndex: 22, type: 'white.jinja' },
|
||||||
|
{ startIndex: 23, type: 'delimiter.tag.jinja' } // %}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
line: '{% block content %} Content {% endblock %}',
|
||||||
|
tokens: [
|
||||||
|
{ startIndex: 0, type: 'delimiter.tag.jinja' }, // {%
|
||||||
|
{ startIndex: 2, type: 'white.jinja' },
|
||||||
|
{ startIndex: 3, type: 'keyword.control.jinja' }, // block
|
||||||
|
{ startIndex: 8, type: 'white.jinja' },
|
||||||
|
{ startIndex: 9, type: 'variable.other.jinja' }, // content
|
||||||
|
{ startIndex: 16, type: 'white.jinja' },
|
||||||
|
{ startIndex: 17, type: 'delimiter.tag.jinja' }, // %}
|
||||||
|
{ startIndex: 19, type: '' }, // Content
|
||||||
|
{ startIndex: 28, type: 'delimiter.tag.jinja' }, // {%
|
||||||
|
{ startIndex: 30, type: 'white.jinja' },
|
||||||
|
{ startIndex: 31, type: 'keyword.control.jinja' }, // endblock
|
||||||
|
{ startIndex: 39, type: 'white.jinja' },
|
||||||
|
{ startIndex: 40, type: 'delimiter.tag.jinja' } // %}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
// Macro definition and call
|
||||||
|
[
|
||||||
|
{
|
||||||
|
line: '{% macro input(name, value) %}<input name="{{ name }}">{% endmacro %}',
|
||||||
|
tokens: [
|
||||||
|
{ startIndex: 0, type: 'delimiter.tag.jinja' }, // {%
|
||||||
|
{ startIndex: 2, type: 'white.jinja' },
|
||||||
|
{ startIndex: 3, type: 'keyword.control.jinja' }, // macro
|
||||||
|
{ startIndex: 8, type: 'white.jinja' },
|
||||||
|
{ startIndex: 9, type: 'variable.other.jinja' }, // input
|
||||||
|
{ startIndex: 14, type: 'delimiter.jinja' }, // (
|
||||||
|
{ startIndex: 15, type: 'variable.other.jinja' }, // name
|
||||||
|
{ startIndex: 19, type: 'delimiter.jinja' }, // ,
|
||||||
|
{ startIndex: 20, type: 'white.jinja' },
|
||||||
|
{ startIndex: 21, type: 'variable.other.jinja' }, // value
|
||||||
|
{ startIndex: 26, type: 'delimiter.jinja' }, // )
|
||||||
|
{ startIndex: 27, type: 'white.jinja' },
|
||||||
|
{ startIndex: 28, type: 'delimiter.tag.jinja' }, // %}
|
||||||
|
{ startIndex: 30, type: '' }, // <input name="
|
||||||
|
{ startIndex: 43, type: 'delimiter.variable.jinja' }, // {{
|
||||||
|
{ startIndex: 45, type: 'white.jinja' },
|
||||||
|
{ startIndex: 46, type: 'variable.other.jinja' }, // name
|
||||||
|
{ startIndex: 50, type: 'white.jinja' },
|
||||||
|
{ startIndex: 51, type: 'delimiter.variable.jinja' }, // }}
|
||||||
|
{ startIndex: 53, type: '' }, // ">
|
||||||
|
{ startIndex: 55, type: 'delimiter.tag.jinja' }, // {%
|
||||||
|
{ startIndex: 57, type: 'white.jinja' },
|
||||||
|
{ startIndex: 58, type: 'keyword.control.jinja' }, // endmacro
|
||||||
|
{ startIndex: 66, type: 'white.jinja' },
|
||||||
|
{ startIndex: 67, type: 'delimiter.tag.jinja' } // %}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
line: '{{ mymacros.input("user") }}',
|
||||||
|
tokens: [
|
||||||
|
{ startIndex: 0, type: 'delimiter.variable.jinja' }, // {{
|
||||||
|
{ startIndex: 2, type: 'white.jinja' },
|
||||||
|
{ startIndex: 3, type: 'variable.other.jinja' }, // mymacros
|
||||||
|
{ startIndex: 11, type: 'delimiter.accessor.jinja' }, // .
|
||||||
|
{ startIndex: 12, type: 'variable.other.jinja' }, // input
|
||||||
|
{ startIndex: 17, type: 'delimiter.jinja' }, // (
|
||||||
|
{ startIndex: 18, type: 'string.quote.double.jinja' }, // "
|
||||||
|
{ startIndex: 19, type: 'string.jinja' }, // user
|
||||||
|
{ startIndex: 23, type: 'string.quote.double.jinja' }, // "
|
||||||
|
{ startIndex: 24, type: 'delimiter.jinja' }, // )
|
||||||
|
{ startIndex: 25, type: 'white.jinja' },
|
||||||
|
{ startIndex: 26, type: 'delimiter.variable.jinja' } // }}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
// String Escapes
|
||||||
|
[
|
||||||
|
{
|
||||||
|
// Test escapes on a single line
|
||||||
|
line: '{{ "World \\"Quote\\" \\\\ Backslash" }}',
|
||||||
|
tokens: [
|
||||||
|
{ startIndex: 0, type: 'delimiter.variable.jinja' }, // {{
|
||||||
|
{ startIndex: 2, type: 'white.jinja' },
|
||||||
|
{ startIndex: 3, type: 'string.quote.double.jinja' }, // "
|
||||||
|
{ startIndex: 4, type: 'string.jinja' }, // World
|
||||||
|
{ startIndex: 10, type: 'constant.character.escape.jinja' }, // \"
|
||||||
|
{ startIndex: 12, type: 'string.jinja' }, // Quote
|
||||||
|
{ startIndex: 17, type: 'constant.character.escape.jinja' }, // \"
|
||||||
|
{ startIndex: 19, type: 'string.jinja' }, //
|
||||||
|
{ startIndex: 20, type: 'constant.character.escape.jinja' }, // \\
|
||||||
|
{ startIndex: 22, type: 'string.jinja' }, // Backslash
|
||||||
|
{ startIndex: 32, type: 'string.quote.double.jinja' }, // "
|
||||||
|
{ startIndex: 33, type: 'white.jinja' },
|
||||||
|
{ startIndex: 34, type: 'delimiter.variable.jinja' } // }}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
[
|
||||||
|
{
|
||||||
|
line: '{{ True and false or None }}',
|
||||||
|
tokens: [
|
||||||
|
{ startIndex: 0, type: 'delimiter.variable.jinja' },
|
||||||
|
{ startIndex: 2, type: 'white.jinja' },
|
||||||
|
{ startIndex: 3, type: 'constant.language.jinja' },
|
||||||
|
{ startIndex: 7, type: 'white.jinja' },
|
||||||
|
{ startIndex: 8, type: 'keyword.control.jinja' },
|
||||||
|
{ startIndex: 11, type: 'white.jinja' },
|
||||||
|
{ startIndex: 12, type: 'constant.language.jinja' },
|
||||||
|
{ startIndex: 17, type: 'white.jinja' },
|
||||||
|
{ startIndex: 18, type: 'keyword.control.jinja' },
|
||||||
|
{ startIndex: 20, type: 'white.jinja' },
|
||||||
|
{ startIndex: 21, type: 'constant.language.jinja' },
|
||||||
|
{ startIndex: 25, type: 'white.jinja' },
|
||||||
|
{ startIndex: 26, type: 'delimiter.variable.jinja' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
// Filter block
|
||||||
|
[
|
||||||
|
{
|
||||||
|
line: '{% filter upper %}Text{% endfilter %}',
|
||||||
|
tokens: [
|
||||||
|
{ startIndex: 0, type: 'delimiter.tag.jinja' }, // {%
|
||||||
|
{ startIndex: 2, type: 'white.jinja' },
|
||||||
|
{ startIndex: 3, type: 'keyword.control.jinja' }, // filter
|
||||||
|
{ startIndex: 9, type: 'white.jinja' },
|
||||||
|
{ startIndex: 10, type: 'variable.other.jinja' }, // upper (filter name treated as variable here)
|
||||||
|
{ startIndex: 15, type: 'white.jinja' },
|
||||||
|
{ startIndex: 16, type: 'delimiter.tag.jinja' }, // %}
|
||||||
|
{ startIndex: 18, type: '' }, // Text
|
||||||
|
{ startIndex: 22, type: 'delimiter.tag.jinja' }, // {%
|
||||||
|
{ startIndex: 24, type: 'white.jinja' },
|
||||||
|
{ startIndex: 25, type: 'keyword.control.jinja' }, // endfilter
|
||||||
|
{ startIndex: 34, type: 'white.jinja' },
|
||||||
|
{ startIndex: 35, type: 'delimiter.tag.jinja' } // %}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
// Include and Import
|
||||||
|
[
|
||||||
|
{
|
||||||
|
line: '{% include "partial.html" %}',
|
||||||
|
tokens: [
|
||||||
|
{ startIndex: 0, type: 'delimiter.tag.jinja' }, // {%
|
||||||
|
{ startIndex: 2, type: 'white.jinja' },
|
||||||
|
{ startIndex: 3, type: 'keyword.control.jinja' }, // include
|
||||||
|
{ startIndex: 10, type: 'white.jinja' },
|
||||||
|
{ startIndex: 11, type: 'string.quote.double.jinja' }, // "
|
||||||
|
{ startIndex: 12, type: 'string.jinja' }, // partial.html
|
||||||
|
{ startIndex: 24, type: 'string.quote.double.jinja' }, // "
|
||||||
|
{ startIndex: 25, type: 'white.jinja' },
|
||||||
|
{ startIndex: 26, type: 'delimiter.tag.jinja' } // %}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
line: '{% import "macros.jinja" as forms %}',
|
||||||
|
tokens: [
|
||||||
|
{ startIndex: 0, type: 'delimiter.tag.jinja' }, // {%
|
||||||
|
{ startIndex: 2, type: 'white.jinja' },
|
||||||
|
{ startIndex: 3, type: 'keyword.control.jinja' }, // import
|
||||||
|
{ startIndex: 9, type: 'white.jinja' },
|
||||||
|
{ startIndex: 10, type: 'string.quote.double.jinja' }, // "
|
||||||
|
{ startIndex: 11, type: 'string.jinja' }, // macros.jinja
|
||||||
|
{ startIndex: 23, type: 'string.quote.double.jinja' }, // "
|
||||||
|
{ startIndex: 24, type: 'white.jinja' },
|
||||||
|
{ startIndex: 25, type: 'keyword.control.jinja' }, // as
|
||||||
|
{ startIndex: 27, type: 'white.jinja' },
|
||||||
|
{ startIndex: 28, type: 'variable.other.jinja' }, // forms
|
||||||
|
{ startIndex: 33, type: 'white.jinja' },
|
||||||
|
{ startIndex: 34, type: 'delimiter.tag.jinja' } // %}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
// With block
|
||||||
|
[
|
||||||
|
{
|
||||||
|
line: '{% with var = 42 %}{{ var }}{% endwith %}',
|
||||||
|
tokens: [
|
||||||
|
{ startIndex: 0, type: 'delimiter.tag.jinja' }, // {%
|
||||||
|
{ startIndex: 2, type: 'white.jinja' },
|
||||||
|
{ startIndex: 3, type: 'keyword.control.jinja' }, // with
|
||||||
|
{ startIndex: 7, type: 'white.jinja' },
|
||||||
|
{ startIndex: 8, type: 'variable.other.jinja' }, // var
|
||||||
|
{ startIndex: 11, type: 'white.jinja' },
|
||||||
|
{ startIndex: 12, type: 'keyword.operator.jinja' }, // =
|
||||||
|
{ startIndex: 13, type: 'white.jinja' },
|
||||||
|
{ startIndex: 14, type: 'number.jinja' }, // 42
|
||||||
|
{ startIndex: 16, type: 'white.jinja' },
|
||||||
|
{ startIndex: 17, type: 'delimiter.tag.jinja' }, // %}
|
||||||
|
{ startIndex: 19, type: 'delimiter.variable.jinja' }, // {{
|
||||||
|
{ startIndex: 21, type: 'white.jinja' },
|
||||||
|
{ startIndex: 22, type: 'variable.other.jinja' }, // var
|
||||||
|
{ startIndex: 25, type: 'white.jinja' },
|
||||||
|
{ startIndex: 26, type: 'delimiter.variable.jinja' }, // }}
|
||||||
|
{ startIndex: 28, type: 'delimiter.tag.jinja' }, // {%
|
||||||
|
{ startIndex: 30, type: 'white.jinja' },
|
||||||
|
{ startIndex: 31, type: 'keyword.control.jinja' }, // endwith
|
||||||
|
{ startIndex: 38, type: 'white.jinja' },
|
||||||
|
{ startIndex: 39, type: 'delimiter.tag.jinja' } // %}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]);
|
||||||
269
src/basic-languages/jinja/jinja.ts
Normal file
269
src/basic-languages/jinja/jinja.ts
Normal file
|
|
@ -0,0 +1,269 @@
|
||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import { languages } from '../../fillers/monaco-editor-core';
|
||||||
|
|
||||||
|
// Language Configuration for Jinja
|
||||||
|
export const conf: languages.LanguageConfiguration = {
|
||||||
|
comments: {
|
||||||
|
blockComment: ['{#', '#}']
|
||||||
|
},
|
||||||
|
brackets: [
|
||||||
|
['{%', '%}'],
|
||||||
|
['{{', '}}'],
|
||||||
|
['{#', '#}'],
|
||||||
|
['(', ')'],
|
||||||
|
['[', ']'],
|
||||||
|
['{', '}']
|
||||||
|
// Note: Whitespace control variants like {%-, -%} are part of the token, not separate brackets
|
||||||
|
],
|
||||||
|
autoClosingPairs: [
|
||||||
|
{ open: '{#', close: ' #}' },
|
||||||
|
{ open: '{%', close: ' %}' },
|
||||||
|
{ open: '{{', close: ' }}' },
|
||||||
|
{ open: '[', close: ']' },
|
||||||
|
{ open: '(', close: ')' },
|
||||||
|
{ open: '{', close: '}' },
|
||||||
|
{ open: '"', close: '"', notIn: ['string', 'comment'] },
|
||||||
|
{ open: "'", close: "'", notIn: ['string', 'comment'] }
|
||||||
|
// Whitespace control pairs might be tricky here, stick to standard for reliable auto-closing
|
||||||
|
],
|
||||||
|
surroundingPairs: [
|
||||||
|
{ open: '"', close: '"' },
|
||||||
|
{ open: "'", close: "'" },
|
||||||
|
{ open: '(', close: ')' },
|
||||||
|
{ open: '[', close: ']' },
|
||||||
|
{ open: '{', close: '}' },
|
||||||
|
{ open: '{%', close: '%}' },
|
||||||
|
{ open: '{{', close: '}}' },
|
||||||
|
{ open: '{#', close: '#}' }
|
||||||
|
],
|
||||||
|
// Add folding markers based on TextMate grammar
|
||||||
|
folding: {
|
||||||
|
markers: {
|
||||||
|
start: new RegExp('^\\s*({%\\s*(block|filter|for|if|macro|raw))'), // Matches start tags
|
||||||
|
end: new RegExp('^\\s*({%\\s*(endblock|endfilter|endfor|endif|endmacro|endraw)\\s*%})') // Matches end tags
|
||||||
|
}
|
||||||
|
},
|
||||||
|
indentationRules: {
|
||||||
|
increaseIndentPattern: new RegExp(
|
||||||
|
'^\\s*({%\\s*(block|filter|for|if|macro|raw|with|autoescape)\\b(?!.*\\b(endblock|endfilter|endfor|endif|endmacro|endraw|endwith|endautoescape))[^%]*%})'
|
||||||
|
),
|
||||||
|
decreaseIndentPattern: new RegExp(
|
||||||
|
'^\\s*({%\\s*(elif|else|endblock|endfilter|endfor|endif|endmacro|endraw|endwith|endautoescape)\\b.*?%})'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Monarch Tokenizer Definition for Jinja
|
||||||
|
export const language = <languages.IMonarchLanguage>{
|
||||||
|
defaultToken: '', // Default to no specific token, avoid 'invalid' spam
|
||||||
|
tokenPostfix: '.jinja',
|
||||||
|
|
||||||
|
keywords: [
|
||||||
|
// Control Structures
|
||||||
|
'if',
|
||||||
|
'endif',
|
||||||
|
'for',
|
||||||
|
'endfor',
|
||||||
|
'block',
|
||||||
|
'endblock',
|
||||||
|
'extends',
|
||||||
|
'include',
|
||||||
|
'import',
|
||||||
|
'from',
|
||||||
|
'as',
|
||||||
|
'recursive',
|
||||||
|
'macro',
|
||||||
|
'endmacro',
|
||||||
|
'call',
|
||||||
|
'endcall',
|
||||||
|
'filter',
|
||||||
|
'endfilter',
|
||||||
|
'set',
|
||||||
|
'endset',
|
||||||
|
'raw',
|
||||||
|
'endraw',
|
||||||
|
'with',
|
||||||
|
'endwith',
|
||||||
|
'autoescape',
|
||||||
|
'endautoescape',
|
||||||
|
// Jinja specific keywords often used within tags
|
||||||
|
'scoped',
|
||||||
|
'required',
|
||||||
|
'ignore',
|
||||||
|
'missing',
|
||||||
|
'context', // Modifiers for include/import/block
|
||||||
|
'trimmed',
|
||||||
|
'notrimmed',
|
||||||
|
'pluralize', // i18n extension
|
||||||
|
'continue',
|
||||||
|
'break', // loop controls extension
|
||||||
|
'do', // do extension
|
||||||
|
// Expressions/Logic
|
||||||
|
'and',
|
||||||
|
'or',
|
||||||
|
'not',
|
||||||
|
'in',
|
||||||
|
'is',
|
||||||
|
'else',
|
||||||
|
'elif'
|
||||||
|
// Note: true, false, none, loop, super, self, varargs, kwargs are handled in tokenizer
|
||||||
|
],
|
||||||
|
|
||||||
|
operators: [
|
||||||
|
'+',
|
||||||
|
'-',
|
||||||
|
'*',
|
||||||
|
'**',
|
||||||
|
'/',
|
||||||
|
'//',
|
||||||
|
'%', // Arithmetic
|
||||||
|
'==',
|
||||||
|
'<=',
|
||||||
|
'>=',
|
||||||
|
'<',
|
||||||
|
'>',
|
||||||
|
'!=', // Comparison
|
||||||
|
'=', // Assignment
|
||||||
|
'|', // Filter pipe
|
||||||
|
'~' // Concatenation
|
||||||
|
// 'and', 'or', 'not', 'in', 'is' are keywords but act as operators
|
||||||
|
],
|
||||||
|
|
||||||
|
// Symbols used for operators, delimiters etc. - simplified as specific tokens are better
|
||||||
|
symbols: /[=><!~?&|+\-*/^%]+/,
|
||||||
|
|
||||||
|
// Common Jinja constants and special variables
|
||||||
|
constants: ['true', 'false', 'none', 'True', 'False', 'None'], // Allow title case for compatibility
|
||||||
|
specialVars: ['loop', 'super', 'self', 'varargs', 'kwargs', 'caller'], // Added caller
|
||||||
|
|
||||||
|
// Modified escapes regex (removed \\, \", \')
|
||||||
|
escapes: /\\(?:[abfnrtv]|x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8}|N\{[a-zA-Z ]+\})/,
|
||||||
|
|
||||||
|
tokenizer: {
|
||||||
|
root: [
|
||||||
|
// Match Jinja delimiters first
|
||||||
|
// Comments: {# ... #}
|
||||||
|
[/\{#-+/, { token: 'comment.block', bracket: '@open', next: '@comment' }], // With whitespace control
|
||||||
|
[/\{#/, { token: 'comment.block', bracket: '@open', next: '@comment' }], // Standard
|
||||||
|
|
||||||
|
// Variables: {{ ... }}
|
||||||
|
[/\{\{-+/, { token: 'delimiter.variable', bracket: '@open', next: '@variable' }], // With whitespace control
|
||||||
|
[/\{\{/, { token: 'delimiter.variable', bracket: '@open', next: '@variable' }], // Standard
|
||||||
|
|
||||||
|
// Blocks: {% ... %}
|
||||||
|
// Raw block needs to be matched first to prevent inner content parsing
|
||||||
|
[
|
||||||
|
/(\{%\s*)(raw)(\s*%\})/,
|
||||||
|
['delimiter.tag', 'keyword.control', { token: 'delimiter.tag', next: '@rawblock' }]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
/(\{%-?\s*)(raw)(\s*-?%\})/,
|
||||||
|
['delimiter.tag', 'keyword.control', { token: 'delimiter.tag', next: '@rawblock' }]
|
||||||
|
], // With whitespace control
|
||||||
|
|
||||||
|
[/\{%-+/, { token: 'delimiter.tag', bracket: '@open', next: '@block' }], // With whitespace control
|
||||||
|
[/\{%/, { token: 'delimiter.tag', bracket: '@open', next: '@block' }], // Standard
|
||||||
|
|
||||||
|
// Anything else is treated as plain text/HTML until a delimiter is found
|
||||||
|
[/[^\{]+/, ''],
|
||||||
|
[/\{/, ''] // Match stray opening braces that aren't part of a delimiter
|
||||||
|
],
|
||||||
|
|
||||||
|
comment: [
|
||||||
|
[/-?#\}/, { token: 'comment.block', bracket: '@close', next: '@pop' }], // Match closing delimiter with optional whitespace control
|
||||||
|
[/[^#\}]+/, 'comment.block'], // Content inside comment
|
||||||
|
[/#|\}/, 'comment.block'] // Consume '#' or '}' within comment (e.g., nested comments - though Jinja doesn't support true nesting)
|
||||||
|
],
|
||||||
|
|
||||||
|
variable: [
|
||||||
|
[/-?\}\}/, { token: 'delimiter.variable', bracket: '@close', next: '@pop' }], // Match closing delimiter with optional whitespace control
|
||||||
|
{ include: '@expressionInside' }
|
||||||
|
],
|
||||||
|
|
||||||
|
block: [
|
||||||
|
[/-?%\}/, { token: 'delimiter.tag', bracket: '@close', next: '@pop' }], // Match closing delimiter with optional whitespace control
|
||||||
|
{ include: '@expressionInside' }
|
||||||
|
],
|
||||||
|
|
||||||
|
// Simplified raw block handling: consumes everything until endraw
|
||||||
|
rawblock: [
|
||||||
|
[
|
||||||
|
/(\{%-?\s*)(endraw)(\s*-?%\})/,
|
||||||
|
['delimiter.tag', 'keyword.control', { token: 'delimiter.tag', next: '@pop' }]
|
||||||
|
],
|
||||||
|
[/[^{%]+/, 'comment.block.raw'], // Any character not part of start delimiter
|
||||||
|
[/\{%?/, 'comment.block.raw'] // Consume parts of delimiters if not endraw
|
||||||
|
],
|
||||||
|
|
||||||
|
expressionInside: [
|
||||||
|
// Match keywords, constants, and special variables
|
||||||
|
[
|
||||||
|
/\b[a-zA-Z_]\w*\b/,
|
||||||
|
{
|
||||||
|
cases: {
|
||||||
|
'@keywords': 'keyword.control',
|
||||||
|
'@constants': 'constant.language',
|
||||||
|
'@specialVars': 'variable.language',
|
||||||
|
'@default': 'variable.other'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
// Numbers (allow underscore separators like in TextMate, though less common in Jinja)
|
||||||
|
[/\d+(_\d+)*(\.\d+)?([eE][+\-]?\d+)?/, 'number'],
|
||||||
|
|
||||||
|
// Strings
|
||||||
|
[/"/, { token: 'string.quote.double', bracket: '@open', next: '@string_double' }], // Start double-quoted string
|
||||||
|
[/'/, { token: 'string.quote.single', bracket: '@open', next: '@string_single' }], // Start single-quoted string
|
||||||
|
|
||||||
|
// Operators and Symbols
|
||||||
|
// Specific rule for filter pipe - pushes to a state to identify the filter name
|
||||||
|
[/\|(?=\s*[a-zA-Z_])/, { token: 'operators.filter', next: '@filterName' }],
|
||||||
|
[
|
||||||
|
/@symbols/,
|
||||||
|
{
|
||||||
|
cases: {
|
||||||
|
'@operators': 'keyword.operator',
|
||||||
|
'@default': 'delimiter' // Treat other symbols as delimiters (e.g., ~)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
// Delimiters: . : , ( ) [ ] { } (pipe handled separately)
|
||||||
|
[/\./, 'delimiter.accessor'], // Dot for attribute access
|
||||||
|
[/[?:,()\[\]{}]/, 'delimiter'], // Other delimiters
|
||||||
|
|
||||||
|
// Whitespace
|
||||||
|
[/\s+/, 'white']
|
||||||
|
],
|
||||||
|
|
||||||
|
string_double: [
|
||||||
|
[/\\\\/, 'constant.character.escape'], // 1. Explicit \\
|
||||||
|
[/\\"/, 'constant.character.escape'], // 2. Explicit \"
|
||||||
|
[/@escapes/, 'constant.character.escape'], // 3. Other known escapes (modified regex)
|
||||||
|
[/\\./, 'string.escape.invalid'], // 4. Invalid escapes
|
||||||
|
[/[^\\"]+/, 'string'], // 5. Regular string content
|
||||||
|
[/"/, { token: 'string.quote.double', bracket: '@close', next: '@pop' }] // 6. Closing quote
|
||||||
|
],
|
||||||
|
|
||||||
|
string_single: [
|
||||||
|
[/\\\\/, 'constant.character.escape'], // 1. Explicit \\
|
||||||
|
[/\\'/, 'constant.character.escape'], // 2. Explicit \'
|
||||||
|
[/@escapes/, 'constant.character.escape'], // 3. Other known escapes (modified regex)
|
||||||
|
[/\\./, 'string.escape.invalid'], // 4. Invalid escapes
|
||||||
|
[/[^\\']+/, 'string'], // 5. Regular string content
|
||||||
|
[/'/, { token: 'string.quote.single', bracket: '@close', next: '@pop' }] // 6. Closing quote
|
||||||
|
],
|
||||||
|
|
||||||
|
// State to capture the filter name after a pipe
|
||||||
|
filterName: [
|
||||||
|
[/\s+/, 'white'], // Eat whitespace before the name
|
||||||
|
[/[a-zA-Z_]\w*/, { token: 'variable.other.filter', next: '@pop' }], // Filter name itself
|
||||||
|
['', { token: '', next: '@pop' }] // If anything else follows pipe (like another pipe or delimiter), just pop
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -31,6 +31,7 @@ import './html/html.contribution';
|
||||||
import './ini/ini.contribution';
|
import './ini/ini.contribution';
|
||||||
import './java/java.contribution';
|
import './java/java.contribution';
|
||||||
import './javascript/javascript.contribution';
|
import './javascript/javascript.contribution';
|
||||||
|
import './jinja/jinja.contribution';
|
||||||
import './julia/julia.contribution';
|
import './julia/julia.contribution';
|
||||||
import './kotlin/kotlin.contribution';
|
import './kotlin/kotlin.contribution';
|
||||||
import './less/less.contribution';
|
import './less/less.contribution';
|
||||||
|
|
|
||||||
23
website/index/samples/sample.jinja.txt
Normal file
23
website/index/samples/sample.jinja.txt
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>{{ page_title|default("Default Title") }}</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Hello, {{ user_name }}!</h1>
|
||||||
|
|
||||||
|
{% if items %}
|
||||||
|
<h2>Items:</h2>
|
||||||
|
<ul>
|
||||||
|
{% for item in items %}
|
||||||
|
<li>{{ item }}</li>
|
||||||
|
{% else %}
|
||||||
|
<li>No items found.</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{# This is a comment #}
|
||||||
|
<p>Current year: {{ current_year }}</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue