2015-10-23 15:47:20 -04:00
|
|
|
/**
|
|
|
|
* NOT a dgeni service because we need to be able to use it externally
|
|
|
|
* as well as from dgeni.
|
|
|
|
* @description
|
|
|
|
*
|
|
|
|
*/
|
2015-10-13 22:44:27 -04:00
|
|
|
var _ = require('lodash');
|
|
|
|
|
2015-10-23 15:47:20 -04:00
|
|
|
var nullLine = '###';
|
|
|
|
var nullLinePattern = new RegExp(nullLine + '\n', 'g');
|
2015-08-03 20:45:58 -04:00
|
|
|
|
2015-10-23 15:47:20 -04:00
|
|
|
module.exports = {
|
|
|
|
buildRegionDocs: buildRegionDocs,
|
|
|
|
getRegionDoc: getRegionDoc,
|
|
|
|
removeDocTags: removeDocTags
|
|
|
|
};
|
|
|
|
|
|
|
|
// split out each fragment in {content} into a separate doc
|
|
|
|
// a fragment is a section of text surrounded by
|
|
|
|
// 1) In front: a comment marker followed by '#docregion' followed by an optional region name. For example:
|
|
|
|
// <-- #docregion foo --> for html
|
|
|
|
// or // #docregion foo for js/ts
|
|
|
|
// 2) In back: a comment marker followed by '#enddocregion'
|
|
|
|
// Regions can be nested and any regions not 'closed' are automatically closed at the end of the doc.
|
|
|
|
|
|
|
|
// empty enddocregion always closes last region started.
|
|
|
|
// enddocregions with names that do no match start region tags get ignored.
|
|
|
|
function buildRegionDocs(content, extn) {
|
|
|
|
var commentInfo = getCommentInfo(extn);
|
|
|
|
if (!commentInfo) {
|
|
|
|
return [ { content: content } ];
|
2015-08-03 20:45:58 -04:00
|
|
|
}
|
|
|
|
|
2015-10-23 15:47:20 -04:00
|
|
|
var lines = result = content.split(/\r?\n/);
|
2015-08-03 20:45:58 -04:00
|
|
|
|
2015-10-23 15:47:20 -04:00
|
|
|
var docStack = []; // items will be both popped and removed from the middle
|
|
|
|
var docMap = {};
|
|
|
|
var docPlaster = '. . .';
|
|
|
|
var doc;
|
|
|
|
var regionNames;
|
|
|
|
lines.forEach(function(line, ix) {
|
|
|
|
if (isCommentLine(line, commentInfo.prefix)) {
|
|
|
|
if (hasRegionTag(line)) {
|
|
|
|
lines[ix] = nullLine;
|
|
|
|
regionNames = getRegionNames(line);
|
|
|
|
regionNames.forEach(function(rn) {
|
|
|
|
doc = docMap[rn];
|
|
|
|
if (!doc) {
|
|
|
|
// regionName may be ''
|
|
|
|
doc = {regionName: rn, ranges: [ { startIx: ix} ] };
|
|
|
|
docMap[rn] = doc;
|
|
|
|
} else {
|
|
|
|
// only add a new range if prev range is closed
|
|
|
|
var lastRange = doc.ranges[doc.ranges.length-1];
|
|
|
|
if (lastRange.endIx) {
|
|
|
|
doc.ranges.push({startIx: ix});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
docStack.push(doc);
|
|
|
|
});
|
|
|
|
|
|
|
|
} else if (hasEndRegionTag(line)) {
|
|
|
|
lines[ix] = nullLine;
|
|
|
|
regionNames = getEndRegionNames(line);
|
|
|
|
regionNames.forEach(function(rn) {
|
|
|
|
// handle endregions with no name specially.
|
|
|
|
// They operate on the last region created.
|
|
|
|
if (rn.length == 0) {
|
|
|
|
if (docStack.length) {
|
|
|
|
// update last item on the stack
|
|
|
|
doc = docStack.pop();
|
|
|
|
doc.ranges[doc.ranges.length - 1].endIx = ix;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
doc = docMap[rn];
|
|
|
|
// ignore endregion if name is specified but not found.
|
|
|
|
if (doc) {
|
|
|
|
doc.ranges[doc.ranges.length - 1].endIx = ix;
|
|
|
|
// remove doc from stack
|
|
|
|
_.remove(docStack, function (item) {
|
|
|
|
return item.regionName === rn;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else if (hasDocPlasterTag(line)) {
|
|
|
|
line[ix] = nullLine;
|
|
|
|
docPlaster = getDocPlaster(line);
|
|
|
|
}
|
2015-09-11 04:10:48 -04:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2015-10-23 15:47:20 -04:00
|
|
|
var docs = _.values(docMap);
|
|
|
|
var plasterComment = docPlaster && commentInfo.plasterPattern.replace('{tag}', docPlaster);
|
|
|
|
docs = reattachDocs(docs, lines, plasterComment);
|
|
|
|
return docs;
|
|
|
|
}
|
|
|
|
|
|
|
|
function getRegionDoc(content, extn, regionName) {
|
|
|
|
var docs = buildRegionDocs(content, extn);
|
|
|
|
var doc = _.find(docs, function (doc) {
|
|
|
|
return doc.regionName === regionName;
|
|
|
|
});
|
|
|
|
return doc && doc.content;
|
|
|
|
}
|
|
|
|
|
|
|
|
function removeDocTags(content, extn) {
|
|
|
|
var commentInfo = getCommentInfo(extn);
|
|
|
|
if (commentInfo == null) {
|
|
|
|
return content;
|
|
|
|
}
|
|
|
|
var lines = result = content.split(/\r?\n/);
|
|
|
|
|
|
|
|
lines.forEach(function(line, ix) {
|
|
|
|
if (isCommentLine(line, commentInfo.prefix)) {
|
|
|
|
if (hasDocTag(line)) {
|
|
|
|
lines[ix] = nullLine;
|
|
|
|
}
|
2015-09-11 04:10:48 -04:00
|
|
|
}
|
|
|
|
});
|
2015-10-23 15:47:20 -04:00
|
|
|
var result = joinLines(lines);
|
2015-09-11 04:10:48 -04:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2015-10-23 15:47:20 -04:00
|
|
|
function reattachDocs(docs, lines, plasterComment) {
|
|
|
|
|
|
|
|
docs.forEach(function(doc) {
|
|
|
|
var content;
|
|
|
|
var fragLines = [];
|
|
|
|
doc.ranges.forEach(function (range) {
|
|
|
|
var subLines;
|
|
|
|
if (range.endIx) {
|
|
|
|
subLines = lines.slice(range.startIx + 1, range.endIx);
|
|
|
|
} else {
|
|
|
|
subLines = lines.slice(range.startIx + 1);
|
|
|
|
}
|
|
|
|
if (plasterComment && fragLines.length) {
|
|
|
|
// pad is the padding on the previous line
|
|
|
|
var pad = fragLines[fragLines.length - 1].match(/(\s*)/)[0];
|
|
|
|
fragLines.push(pad + plasterComment);
|
|
|
|
}
|
|
|
|
fragLines = fragLines.concat(subLines);
|
|
|
|
});
|
|
|
|
fragLines = trimLeftIndent(fragLines);
|
|
|
|
doc.content = joinLines(fragLines);
|
|
|
|
});
|
|
|
|
return docs;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getCommentInfo(extension) {
|
|
|
|
var commentInfo;
|
|
|
|
switch (extension) {
|
|
|
|
case 'ts':
|
|
|
|
case 'js':
|
|
|
|
case 'dart':
|
|
|
|
commentInfo = {
|
|
|
|
prefix: '//',
|
|
|
|
plasterPattern: '/* {tag} */'
|
|
|
|
};
|
|
|
|
break;
|
|
|
|
case 'html':
|
|
|
|
commentInfo = {
|
|
|
|
prefix: '<!--',
|
|
|
|
plasterPattern: '<!-- {tag} -->'
|
|
|
|
};
|
|
|
|
break;
|
|
|
|
case 'css':
|
|
|
|
commentInfo = {
|
|
|
|
prefix: '/*',
|
|
|
|
plasterPattern: '/* {tag} */'
|
|
|
|
};
|
|
|
|
break;
|
|
|
|
case 'json':
|
|
|
|
return null;
|
|
|
|
case 'yaml':
|
|
|
|
commentInfo = {
|
|
|
|
prefix: '#',
|
|
|
|
plasterPattern: '# {tag} '
|
|
|
|
};
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return commentInfo;
|
|
|
|
}
|
|
|
|
|
2015-10-14 15:47:22 -04:00
|
|
|
function isCommentLine(line, commentPrefix) {
|
|
|
|
return line.trim().indexOf(commentPrefix) == 0;
|
2015-08-03 20:45:58 -04:00
|
|
|
}
|
|
|
|
|
2015-10-23 15:47:20 -04:00
|
|
|
function hasDocTag(line) {
|
|
|
|
return hasRegionTag(line) || hasEndRegionTag(line) || hasDocPlasterTag(line);
|
|
|
|
}
|
|
|
|
|
2015-08-03 20:45:58 -04:00
|
|
|
function hasRegionTag(line) {
|
|
|
|
return line.indexOf("#docregion") >= 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
function hasEndRegionTag(line) {
|
|
|
|
return line.indexOf("#enddocregion") >= 0;
|
|
|
|
}
|
|
|
|
|
2015-10-14 15:47:22 -04:00
|
|
|
function hasDocPlasterTag(line) {
|
|
|
|
return line.indexOf("#docplaster") >= 0;
|
|
|
|
}
|
|
|
|
|
2015-10-13 22:44:27 -04:00
|
|
|
function getRegionNames(line) {
|
|
|
|
return extractRegionNames(line, /#docregion\s*(\S.*)/);
|
|
|
|
}
|
|
|
|
|
|
|
|
function getEndRegionNames(line) {
|
|
|
|
return extractRegionNames(line, /#enddocregion\s*(\S.*)/);
|
|
|
|
}
|
|
|
|
|
2015-10-14 15:47:22 -04:00
|
|
|
function getDocPlaster(line) {
|
|
|
|
var rx = /#docplaster\s*(\S.*)/;
|
|
|
|
try {
|
|
|
|
var plaster = line.match(rx)[1];
|
2015-10-20 19:51:36 -04:00
|
|
|
plaster = plaster.replace("-->","").replace('\*\/',"");
|
2015-10-14 15:47:22 -04:00
|
|
|
return plaster.trim();
|
|
|
|
} catch (e) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-13 22:44:27 -04:00
|
|
|
function extractRegionNames(line, rx) {
|
2015-08-03 20:45:58 -04:00
|
|
|
try {
|
2015-10-13 22:44:27 -04:00
|
|
|
var names = line.match(rx)[1];
|
|
|
|
names = names.replace(/\s*/g,'');
|
2015-08-03 20:45:58 -04:00
|
|
|
// Hack for html regions that look like <!-- #docregion --> or */
|
2015-10-13 22:44:27 -04:00
|
|
|
names = names.replace("-->","").replace('\*\/',"");
|
|
|
|
names = names.split(',');
|
|
|
|
return names;
|
2015-08-03 20:45:58 -04:00
|
|
|
} catch (e) {
|
2015-10-13 22:44:27 -04:00
|
|
|
return [''];
|
2015-08-03 20:45:58 -04:00
|
|
|
}
|
|
|
|
}
|
2015-10-23 15:47:20 -04:00
|
|
|
|
|
|
|
function trimLeftIndent(lines) {
|
|
|
|
var minIx = 100;
|
|
|
|
var ok = lines.every(function(line) {
|
|
|
|
// var ix = line.search(/\S/);
|
|
|
|
var ix = line.search(/[^ ]/);
|
|
|
|
if (ix === 0) return false;
|
|
|
|
if (ix === -1) return true;
|
|
|
|
if (ix > 0) {
|
|
|
|
minIx = Math.min(minIx, ix);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
if ( (!ok) || minIx === 100) return lines;
|
|
|
|
|
|
|
|
var result = lines.map(function(line) {
|
|
|
|
if (line.length > minIx) {
|
|
|
|
return line.substr(minIx);
|
|
|
|
} else {
|
|
|
|
// this can happen if line is all blanks and shorter than mixIx
|
|
|
|
return line;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
function joinLines(lines) {
|
|
|
|
var content = lines.join('\n');
|
|
|
|
// eliminate all #docregion lines
|
|
|
|
content = content.replace(nullLinePattern, '');
|
|
|
|
if (content.substr(-3) === nullLine) {
|
|
|
|
content = content.substr(0, content.length - 3);
|
|
|
|
}
|
|
|
|
return content;
|
|
|
|
}
|