chore(doc-gen): new syntax for cheatsheet items

This commit is contained in:
Peter Bacon Darwin 2015-12-08 21:49:32 +00:00 committed by Naomi Black
parent 8459a4dd5c
commit 3ede7bfea9
3 changed files with 152 additions and 76 deletions

View File

@ -1,6 +1,6 @@
var Package = require('dgeni').Package;
module.exports = new Package('cheatsheet', [require('../content-package')])
module.exports = new Package('cheatsheet', [require('../content-package'), require('../target-package')])
.factory(require('./services/cheatsheetItemParser'))
.processor(require('./processors/createCheatsheetDoc'))

View File

@ -29,57 +29,92 @@
* }
* ```
*/
module.exports = function cheatsheetItemParser() {
module.exports = function cheatsheetItemParser(targetEnvironments) {
return function(text) {
var index = 0;
var fields = getFields(text, ['syntax', 'description']);
var item = {
syntax: '',
bold: [],
description: ''
};
var STATES = {
inSyntax: function() {
if (text.charAt(index) !== '`') throw new Error('item syntax must start with a backtick');
index += 1;
var syntaxStart = index;
while(index < text.length && text.charAt(index) !== '`') index++;
if (index === text.length) throw new Error('item syntax must end with a backtick');
item.syntax = text.substring(syntaxStart, index);
state = STATES.pipe;
index++;
},
pipe: function() {
if (text.charAt(index) === '|') {
index++;
while(index < text.length && /\s/.test(text.charAt(index))) index++;
state = STATES.bold;
} else {
state = STATES.description;
fields.forEach(function(field) {
if (!field.languages || targetEnvironments.someActive(field.languages)) {
switch(field.name) {
case 'syntax':
parseSyntax(field.value.trim());
break;
case 'description':
item.description = field.value.trim();
break;
}
},
bold: function() {
if (text.charAt(index) !== '`') throw new Error('bold matcher must start with a backtick');
index += 1;
var boldStart = index;
while(index < text.length && text.charAt(index) !== '`') index++;
if (index === text.length) throw new Error('bold matcher must end with a backtick');
item.bold.push(text.substring(boldStart, index));
state = STATES.pipe;
index++;
},
description: function() {
item.description = text.substring(index);
state = null;
}
};
var state = STATES.inSyntax;
while(state) {
state();
}
});
return item;
function parseSyntax(text) {
var index = 0;
if (text.charAt(index) !== '`') throw new Error('item syntax must start with a backtick');
var start = index + 1;
index = text.indexOf('`', start);
if (index === -1) throw new Error('item syntax must end with a backtick');
item.syntax = text.substring(start, index);
start = index + 1;
// skip to next pipe
while(index < text.length && text.charAt(index) !== '|') index += 1;
while(text.charAt(start) === '|') {
start += 1;
// skip whitespace
while(start < text.length && /\s/.test(text.charAt(start))) start++;
if (text.charAt(start) !== '`') throw new Error('bold matcher must start with a backtick');
start += 1;
index = text.indexOf('`', start);
if (index === -1) throw new Error('bold matcher must end with a backtick');
item.bold.push(text.substring(start, index));
start = index + 1;
}
if (start !== text.length) {
throw new Error('syntax field must only contain a syntax code block and zero or more bold ' +
'matcher code blocks, delimited by pipes.\n' +
'Instead it was "' + text + '"');
}
}
};
}
function getFields(text, fieldNames) {
var FIELD_START = /^([^:(]+)\(?([^)]+)?\)?:$/;
var lines = text.split('\n');
var fields = [];
var field, line;
while(lines.length) {
line = lines.shift();
var match = FIELD_START.exec(line);
if (match && fieldNames.indexOf(match[1]) !== -1) {
// start new field
if (field) {fields.push(field);}
field = { name: match[1], languages: (match[2] && match[2].split(' ')), value: ''};
} else {
if (!field) throw new Error('item must start with one of the following field specifiers:\n' +
fieldNames.map(function(field) { return field + ':'; }).join('\n') + '\n' +
'but instead it contained: "' + text + '"');
field.value += line + '\n';
}
}
if (field) {fields.push(field);}
return fields;
}

