text: /*
  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
  */
  
  import * as css_shared from "./lib/css-shared.js";
  
  /** @type LanguageFn */
  export default function(hljs) {
    const PSEUDO_SELECTORS = css_shared.PSEUDO_SELECTORS;
  
  
    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: [
        { begin: /-(webkit|moz|ms|o)-/ },
        {
          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
      ]
    };
  
    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
    };
  }
  