l0bsterssg

node.js static responsive blog post generator
Log | Files | Refs | README

less.js (5069B)


      1 /*
      2 Language: Less
      3 Description: It's CSS, with just a little more.
      4 Author:   Max Mikhailov <seven.phases.max@gmail.com>
      5 Website: http://lesscss.org
      6 Category: common, css
      7 */
      8 
      9 function less(hljs) {
     10   var IDENT_RE        = '[\\w-]+'; // yes, Less identifiers may begin with a digit
     11   var INTERP_IDENT_RE = '(' + IDENT_RE + '|@{' + IDENT_RE + '})';
     12 
     13   /* Generic Modes */
     14 
     15   var RULES = [], VALUE = []; // forward def. for recursive modes
     16 
     17   var STRING_MODE = function(c) { return {
     18     // Less strings are not multiline (also include '~' for more consistent coloring of "escaped" strings)
     19     className: 'string', begin: '~?' + c + '.*?' + c
     20   };};
     21 
     22   var IDENT_MODE = function(name, begin, relevance) { return {
     23     className: name, begin: begin, relevance: relevance
     24   };};
     25 
     26   var PARENS_MODE = {
     27     // used only to properly balance nested parens inside mixin call, def. arg list
     28     begin: '\\(', end: '\\)', contains: VALUE, relevance: 0
     29   };
     30 
     31   // generic Less highlighter (used almost everywhere except selectors):
     32   VALUE.push(
     33     hljs.C_LINE_COMMENT_MODE,
     34     hljs.C_BLOCK_COMMENT_MODE,
     35     STRING_MODE("'"),
     36     STRING_MODE('"'),
     37     hljs.CSS_NUMBER_MODE, // fixme: it does not include dot for numbers like .5em :(
     38     {
     39       begin: '(url|data-uri)\\(',
     40       starts: {className: 'string', end: '[\\)\\n]', excludeEnd: true}
     41     },
     42     IDENT_MODE('number', '#[0-9A-Fa-f]+\\b'),
     43     PARENS_MODE,
     44     IDENT_MODE('variable', '@@?' + IDENT_RE, 10),
     45     IDENT_MODE('variable', '@{'  + IDENT_RE + '}'),
     46     IDENT_MODE('built_in', '~?`[^`]*?`'), // inline javascript (or whatever host language) *multiline* string
     47     { // @media features (it’s here to not duplicate things in AT_RULE_MODE with extra PARENS_MODE overriding):
     48       className: 'attribute', begin: IDENT_RE + '\\s*:', end: ':', returnBegin: true, excludeEnd: true
     49     },
     50     {
     51       className: 'meta',
     52       begin: '!important'
     53     }
     54   );
     55 
     56   var VALUE_WITH_RULESETS = VALUE.concat({
     57     begin: '{', end: '}', contains: RULES
     58   });
     59 
     60   var MIXIN_GUARD_MODE = {
     61     beginKeywords: 'when', endsWithParent: true,
     62     contains: [{beginKeywords: 'and not'}].concat(VALUE) // using this form to override VALUE’s 'function' match
     63   };
     64 
     65   /* Rule-Level Modes */
     66 
     67   var RULE_MODE = {
     68     begin: INTERP_IDENT_RE + '\\s*:', returnBegin: true, end: '[;}]',
     69     relevance: 0,
     70     contains: [
     71       {
     72         className: 'attribute',
     73         begin: INTERP_IDENT_RE, end: ':', excludeEnd: true,
     74         starts: {
     75           endsWithParent: true, illegal: '[<=$]',
     76           relevance: 0,
     77           contains: VALUE
     78         }
     79       }
     80     ]
     81   };
     82 
     83   var AT_RULE_MODE = {
     84     className: 'keyword',
     85     begin: '@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b',
     86     starts: {end: '[;{}]', returnEnd: true, contains: VALUE, relevance: 0}
     87   };
     88 
     89   // variable definitions and calls
     90   var VAR_RULE_MODE = {
     91     className: 'variable',
     92     variants: [
     93       // using more strict pattern for higher relevance to increase chances of Less detection.
     94       // this is *the only* Less specific statement used in most of the sources, so...
     95       // (we’ll still often loose to the css-parser unless there's '//' comment,
     96       // simply because 1 variable just can't beat 99 properties :)
     97       {begin: '@' + IDENT_RE + '\\s*:', relevance: 15},
     98       {begin: '@' + IDENT_RE}
     99     ],
    100     starts: {end: '[;}]', returnEnd: true, contains: VALUE_WITH_RULESETS}
    101   };
    102 
    103   var SELECTOR_MODE = {
    104     // first parse unambiguous selectors (i.e. those not starting with tag)
    105     // then fall into the scary lookahead-discriminator variant.
    106     // this mode also handles mixin definitions and calls
    107     variants: [{
    108       begin: '[\\.#:&\\[>]', end: '[;{}]'  // mixin calls end with ';'
    109       }, {
    110       begin: INTERP_IDENT_RE, end: '{'
    111     }],
    112     returnBegin: true,
    113     returnEnd:   true,
    114     illegal: '[<=\'$"]',
    115     relevance: 0,
    116     contains: [
    117       hljs.C_LINE_COMMENT_MODE,
    118       hljs.C_BLOCK_COMMENT_MODE,
    119       MIXIN_GUARD_MODE,
    120       IDENT_MODE('keyword',  'all\\b'),
    121       IDENT_MODE('variable', '@{'  + IDENT_RE + '}'),     // otherwise it’s identified as tag
    122       IDENT_MODE('selector-tag',  INTERP_IDENT_RE + '%?', 0), // '%' for more consistent coloring of @keyframes "tags"
    123       IDENT_MODE('selector-id', '#' + INTERP_IDENT_RE),
    124       IDENT_MODE('selector-class', '\\.' + INTERP_IDENT_RE, 0),
    125       IDENT_MODE('selector-tag',  '&', 0),
    126       {className: 'selector-attr', begin: '\\[', end: '\\]'},
    127       {className: 'selector-pseudo', begin: /:(:)?[a-zA-Z0-9\_\-\+\(\)"'.]+/},
    128       {begin: '\\(', end: '\\)', contains: VALUE_WITH_RULESETS}, // argument list of parametric mixins
    129       {begin: '!important'} // eat !important after mixin call or it will be colored as tag
    130     ]
    131   };
    132 
    133   RULES.push(
    134     hljs.C_LINE_COMMENT_MODE,
    135     hljs.C_BLOCK_COMMENT_MODE,
    136     AT_RULE_MODE,
    137     VAR_RULE_MODE,
    138     RULE_MODE,
    139     SELECTOR_MODE
    140   );
    141 
    142   return {
    143     name: 'Less',
    144     case_insensitive: true,
    145     illegal: '[=>\'/<($"]',
    146     contains: RULES
    147   };
    148 }
    149 
    150 module.exports = less;