Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Update handlebars syntax for Glimmer and Ember syntaxes #3099

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,8 @@
"terser": "^5.3.8",
"tiny-worker": "^2.3.0",
"typescript": "^4.0.5"
},
"volta": {
"node": "14.16.0"
}
}
209 changes: 151 additions & 58 deletions src/languages/handlebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,57 +3,108 @@ Language: Handlebars
Requires: xml.js
Author: Robin Ward <robin.ward@gmail.com>
Description: Matcher for Handlebars as well as EmberJS additions.
Website: https://handlebarsjs.com
Website: https://handlebarsjs.com, https://emberjs.com
Category: template
*/

import * as regex from '../lib/regex.js';


export default function(hljs) {
const BUILT_INS = {
$pattern: /[\w.\/]+/,
'builtin-name': [
'action',
'bindattr',
'collection',
'component',
'concat',
'debugger',
'each',
'each-in',
'get',
'hash',
'if',
'in',
'input',
'link-to',
'loc',
'log',
'lookup',
'mut',
'outlet',
'partial',
'query-params',
'render',
'template',
'textarea',
'unbound',
'unless',
'view',
'with',
'yield'
]
};
// Merged: https://github.com/emberjs/rfcs/pull/560
const EQUALITY_HELPERS = [
'eq',
'neq'
];
// Merged: https://github.com/emberjs/rfcs/pull/561
const NUMERIC_COMPARISON_HELPERS = [
'gt',
'gte',
'le',
'lte'
];
// Merged: https://github.com/emberjs/rfcs/pull/562/files
const LOGICAL_OPERATOR_HELPERS = [
'and',
'or',
'not'
];

const LITERALS = {
$pattern: /[\w.\/]+/,
literal: [
'true',
'false',
'undefined',
'null'
]
};
const OTHER_OPERATORS = [
// ember-truth-helpers
'not-eq',
'xor',
'is-array',
'is-object',
'is-equal'
];
const BLOCK_HELPERS = [
'let',
'each',
'each-in',
'if',
'unless'
];
const INLINE_HELPERS = [
'log',
'debugger',
'has-block',
'concat',
'fn',
'component',
'get',
'hash',
'query-params'
];

const MODIFIERS = [
'action',
'on'
];

const LEGACY = [
'input',
'link-to',
'mut',
'bindattr',
'collection',
'in',
'loc',
'lookup',
'partial',
'render',
'template',
'textarea',
'unbound',
'view',
'with',
'yield'
];

const SPECIAL = [
'outlet',
'yield',
'as'
];

const LITERALS = [
'true',
'false',
'undefined',
'null'
];

const KEYWORD_REGEX = /[\w.\/]+/;

const HELPERS = [
...SPECIAL,
...LEGACY,
...INLINE_HELPERS,
...OTHER_OPERATORS,
...LOGICAL_OPERATOR_HELPERS,
...NUMERIC_COMPARISON_HELPERS,
...EQUALITY_HELPERS
];

// as defined in https://handlebarsjs.com/guide/expressions.html#literal-segments
// this regex matches literal segments like ' abc ' or [ abc ] as well as helpers and paths
Expand All @@ -69,7 +120,7 @@ export default function(hljs) {
SINGLE_QUOTED_ID_REGEX,
BRACKET_QUOTED_ID_REGEX,
PLAIN_ID_REGEX
);
);

