diff --git a/public/doc-shredder/_test/gulpfile.js b/public/doc-shredder/_test/gulpfile.js
index 8a8d0e2f84..24db0bdc8a 100644
--- a/public/doc-shredder/_test/gulpfile.js
+++ b/public/doc-shredder/_test/gulpfile.js
@@ -7,13 +7,13 @@ var taskListing = require('gulp-task-listing');
var docShredder = require('../doc-shredder');
var shredOptions = {
- examplesDir: "test_source",
+ examplesDir: "test_source/foo",
fragmentsDir: "test_fragments"
};
gulp.task('help', taskListing);
-gulp.task('shred', function() {
+gulp.task('shred', ['clean'], function() {
return docShredder.shred(shredOptions);
});
diff --git a/public/doc-shredder/_test/test_source/foo/main2.ts b/public/doc-shredder/_test/test_source/foo/main2.ts
new file mode 100644
index 0000000000..b33b2a30c5
--- /dev/null
+++ b/public/doc-shredder/_test/test_source/foo/main2.ts
@@ -0,0 +1,18 @@
+// #docregion import,twoparts
+import {Component, View, bootstrap} from 'angular2/angular2';
+// #enddocregion twoparts, import
+
+@Component({
+ selector: 'my-app'
+})
+@View({
+ template: '
My first Angular 2 App
'
+})
+class AppComponent {
+}
+
+// #docregion bootstrap, twoparts
+bootstrap(AppComponent);
+// #enddocregion twoparts
+// to be included in bootstrap...
+// #enddocregion
diff --git a/public/doc-shredder/regionExtractor.js b/public/doc-shredder/regionExtractor.js
index e01bc718e0..01ead64c9b 100644
--- a/public/doc-shredder/regionExtractor.js
+++ b/public/doc-shredder/regionExtractor.js
@@ -1,3 +1,5 @@
+var _ = require('lodash');
+
module.exports = function regionExtractor() {
var nullLine = '###';
@@ -10,37 +12,82 @@ module.exports = function regionExtractor() {
// 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.
+
return function(content, commentPrefixes) {
var lines = result = content.split(/\r?\n/);
- var docs = [];
- var docStack = [];
- var doc = null;
+ var docStack = [];
+ var docMap = {};
+ var doc;
+ var regionNames;
lines.forEach(function(line, ix) {
if (isCommentLine(line, commentPrefixes)) {
if (hasRegionTag(line)) {
- if (doc) docStack.push(doc);
- doc = {startIx: ix, regionName: getRegionName(line)};
lines[ix] = nullLine;
- docs.push(doc);
+
+ 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)) {
- if (doc) {
- lines[ix] = nullLine;
- doc.endIx = ix;
- doc = docStack.pop();
- }
+ 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;
+ });
+ }
+ }
+
+ })
}
}
});
+ var docs = _.values(docMap);
docs.forEach(function(doc) {
- var fragLines, content;
- if (doc.endIx) {
- fragLines = lines.slice(doc.startIx + 1, doc.endIx);
- } else {
- fragLines = lines.slice(doc.startIx + 1);
- }
+ 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);
+ }
+ fragLines = fragLines.concat(subLines);
+ });
fragLines = trimLeftIndent(fragLines);
content = fragLines.join('\n');
// eliminate all #docregion lines
@@ -94,13 +141,23 @@ function hasEndRegionTag(line) {
return line.indexOf("#enddocregion") >= 0;
}
-function getRegionName(line) {
+function getRegionNames(line) {
+ return extractRegionNames(line, /#docregion\s*(\S.*)/);
+}
+
+function getEndRegionNames(line) {
+ return extractRegionNames(line, /#enddocregion\s*(\S.*)/);
+}
+
+function extractRegionNames(line, rx) {
try {
- var name = line.match(/#docregion\s*(\S*).*/)[1];
+ var names = line.match(rx)[1];
+ names = names.replace(/\s*/g,'');
// Hack for html regions that look like or */
- name = name.replace("-->","").replace('\*\/',"");
- return name;
+ names = names.replace("-->","").replace('\*\/',"");
+ names = names.split(',');
+ return names;
} catch (e) {
- return '';
+ return [''];
}
}
diff --git a/public/doc-shredder/regionExtractor.old.js b/public/doc-shredder/regionExtractor.old.js
new file mode 100644
index 0000000000..e01bc718e0
--- /dev/null
+++ b/public/doc-shredder/regionExtractor.old.js
@@ -0,0 +1,106 @@
+module.exports = function regionExtractor() {
+
+ var nullLine = '###';
+ var nullLinePattern = new RegExp(nullLine + '\n', 'g');
+
+ // 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.
+ return function(content, commentPrefixes) {
+
+ var lines = result = content.split(/\r?\n/);
+ var docs = [];
+ var docStack = [];
+ var doc = null;
+
+ lines.forEach(function(line, ix) {
+ if (isCommentLine(line, commentPrefixes)) {
+ if (hasRegionTag(line)) {
+ if (doc) docStack.push(doc);
+ doc = {startIx: ix, regionName: getRegionName(line)};
+ lines[ix] = nullLine;
+ docs.push(doc);
+ } else if (hasEndRegionTag(line)) {
+ if (doc) {
+ lines[ix] = nullLine;
+ doc.endIx = ix;
+ doc = docStack.pop();
+ }
+ }
+ }
+ });
+
+ docs.forEach(function(doc) {
+ var fragLines, content;
+ if (doc.endIx) {
+ fragLines = lines.slice(doc.startIx + 1, doc.endIx);
+ } else {
+ fragLines = lines.slice(doc.startIx + 1);
+ }
+ fragLines = trimLeftIndent(fragLines);
+ content = fragLines.join('\n');
+ // eliminate all #docregion lines
+ content = content.replace(nullLinePattern, '');
+ if (content.substr(-3) === nullLine) {
+ content = content.substr(0, content.length-3);
+ }
+ doc.content = content;
+ });
+ return docs;
+ }
+
+};
+
+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 isCommentLine(line, commentPrefixes) {
+ return commentPrefixes.some(function(prefix) {
+ return line.trim().indexOf(prefix) == 0;
+ });
+}
+
+function hasRegionTag(line) {
+ return line.indexOf("#docregion") >= 0;
+}
+
+function hasEndRegionTag(line) {
+ return line.indexOf("#enddocregion") >= 0;
+}
+
+function getRegionName(line) {
+ try {
+ var name = line.match(/#docregion\s*(\S*).*/)[1];
+ // Hack for html regions that look like or */
+ name = name.replace("-->","").replace('\*\/',"");
+ return name;
+ } catch (e) {
+ return '';
+ }
+}