mirror of
https://github.com/microsoft/monaco-editor.git
synced 2025-12-22 11:35:40 +01:00
Merge a1e38a4104 into ec78a33c7b
This commit is contained in:
commit
d60ab7c791
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 './java/java.contribution';
|
||||
import './javascript/javascript.contribution';
|
||||
import './jinja/jinja.contribution';
|
||||
import './julia/julia.contribution';
|
||||
import './kotlin/kotlin.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