const IDENTIFIER_REGEX = regex.concat(
regex.optional(/\.|\.\/|\//), // relative or absolute path
Expand Down Expand Up @@ -145,7 +196,7 @@ export default function(hljs) {
BLOCK_PARAMS,
HASH,
HELPER_PARAMETER,
SUB_EXPRESSION
SUB_EXPRESSION,
],
returnEnd: true
// the property "end" is defined through inheritance when the mode is used. If depends
Expand All @@ -154,31 +205,48 @@ export default function(hljs) {
};

const SUB_EXPRESSION_CONTENTS = hljs.inherit(HELPER_NAME_OR_PATH_EXPRESSION, {
className: 'name',
keywords: BUILT_INS,
className: 'function',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make sure that your PR explanation justifies (visual) changes like this and explains why they are necessary...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can do!

In this particular case, helpers are functions (and take args), and not actually just a name, like you'd have with the name of a Component invocation.

keywords: HELPERS,
starts: hljs.inherit(HELPER_PARAMETERS, {
end: /\)/
})
});

SUB_EXPRESSION.contains = [SUB_EXPRESSION_CONTENTS];
SUB_EXPRESSION.contains = [ SUB_EXPRESSION_CONTENTS ];

const KEYWORD_CONTENTS = hljs.inherit({}, {
keywords: [
...SPECIAL,
...LITERALS
],
className: 'keyword'
});

const TAG_CONTENTS = hljs.inherit(HELPER_NAME_OR_PATH_EXPRESSION, {
keywords: BLOCK_HELPERS,
className: 'name',
starts: hljs.inherit(HELPER_PARAMETERS, {
end: />/
})
});

const OPENING_BLOCK_MUSTACHE_CONTENTS = hljs.inherit(HELPER_NAME_OR_PATH_EXPRESSION, {
keywords: BUILT_INS,
keywords: BLOCK_HELPERS,
className: 'name',
starts: hljs.inherit(HELPER_PARAMETERS, {
end: /\}\}/
})
});


const CLOSING_BLOCK_MUSTACHE_CONTENTS = hljs.inherit(HELPER_NAME_OR_PATH_EXPRESSION, {
keywords: BUILT_INS,
keywords: BLOCK_HELPERS,
className: 'name'
});

const BASIC_MUSTACHE_CONTENTS = hljs.inherit(HELPER_NAME_OR_PATH_EXPRESSION, {
className: 'name',
keywords: BUILT_INS,
keywords: HELPERS,
starts: hljs.inherit(HELPER_PARAMETERS, {
end: /\}\}/
})
Expand Down Expand Up @@ -213,7 +281,7 @@ export default function(hljs) {
className: 'template-tag',
begin: /\{\{\{\{(?!\/)/,
end: /\}\}\}\}/,
contains: [OPENING_BLOCK_MUSTACHE_CONTENTS],
contains: [ OPENING_BLOCK_MUSTACHE_CONTENTS ],
starts: {
end: /\{\{\{\{\//,
returnEnd: true,
Expand All @@ -225,14 +293,33 @@ export default function(hljs) {
className: 'template-tag',
begin: /\{\{\{\{\//,
end: /\}\}\}\}/,
contains: [CLOSING_BLOCK_MUSTACHE_CONTENTS]
contains: [ CLOSING_BLOCK_MUSTACHE_CONTENTS ]
},
// open angle-brocket component invocation
{
className: 'template-tag',
begin: /<:?/,
end: />/,
contains: [
TAG_CONTENTS,
// OPENING_BLOCK_MUSTACHE_CONTENTS,
// BLOCK_PARAMS,
// KEYWORD_CONTENTS
]
},
// end angle-brocket component invocation
{
className: 'template-tag',
begin: /<:?\//,
end: />/,
contains: [ ]
},
{
// open block statement
className: 'template-tag',
begin: /\{\{#/,
end: /\}\}/,
contains: [OPENING_BLOCK_MUSTACHE_CONTENTS]
contains: [ OPENING_BLOCK_MUSTACHE_CONTENTS ]
},
{
className: 'template-tag',
Expand All @@ -246,26 +333,32 @@ export default function(hljs) {
end: /\}\}/,
keywords: 'else if'
},
{
className: 'bulit_in',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo :-)

begin: /\{\{/,
end: /\}\}/,
keywords: MODIFIERS
},
{
// closing block statement
className: 'template-tag',
begin: /\{\{\//,
end: /\}\}/,
contains: [CLOSING_BLOCK_MUSTACHE_CONTENTS]
contains: [ CLOSING_BLOCK_MUSTACHE_CONTENTS ]
},
{
// template variable or helper-call that is NOT html-escaped
className: 'template-variable',
begin: /\{\{\{/,
end: /\}\}\}/,
contains: [BASIC_MUSTACHE_CONTENTS]
contains: [ BASIC_MUSTACHE_CONTENTS ]
},
{
// template variable or helper-call that is html-escaped
className: 'template-variable',
begin: /\{\{/,
end: /\}\}/,
contains: [BASIC_MUSTACHE_CONTENTS]
contains: [ BASIC_MUSTACHE_CONTENTS ]
}
]
};
Expand Down