You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
150 lines
5.0 KiB
150 lines
5.0 KiB
/*
|
|
Language: Less
|
|
Description: It's CSS, with just a little more.
|
|
Author: Max Mikhailov <seven.phases.max@gmail.com>
|
|
Website: http://lesscss.org
|
|
Category: common, css
|
|
*/
|
|
|
|
function less(hljs) {
|
|
var IDENT_RE = '[\\w-]+'; // yes, Less identifiers may begin with a digit
|
|
var INTERP_IDENT_RE = '(' + IDENT_RE + '|@\\{' + IDENT_RE + '\\})';
|
|
|
|
/* Generic Modes */
|
|
|
|
var RULES = [], VALUE = []; // forward def. for recursive modes
|
|
|
|
var STRING_MODE = function(c) { return {
|
|
// Less strings are not multiline (also include '~' for more consistent coloring of "escaped" strings)
|
|
className: 'string', begin: '~?' + c + '.*?' + c
|
|
};};
|
|
|
|
var IDENT_MODE = function(name, begin, relevance) { return {
|
|
className: name, begin: begin, relevance: relevance
|
|
};};
|
|
|
|
var PARENS_MODE = {
|
|
// used only to properly balance nested parens inside mixin call, def. arg list
|
|
begin: '\\(', end: '\\)', contains: VALUE, relevance: 0
|
|
};
|
|
|
|
// generic Less highlighter (used almost everywhere except selectors):
|
|
VALUE.push(
|
|
hljs.C_LINE_COMMENT_MODE,
|
|
hljs.C_BLOCK_COMMENT_MODE,
|
|
STRING_MODE("'"),
|
|
STRING_MODE('"'),
|
|
hljs.CSS_NUMBER_MODE, // fixme: it does not include dot for numbers like .5em :(
|
|
{
|
|
begin: '(url|data-uri)\\(',
|
|
starts: {className: 'string', end: '[\\)\\n]', excludeEnd: true}
|
|
},
|
|
IDENT_MODE('number', '#[0-9A-Fa-f]+\\b'),
|
|
PARENS_MODE,
|
|
IDENT_MODE('variable', '@@?' + IDENT_RE, 10),
|
|
IDENT_MODE('variable', '@\\{' + IDENT_RE + '\\}'),
|
|
IDENT_MODE('built_in', '~?`[^`]*?`'), // inline javascript (or whatever host language) *multiline* string
|
|
{ // @media features (it’s here to not duplicate things in AT_RULE_MODE with extra PARENS_MODE overriding):
|
|
className: 'attribute', begin: IDENT_RE + '\\s*:', end: ':', returnBegin: true, excludeEnd: true
|
|
},
|
|
{
|
|
className: 'meta',
|
|
begin: '!important'
|
|
}
|
|
);
|
|
|
|
var VALUE_WITH_RULESETS = VALUE.concat({
|
|
begin: /\{/, end: /\}/, contains: RULES
|
|
});
|
|
|
|
var MIXIN_GUARD_MODE = {
|
|
beginKeywords: 'when', endsWithParent: true,
|
|
contains: [{beginKeywords: 'and not'}].concat(VALUE) // using this form to override VALUE’s 'function' match
|
|
};
|
|
|
|
/* Rule-Level Modes */
|
|
|
|
var RULE_MODE = {
|
|
begin: INTERP_IDENT_RE + '\\s*:', returnBegin: true, end: '[;}]',
|
|
relevance: 0,
|
|
contains: [
|
|
{
|
|
className: 'attribute',
|
|
begin: INTERP_IDENT_RE, end: ':', excludeEnd: true,
|
|
starts: {
|
|
endsWithParent: true, illegal: '[<=$]',
|
|
relevance: 0,
|
|
contains: VALUE
|
|
}
|
|
}
|
|
]
|
|
};
|
|
|
|
var AT_RULE_MODE = {
|
|
className: 'keyword',
|
|
begin: '@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b',
|
|
starts: {end: '[;{}]', returnEnd: true, contains: VALUE, relevance: 0}
|
|
};
|
|
|
|
// variable definitions and calls
|
|
var VAR_RULE_MODE = {
|
|
className: 'variable',
|
|
variants: [
|
|
// using more strict pattern for higher relevance to increase chances of Less detection.
|
|
// this is *the only* Less specific statement used in most of the sources, so...
|
|
// (we’ll still often loose to the css-parser unless there's '//' comment,
|
|
// simply because 1 variable just can't beat 99 properties :)
|
|
{begin: '@' + IDENT_RE + '\\s*:', relevance: 15},
|
|
{begin: '@' + IDENT_RE}
|
|
],
|
|
starts: {end: '[;}]', returnEnd: true, contains: VALUE_WITH_RULESETS}
|
|
};
|
|
|
|
var SELECTOR_MODE = {
|
|
// first parse unambiguous selectors (i.e. those not starting with tag)
|
|
// then fall into the scary lookahead-discriminator variant.
|
|
// this mode also handles mixin definitions and calls
|
|
variants: [{
|
|
begin: '[\\.#:&\\[>]', end: '[;{}]' // mixin calls end with ';'
|
|
}, {
|
|
begin: INTERP_IDENT_RE, end: /\{/
|
|
}],
|
|
returnBegin: true,
|
|
returnEnd: true,
|
|
illegal: '[<=\'$"]',
|
|
relevance: 0,
|
|
contains: [
|
|
hljs.C_LINE_COMMENT_MODE,
|
|
hljs.C_BLOCK_COMMENT_MODE,
|
|
MIXIN_GUARD_MODE,
|
|
IDENT_MODE('keyword', 'all\\b'),
|
|
IDENT_MODE('variable', '@\\{' + IDENT_RE + '\\}'), // otherwise it’s identified as tag
|
|
IDENT_MODE('selector-tag', INTERP_IDENT_RE + '%?', 0), // '%' for more consistent coloring of @keyframes "tags"
|
|
IDENT_MODE('selector-id', '#' + INTERP_IDENT_RE),
|
|
IDENT_MODE('selector-class', '\\.' + INTERP_IDENT_RE, 0),
|
|
IDENT_MODE('selector-tag', '&', 0),
|
|
{className: 'selector-attr', begin: '\\[', end: '\\]'},
|
|
{className: 'selector-pseudo', begin: /:(:)?[a-zA-Z0-9_\-+()"'.]+/},
|
|
{begin: '\\(', end: '\\)', contains: VALUE_WITH_RULESETS}, // argument list of parametric mixins
|
|
{begin: '!important'} // eat !important after mixin call or it will be colored as tag
|
|
]
|
|
};
|
|
|
|
RULES.push(
|
|
hljs.C_LINE_COMMENT_MODE,
|
|
hljs.C_BLOCK_COMMENT_MODE,
|
|
AT_RULE_MODE,
|
|
VAR_RULE_MODE,
|
|
RULE_MODE,
|
|
SELECTOR_MODE
|
|
);
|
|
|
|
return {
|
|
name: 'Less',
|
|
case_insensitive: true,
|
|
illegal: '[=>\'/<($"]',
|
|
contains: RULES
|
|
};
|
|
}
|
|
|
|
module.exports = less;
|
|
|