From 593938011f8545d2cbd8e01d6693ba17a2e58ae7 Mon Sep 17 00:00:00 2001 From: Jay Traband Date: Tue, 13 Oct 2015 19:44:27 -0700 Subject: [PATCH] support for multiple regions with the same name --- public/doc-shredder/_test/gulpfile.js | 4 +- .../_test/test_source/foo/main2.ts | 18 +++ public/doc-shredder/regionExtractor.js | 101 +++++++++++++---- public/doc-shredder/regionExtractor.old.js | 106 ++++++++++++++++++ 4 files changed, 205 insertions(+), 24 deletions(-) create mode 100644 public/doc-shredder/_test/test_source/foo/main2.ts create mode 100644 public/doc-shredder/regionExtractor.old.js 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 ''; + } +}