module.exports = function(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 FUNCT_MODE = function(name, ident, obj) { return hljs.inherit({ className: name, begin: ident + '\\(', end: '\\(', returnBegin: true, excludeEnd: true, relevance: 0 }, obj); }; 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 :( IDENT_MODE('hexcolor', '#[0-9A-Fa-f]+\\b'), FUNCT_MODE('function', '(url|data-uri)', { starts: {className: 'string', end: '[\\)\\n]', excludeEnd: true} }), FUNCT_MODE('function', IDENT_RE), 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 } ); 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 = { className: 'attribute', begin: INTERP_IDENT_RE, end: ':', excludeEnd: true, contains: [hljs.C_LINE_COMMENT_MODE, hljs.C_BLOCK_COMMENT_MODE], illegal: /\S/, starts: {end: '[;}]', returnEnd: true, contains: VALUE, illegal: '[<=$]'} }; var AT_RULE_MODE = { className: 'at_rule', // highlight only at-rule 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: '[<=\'$"]', 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('tag', INTERP_IDENT_RE + '%?', 0), // '%' for more consistent coloring of @keyframes "tags" IDENT_MODE('id', '#' + INTERP_IDENT_RE), IDENT_MODE('class', '\\.' + INTERP_IDENT_RE, 0), IDENT_MODE('keyword', '&', 0), FUNCT_MODE('pseudo', ':not'), FUNCT_MODE('keyword', ':extend'), IDENT_MODE('pseudo', '::?' + INTERP_IDENT_RE), {className: 'attr_selector', begin: '\\[', end: '\\]'}, {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, SELECTOR_MODE, RULE_MODE ); return { case_insensitive: true, illegal: '[=>\'/<($"]', contains: RULES }; };