github.com/kayoticsully/syncthing@v0.8.9-0.20140724133906-c45a2fdc03f8/assets/bootstrap-3.1.1/grunt/bs-lessdoc-parser.js (about)

     1  /*!
     2   * Bootstrap Grunt task for parsing Less docstrings
     3   * http://getbootstrap.com
     4   * Copyright 2014 Twitter, Inc.
     5   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
     6   */
     7  'use strict';
     8  
     9  var markdown = require('markdown').markdown;
    10  
    11  function markdown2html(markdownString) {
    12    // the slice removes the <p>...</p> wrapper output by Markdown processor
    13    return markdown.toHTML(markdownString.trim()).slice(3, -4);
    14  }
    15  
    16  
    17  /*
    18  Mini-language:
    19    //== This is a normal heading, which starts a section. Sections group variables together.
    20    //## Optional description for the heading
    21  
    22    //=== This is a subheading.
    23  
    24    //** Optional description for the following variable. You **can** use Markdown in descriptions to discuss `<html>` stuff.
    25    @foo: #ffff;
    26  
    27    //-- This is a heading for a section whose variables shouldn't be customizable
    28  
    29    All other lines are ignored completely.
    30  */
    31  
    32  
    33  var CUSTOMIZABLE_HEADING = /^[/]{2}={2}(.*)$/;
    34  var UNCUSTOMIZABLE_HEADING = /^[/]{2}-{2}(.*)$/;
    35  var SUBSECTION_HEADING = /^[/]{2}={3}(.*)$/;
    36  var SECTION_DOCSTRING = /^[/]{2}#{2}(.*)$/;
    37  var VAR_ASSIGNMENT = /^(@[a-zA-Z0-9_-]+):[ ]*([^ ;][^;]+);[ ]*$/;
    38  var VAR_DOCSTRING = /^[/]{2}[*]{2}(.*)$/;
    39  
    40  function Section(heading, customizable) {
    41    this.heading = heading.trim();
    42    this.id = this.heading.replace(/\s+/g, '-').toLowerCase();
    43    this.customizable = customizable;
    44    this.docstring = null;
    45    this.subsections = [];
    46  }
    47  
    48  Section.prototype.addSubSection = function (subsection) {
    49    this.subsections.push(subsection);
    50  };
    51  
    52  function SubSection(heading) {
    53    this.heading = heading.trim();
    54    this.id = this.heading.replace(/\s+/g, '-').toLowerCase();
    55    this.variables = [];
    56  }
    57  
    58  SubSection.prototype.addVar = function (variable) {
    59    this.variables.push(variable);
    60  };
    61  
    62  function VarDocstring(markdownString) {
    63    this.html = markdown2html(markdownString);
    64  }
    65  
    66  function SectionDocstring(markdownString) {
    67    this.html = markdown2html(markdownString);
    68  }
    69  
    70  function Variable(name, defaultValue) {
    71    this.name = name;
    72    this.defaultValue = defaultValue;
    73    this.docstring = null;
    74  }
    75  
    76  function Tokenizer(fileContent) {
    77    this._lines = fileContent.split('\n');
    78    this._next = undefined;
    79  }
    80  
    81  Tokenizer.prototype.unshift = function (token) {
    82    if (this._next !== undefined) {
    83      throw new Error('Attempted to unshift twice!');
    84    }
    85    this._next = token;
    86  };
    87  
    88  Tokenizer.prototype._shift = function () {
    89    // returning null signals EOF
    90    // returning undefined means the line was ignored
    91    if (this._next !== undefined) {
    92      var result = this._next;
    93      this._next = undefined;
    94      return result;
    95    }
    96    if (this._lines.length <= 0) {
    97      return null;
    98    }
    99    var line = this._lines.shift();
   100    var match = null;
   101    match = SUBSECTION_HEADING.exec(line);
   102    if (match !== null) {
   103      return new SubSection(match[1]);
   104    }
   105    match = CUSTOMIZABLE_HEADING.exec(line);
   106    if (match !== null) {
   107      return new Section(match[1], true);
   108    }
   109    match = UNCUSTOMIZABLE_HEADING.exec(line);
   110    if (match !== null) {
   111      return new Section(match[1], false);
   112    }
   113    match = SECTION_DOCSTRING.exec(line);
   114    if (match !== null) {
   115      return new SectionDocstring(match[1]);
   116    }
   117    match = VAR_DOCSTRING.exec(line);
   118    if (match !== null) {
   119      return new VarDocstring(match[1]);
   120    }
   121    var commentStart = line.lastIndexOf('//');
   122    var varLine = (commentStart === -1) ? line : line.slice(0, commentStart);
   123    match = VAR_ASSIGNMENT.exec(varLine);
   124    if (match !== null) {
   125      return new Variable(match[1], match[2]);
   126    }
   127    return undefined;
   128  };
   129  
   130  Tokenizer.prototype.shift = function () {
   131    while (true) {
   132      var result = this._shift();
   133      if (result === undefined) {
   134        continue;
   135      }
   136      return result;
   137    }
   138  };
   139  
   140  function Parser(fileContent) {
   141    this._tokenizer = new Tokenizer(fileContent);
   142  }
   143  
   144  Parser.prototype.parseFile = function () {
   145    var sections = [];
   146    while (true) {
   147      var section = this.parseSection();
   148      if (section === null) {
   149        if (this._tokenizer.shift() !== null) {
   150          throw new Error('Unexpected unparsed section of file remains!');
   151        }
   152        return sections;
   153      }
   154      sections.push(section);
   155    }
   156  };
   157  
   158  Parser.prototype.parseSection = function () {
   159    var section = this._tokenizer.shift();
   160    if (section === null) {
   161      return null;
   162    }
   163    if (!(section instanceof Section)) {
   164      throw new Error('Expected section heading; got: ' + JSON.stringify(section));
   165    }
   166    var docstring = this._tokenizer.shift();
   167    if (docstring instanceof SectionDocstring) {
   168      section.docstring = docstring;
   169    }
   170    else {
   171      this._tokenizer.unshift(docstring);
   172    }
   173    this.parseSubSections(section);
   174  
   175    return section;
   176  };
   177  
   178  Parser.prototype.parseSubSections = function (section) {
   179    while (true) {
   180      var subsection = this.parseSubSection();
   181      if (subsection === null) {
   182        if (section.subsections.length === 0) {
   183          // Presume an implicit initial subsection
   184          subsection = new SubSection('');
   185          this.parseVars(subsection);
   186        }
   187        else {
   188          break;
   189        }
   190      }
   191      section.addSubSection(subsection);
   192    }
   193  
   194    if (section.subsections.length === 1 && !(section.subsections[0].heading) && section.subsections[0].variables.length === 0) {
   195      // Ignore lone empty implicit subsection
   196      section.subsections = [];
   197    }
   198  };
   199  
   200  Parser.prototype.parseSubSection = function () {
   201    var subsection = this._tokenizer.shift();
   202    if (subsection instanceof SubSection) {
   203      this.parseVars(subsection);
   204      return subsection;
   205    }
   206    this._tokenizer.unshift(subsection);
   207    return null;
   208  };
   209  
   210  Parser.prototype.parseVars = function (subsection) {
   211    while (true) {
   212      var variable = this.parseVar();
   213      if (variable === null) {
   214        return;
   215      }
   216      subsection.addVar(variable);
   217    }
   218  };
   219  
   220  Parser.prototype.parseVar = function () {
   221    var docstring = this._tokenizer.shift();
   222    if (!(docstring instanceof VarDocstring)) {
   223      this._tokenizer.unshift(docstring);
   224      docstring = null;
   225    }
   226    var variable = this._tokenizer.shift();
   227    if (variable instanceof Variable) {
   228      variable.docstring = docstring;
   229      return variable;
   230    }
   231    this._tokenizer.unshift(variable);
   232    return null;
   233  };
   234  
   235  
   236  module.exports = Parser;