import * as css from "./lib/css-shared.js";
export default function(hljs) {
const modes = css.MODES(hljs);
const PSEUDO_SELECTORS = css.PSEUDO_SELECTORS;
const AT_MODIFIERS = "and or not only";
const IDENT_RE = '[\\w-]+';
const INTERP_IDENT_RE = '(' + IDENT_RE + '|@\\{' + IDENT_RE + '\\})';
const RULES = []; const VALUE_MODES = [];
const STRING_MODE = function(c) {
return {
className: 'string',
begin: '~?' + c + '.*?' + c
};
};
const IDENT_MODE = function(name, begin, relevance) {
return {
className: name,
begin: begin,
relevance: relevance
};
};
const AT_KEYWORDS = {
$pattern: /[a-z-]+/,
keyword: AT_MODIFIERS,
attribute: css.MEDIA_FEATURES.join(" ")
};
const PARENS_MODE = {
begin: '\\(',
end: '\\)',
contains: VALUE_MODES,
keywords: AT_KEYWORDS,
relevance: 0
};
VALUE_MODES.push(
hljs.C_LINE_COMMENT_MODE,
hljs.C_BLOCK_COMMENT_MODE,
STRING_MODE("'"),
STRING_MODE('"'),
modes.CSS_NUMBER_MODE,
{
begin: '(url|data-uri)\\(',
starts: {
className: 'string',
end: '[\\)\\n]',
excludeEnd: true
}
},
modes.HEXCOLOR,
PARENS_MODE,
IDENT_MODE('variable', '@@?' + IDENT_RE, 10),
IDENT_MODE('variable', '@\\{' + IDENT_RE + '\\}'),
IDENT_MODE('built_in', '~?`[^`]*?`'),
{
className: 'attribute',
begin: IDENT_RE + '\\s*:',
end: ':',
returnBegin: true,
excludeEnd: true
},
modes.IMPORTANT
);
const VALUE_WITH_RULESETS = VALUE_MODES.concat({
begin: /\{/,
end: /\}/,
contains: RULES
});
const MIXIN_GUARD_MODE = {
beginKeywords: 'when',
endsWithParent: true,
contains: [ { beginKeywords: 'and not' } ].concat(VALUE_MODES)
};
const RULE_MODE = {
begin: INTERP_IDENT_RE + '\\s*:',
returnBegin: true,
end: /[;}]/,
relevance: 0,
contains: [
{ begin: /-(webkit|moz|ms|o)-/ },
modes.CSS_VARIABLE,
{
className: 'attribute',
begin: '\\b(' + css.ATTRIBUTES.join('|') + ')\\b',
end: /(?=:)/,
starts: {
endsWithParent: true,
illegal: '[<=$]',
relevance: 0,
contains: VALUE_MODES
}
}
]
};
const AT_RULE_MODE = {
className: 'keyword',
begin: '@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b',
starts: {
end: '[;{}]',
keywords: AT_KEYWORDS,
returnEnd: true,
contains: VALUE_MODES,
relevance: 0
}
};
const VAR_RULE_MODE = {
className: 'variable',
variants: [
{
begin: '@' + IDENT_RE + '\\s*:',
relevance: 15
},
{ begin: '@' + IDENT_RE }
],
starts: {
end: '[;}]',
returnEnd: true,
contains: VALUE_WITH_RULESETS
}
};
const SELECTOR_MODE = {
variants: [
{
begin: '[\\.#:&\\[>]',
end: '[;{}]'
},
{
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 + '\\}'),
{
begin: '\\b(' + css.TAGS.join('|') + ')\\b',
className: 'selector-tag'
},
modes.CSS_NUMBER_MODE,
IDENT_MODE('selector-tag', INTERP_IDENT_RE, 0),
IDENT_MODE('selector-id', '#' + INTERP_IDENT_RE),
IDENT_MODE('selector-class', '\\.' + INTERP_IDENT_RE, 0),
IDENT_MODE('selector-tag', '&', 0),
modes.ATTRIBUTE_SELECTOR_MODE,
{
className: 'selector-pseudo',
begin: ':(' + css.PSEUDO_CLASSES.join('|') + ')'
},
{
className: 'selector-pseudo',
begin: ':(:)?(' + css.PSEUDO_ELEMENTS.join('|') + ')'
},
{
begin: /\(/,
end: /\)/,
relevance: 0,
contains: VALUE_WITH_RULESETS
},
{ begin: '!important' },
modes.FUNCTION_DISPATCH
]
};
const PSEUDO_SELECTOR_MODE = {
begin: IDENT_RE + ':(:)?' + `(${PSEUDO_SELECTORS.join('|')})`,
returnBegin: true,
contains: [ SELECTOR_MODE ]
};
RULES.push(
hljs.C_LINE_COMMENT_MODE,
hljs.C_BLOCK_COMMENT_MODE,
AT_RULE_MODE,
VAR_RULE_MODE,
PSEUDO_SELECTOR_MODE,
RULE_MODE,
SELECTOR_MODE
);
return {
name: 'Less',
case_insensitive: true,
illegal: '[=>\'/<($"]',
contains: RULES
};
}