From 408ad698508515db73382425fb896ffa625d3faf Mon Sep 17 00:00:00 2001 From: Matt Vague Date: Sun, 4 Apr 2021 15:14:02 -0700 Subject: [PATCH] First pass at implementing liquid language support --- src/liquid/liquid.contribution.ts | 12 +++ src/liquid/liquid.test.ts | 115 +++++++++++++++++++++++ src/liquid/liquid.ts | 148 ++++++++++++++++++++++++++++++ src/monaco.contribution.ts | 1 + 4 files changed, 276 insertions(+) create mode 100644 src/liquid/liquid.contribution.ts create mode 100644 src/liquid/liquid.test.ts create mode 100644 src/liquid/liquid.ts diff --git a/src/liquid/liquid.contribution.ts b/src/liquid/liquid.contribution.ts new file mode 100644 index 00000000..feb7154b --- /dev/null +++ b/src/liquid/liquid.contribution.ts @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * 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'; + +registerLanguage({ + id: 'liquid', + extensions: ['.liquid', '.liquid.html', '.liquid.css'], + loader: () => import('./liquid') +}); diff --git a/src/liquid/liquid.test.ts b/src/liquid/liquid.test.ts new file mode 100644 index 00000000..a24f567d --- /dev/null +++ b/src/liquid/liquid.test.ts @@ -0,0 +1,115 @@ +/*--------------------------------------------------------------------------------------------- + * 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( + ['liquid', 'css'], + [ + // Just HTML + [ + { + line: '

liquid!

', + tokens: [ + { startIndex: 0, type: 'delimiter.html' }, + { startIndex: 1, type: 'tag.html' }, + { startIndex: 3, type: 'delimiter.html' }, + { startIndex: 4, type: '' }, + { startIndex: 11, type: 'delimiter.html' }, + { startIndex: 13, type: 'tag.html' }, + { startIndex: 15, type: 'delimiter.html' } + ] + } + ], + + // Simple output + [ + { + line: '

{{ title }}

', + tokens: [ + { startIndex: 0, type: 'delimiter.html' }, + { startIndex: 1, type: 'tag.html' }, + { startIndex: 3, type: 'delimiter.html' }, + { startIndex: 4, type: 'delimiter.liquid' }, + { startIndex: 6, type: '' }, + { startIndex: 7, type: 'variable.liquid' }, + { startIndex: 12, type: '' }, + { startIndex: 13, type: 'delimiter.liquid' }, + { startIndex: 15, type: 'delimiter.html' }, + { startIndex: 17, type: 'tag.html' }, + { startIndex: 19, type: 'delimiter.html' } + ] + } + ], + + // // Output filter + [ + { + line: '

{{ 3.14159265 | round | default: "pi" }}

