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;