diff --git a/README.md b/README.md index 1fdbaeb..5fc86b6 100644 --- a/README.md +++ b/README.md @@ -134,8 +134,8 @@ code blocks are nested within an unordered list: ## Sections and subsections The section above is 1.3, counting by headings. It has two subsections -(1.3.1 and 1.3.2). We can specify a section to extract the content from -just that section: +(1.3.1 and 1.3.2). We can specify a section number to extract the +content from just that section: ``` $ cat README.md | codedown haskell --section 1.3 @@ -158,6 +158,14 @@ main :: IO () main = putStrLn $ show x ``` +We can also specify a section by heading: + +``` +cat README.md | ./codedown.js haskell --section '### Variables in different languages' +x :: Int +x = 42 +``` + ## Wildcard matching Codedown can use wildcards to match file paths, which are used by some diff --git a/lib/codedown.js b/lib/codedown.js index 61d4bfa..b1d50dc 100755 --- a/lib/codedown.js +++ b/lib/codedown.js @@ -6,7 +6,7 @@ var readline = root.readline || require('readline'); var minimatch = root.minimatch || require("minimatch"); - var codedown = function(src, lang, separator, section) { + var codedown = function(src, lang, separator, targetSection) { if (separator === undefined) { separator = ''; @@ -26,15 +26,43 @@ } } - var tracker = []; + var currentSectionNumber = []; + var currentSectionName = null; + var sectionNumbers = {}; + + var sectionNumberMatches = + function () { + return currentSectionNumber.join('.').startsWith(targetSection); + }; + + var sectionNameMatches = + function () { + return sectionNumbers[targetSection] && + sectionNumbers[currentSectionName].startsWith(sectionNumbers[targetSection]); + }; + + var sectionMatches = + function () { + return !targetSection || sectionNumberMatches() || sectionNameMatches(); + }; + + var languageMatches = + function (language) { + return language && minimatch(language, lang); + }; renderer.heading = function(text, level, raw) { var index = level - 1; // 0-based indexing for (var i = 0; i <= index; i++) { - tracker[i] = tracker[i] || 0; + currentSectionNumber[i] = currentSectionNumber[i] || 0; } - tracker[index] = tracker[index] + 1; + currentSectionNumber[index] = currentSectionNumber[index] + 1; + currentSectionNumber.splice(level); + + currentSectionName = ('#'.repeat(level) + ' ' + raw).trim(); + sectionNumbers[currentSectionName] = currentSectionNumber.join('.'); + return ''; }; @@ -43,10 +71,7 @@ var result = ''; - if ( - language && minimatch(language, lang) && - (!section || tracker.join('.').startsWith(section)) - ) { + if (languageMatches(language) && sectionMatches()) { result = separator + src + '\n'; } diff --git a/test/codedown.js b/test/codedown.js index b3ab15f..f01f271 100644 --- a/test/codedown.js +++ b/test/codedown.js @@ -80,7 +80,7 @@ describe('codedown', function(){ }); }); - it('should extract code by section (1)', function (done) { + it('should extract code by section number (1)', function (done) { process.exec('cat README.md | ./codedown.js haskell --section 1', function (err, stdout, stderr) { if (!err) { assert.equal( @@ -100,7 +100,7 @@ describe('codedown', function(){ }); }); - it('should extract code by section (1.3)', function (done) { + it('should extract code by section number (1.3)', function (done) { process.exec('cat README.md | ./codedown.js haskell --section 1.3', function (err, stdout, stderr) { if (!err) { assert.equal( @@ -120,7 +120,7 @@ describe('codedown', function(){ }); }); - it('should extract code by section (1.3.1)', function (done) { + it('should extract code by section number (1.3.1)', function (done) { process.exec('cat README.md | ./codedown.js haskell --section 1.3.1', function (err, stdout, stderr) { if (!err) { assert.equal( @@ -137,7 +137,7 @@ describe('codedown', function(){ }); }); - it('should extract code by section (1.3.2)', function (done) { + it('should extract code by section number (1.3.2)', function (done) { process.exec('cat README.md | ./codedown.js haskell --section 1.3.2', function (err, stdout, stderr) { if (!err) { assert.equal( @@ -154,4 +154,58 @@ describe('codedown', function(){ }); }); + it('should extract code by section name (## Examples)', function (done) { + process.exec('cat README.md | ./codedown.js haskell --section "## Examples"', function (err, stdout, stderr) { + if (!err) { + assert.equal( + stdout, + [ 'x :: Int' + , 'x = 42' + , '' + , 'main :: IO ()' + , 'main = putStrLn $ show x' + , '' + ].join('\n') + ); + done(); + } else { + console.log(stderr); + } + }); + }); + + it('should extract code by section name (### Variables in different languages)', function (done) { + process.exec('cat README.md | ./codedown.js haskell --section "### Variables in different languages"', function (err, stdout, stderr) { + if (!err) { + assert.equal( + stdout, + [ 'x :: Int' + , 'x = 42' + , '' + ].join('\n') + ); + done(); + } else { + console.log(stderr); + } + }); + }); + + it('should extract code by section name (### Console output in different languages)', function (done) { + process.exec('cat README.md | ./codedown.js haskell --section "### Console output in different languages"', function (err, stdout, stderr) { + if (!err) { + assert.equal( + stdout, + [ 'main :: IO ()' + , 'main = putStrLn $ show x' + , '' + ].join('\n') + ); + done(); + } else { + console.log(stderr); + } + }); + }); + });