', + tokens: [ + { startIndex: 0, type: 'delimiter.html' }, + { startIndex: 1, type: 'tag.html' }, + { startIndex: 3, type: 'delimiter.html' }, + { startIndex: 4, type: 'delimiter.liquid' }, + { startIndex: 6, type: '' }, + { startIndex: 7, type: 'number.liquid' }, + { startIndex: 17, type: '' }, + { startIndex: 20, type: 'variable.liquid' }, + { startIndex: 25, type: '' }, + { startIndex: 28, type: 'variable.liquid' }, + { startIndex: 36, type: '' }, + { startIndex: 37, type: 'string.liquid' }, + { startIndex: 41, type: '' }, + { startIndex: 43, type: 'delimiter.liquid' }, + { startIndex: 45, type: 'delimiter.html' }, + { startIndex: 47, type: 'tag.html' }, + { startIndex: 49, type: 'delimiter.html' } + ] + } + ], + + // Tag + [ + { + line: '
{% render "files/file123.html" %}
', + tokens: [ + { startIndex: 0, type: 'delimiter.html' }, + { startIndex: 1, type: 'tag.html' }, + { startIndex: 4, type: 'delimiter.html' }, + { startIndex: 5, type: 'delimiter.output.liquid' }, + { startIndex: 7, type: '' }, + { startIndex: 8, type: 'variable.liquid' }, + { startIndex: 14, type: '' }, + { startIndex: 15, type: 'string.liquid' }, + { startIndex: 35, type: '' }, + { startIndex: 36, type: 'delimiter.liquid' }, + { startIndex: 38, type: 'delimiter.html' }, + { startIndex: 40, type: 'tag.html' }, + { startIndex: 43, type: 'delimiter.html' } + ] + } + ], + + // Handlebars comment + [ + { + line: '
Anything you put between {% comment %} and {% endcomment %} tags
', + tokens: [ + { startIndex: 0, type: 'delimiter.html' }, + { startIndex: 1, type: 'tag.html' }, + { startIndex: 4, type: 'delimiter.html' }, + { startIndex: 5, type: '' }, + { startIndex: 30, type: 'comment.start.liquid' }, + { startIndex: 43, type: 'comment.content.liquid' }, + { startIndex: 48, type: 'comment.end.liquid' }, + { startIndex: 64, type: '' }, + { startIndex: 69, type: 'delimiter.html' }, + { startIndex: 71, type: 'tag.html' }, + { startIndex: 74, type: 'delimiter.html' } + ] + } + ] + ] +); diff --git a/src/liquid/liquid.ts b/src/liquid/liquid.ts new file mode 100644 index 00000000..cccb5033 --- /dev/null +++ b/src/liquid/liquid.ts @@ -0,0 +1,148 @@ +/*--------------------------------------------------------------------------------------------- + * 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'; + +const EMPTY_ELEMENTS: string[] = [ + 'area', + 'base', + 'br', + 'col', + 'embed', + 'hr', + 'img', + 'input', + 'keygen', + 'link', + 'menuitem', + 'meta', + 'param', + 'source', + 'track', + 'wbr' +]; + +export const conf: languages.LanguageConfiguration = { + wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\$\^\&\*\(\)\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\s]+)/g, + + // comments: { + // blockComment: ['{{!--', '--}}'] + // }, + + brackets: [ + [''], + ['<', '>'], + ['{{', '}}'], + ['{%', '%}'], + ['{', '}'], + ['(', ')'] + ], + + autoClosingPairs: [ + { open: '{', close: '}' }, + { open: '%', close: '%' }, + { open: '[', close: ']' }, + { open: '(', close: ')' }, + { open: '"', close: '"' }, + { open: "'", close: "'" } + ], + + surroundingPairs: [ + { open: '<', close: '>' }, + { open: '"', close: '"' }, + { open: "'", close: "'" } + ], + + onEnterRules: [ + { + beforeText: new RegExp( + `<(?!(?:${EMPTY_ELEMENTS.join('|')}))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$`, + 'i' + ), + afterText: /^<\/(\w[\w\d]*)\s*>$/i, + action: { + indentAction: languages.IndentAction.IndentOutdent + } + }, + { + beforeText: new RegExp( + `<(?!(?:${EMPTY_ELEMENTS.join('|')}))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$`, + 'i' + ), + action: { indentAction: languages.IndentAction.Indent } + } + ] +}; + +export const language = { + defaultToken: '', + tokenPostfix: '', + // ignoreCase: true, + + // The main tokenizer for our languages + tokenizer: { + root: [ + [/\{\%\s*comment\s*\%\}/, 'comment.start.liquid', '@comment'], + [/\{\{/, { token: '@rematch', switchTo: '@liquidInSimpleState.root' }], + [/\{\%/, { token: '@rematch', switchTo: '@liquidInSimpleState.root' }], + [/(<)(\w+)(\/>)/, ['delimiter.html', 'tag.html', 'delimiter.html']], + [/(<)([:\w]+)/, ['delimiter.html', { token: 'tag.html', next: '@otherTag' }]], + [/(<\/)(\w+)/, ['delimiter.html', { token: 'tag.html', next: '@otherTag' }]], + [//, 'delimiter.html', '@pop'], + [/"([^"]*)"/, 'attribute.value'], + [/'([^']*)'/, 'attribute.value'], + [/[\w\-]+/, 'attribute.name'], + [/=/, 'delimiter'], + [/[ \t\r\n]+/] // whitespace + ], + + liquidInSimpleState: [ + [/\{\{/, 'delimiter.liquid'], + [/\}\}/, { token: 'delimiter.liquid', switchTo: '@$S2.$S3' }], + [/\{\%/, 'delimiter.output.liquid'], + [/\%\}/, { token: 'delimiter.liquid', switchTo: '@$S2.$S3' }], + { include: 'liquidRoot' } + ], + + liquidInTagState: [ + [/%\}/, { token: 'delimiter.output.liquid', switchTo: '@$S2.$S3' }], + // { include: 'liquidRoot' }, + [/[^%]/, 'wut'] + ], + + liquidRoot: [ + [/\d+(\.\d+)?/, 'number.liquid'], + [/"[^"]*"/, 'string.liquid'], + [/'[^']*'/, 'string.liquid'], + [/[\s]+/], + [/[^}|%]/, 'variable.liquid'] + ] + } +}; diff --git a/src/monaco.contribution.ts b/src/monaco.contribution.ts index cf90f679..0ab45b34 100644 --- a/src/monaco.contribution.ts +++ b/src/monaco.contribution.ts @@ -31,6 +31,7 @@ import './kotlin/kotlin.contribution'; import './less/less.contribution'; import './lexon/lexon.contribution'; import './lua/lua.contribution'; +import './liquid/liquid.contribution'; import './m3/m3.contribution'; import './markdown/markdown.contribution'; import './mips/mips.contribution';