View File

@ -8,45 +8,86 @@ describe('cheatsheetItemParser', function() {
dgeni = new Dgeni([mockPackage()]);
injector = dgeni.configureInjector();
cheatsheetItemParser = injector.get('cheatsheetItemParser');
var targetEnvironments = injector.get('targetEnvironments');
targetEnvironments.addAllowed('js');
targetEnvironments.addAllowed('ts', true);
});
it('should extract the syntax', function() {
expect(cheatsheetItemParser('`abc`')).toEqual({
syntax: 'abc',
bold: [],
description: ''
describe('no language targets', function() {
it('should extract the syntax', function() {
expect(cheatsheetItemParser('syntax:\n`abc`')).toEqual({
syntax: 'abc',
bold: [],
description: ''
});
});
it('should extract the bolds', function() {
expect(cheatsheetItemParser('syntax:\n`abc`|`bold1`|`bold2`')).toEqual({
syntax: 'abc',
bold: ['bold1', 'bold2'],
description: ''
});
});
it('should extract the description', function() {
expect(cheatsheetItemParser('syntax:\n`abc`|`bold1`|`bold2`\ndescription:\nsome description')).toEqual({
syntax: 'abc',
bold: ['bold1', 'bold2'],
description: 'some description'
});
});
it('should allow bold to be optional', function() {
expect(cheatsheetItemParser('syntax:\n`abc`\ndescription:\nsome description')).toEqual({
syntax: 'abc',
bold: [],
description: 'some description'
});
});
it('should allow whitespace between the parts', function() {
expect(cheatsheetItemParser('syntax:\n`abc`| `bold1`| `bold2`\ndescription:\n\nsome description')).toEqual({
syntax: 'abc',
bold: ['bold1', 'bold2'],
description: 'some description'
});
});
});
it('should extract the bolds', function() {
expect(cheatsheetItemParser('`abc`|`bold1`|`bold2`')).toEqual({
syntax: 'abc',
bold: ['bold1', 'bold2'],
description: ''
describe('with language targets', function() {
it('should extract the active language', function() {
expect(cheatsheetItemParser('syntax(ts):\n`abc`|`bold1`|`bold2`\ndescription(ts):\nsome description')).toEqual({
syntax: 'abc',
bold: ['bold1', 'bold2'],
description: 'some description'
});
});
it('should ignore the non-active language', function() {
expect(cheatsheetItemParser('syntax(js):\n`abc`|`bold1`|`bold2`\ndescription(js):\nsome description')).toEqual({
syntax: '',
bold: [],
description: ''
});
});
it('should select the active language and ignore non-active language', function() {
expect(cheatsheetItemParser(
'syntax(js):\n`JS`|`boldJS``\n' +
'syntax(ts):\n`TS`|`boldTS`\n' +
'description(js):\nJS description\n' +
'description(ts):\nTS description')).toEqual({
syntax: 'TS',
bold: ['boldTS'],
description: 'TS description'
});
});
it('should error if a language target is used that is not allowed', function() {
expect(function() {
cheatsheetItemParser('syntax(dart):\n`abc`|`bold1`|`bold2`\ndescription(ts):\nsome description');
}).toThrowError('Error accessing target "dart". It is not in the list of allowed targets: js,ts');
});
});
it('should extract the description', function() {
expect(cheatsheetItemParser('`abc`|`bold1`|`bold2`some description')).toEqual({
syntax: 'abc',
bold: ['bold1', 'bold2'],
description: 'some description'
});
});
it('should allow bold to be optional', function() {
expect(cheatsheetItemParser('`abc`some description')).toEqual({
syntax: 'abc',
bold: [],
description: 'some description'
});
});
it('should allow whitespace between the parts', function() {
expect(cheatsheetItemParser('`abc`| `bold1`| `bold2`\n\nsome description')).toEqual({
syntax: 'abc',
bold: ['bold1', 'bold2'],
description: '\n\nsome description'
});
})
});