From 0324d1c823047f942b199bad347401bede338fdb Mon Sep 17 00:00:00 2001 From: James Earl Douglas Date: Mon, 25 Dec 2023 14:45:18 -0700 Subject: [PATCH] Allow extraction from a single section (#27) This adds support for a new `--section` argument, allowing the input to be filtered down to a single numbered section. This also adds support for named (rather than positional) arguments. The separator argument is now specified via `--separator`. --- README.md | 101 +++++++++++++++++++++++++++++++++-------------- codedown.js | 23 ++++++++--- lib/codedown.js | 34 ++++++++++++++-- package.json | 1 + test/codedown.js | 93 ++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 207 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 38261b8..81a85d9 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,12 @@ [release-badge]: https://badge.fury.io/js/codedown.svg [release-link]: https://www.npmjs.com/package/codedown -Codedown is a little utility to extract code blocks from Markdown files. +# Codedown -Inspired by [literate Haskell][1], codedown can be used to: +Codedown is a little utility to extract code blocks from Markdown files. +Inspired by [literate +Haskell](https://wiki.haskell.org/Literate_programming), Codedown can be +used to: * Validate the correctness of code embedded in Markdown * Run code embedded in Markdown @@ -21,28 +24,44 @@ Inspired by [literate Haskell][1], codedown can be used to: ## Quicker start -To skip installing codedown locally, [try it online][2]. +To skip installing Codedown locally, [try it +online](https://earldouglas.github.io/codedown/). ## Quick start -Install codedown: +Install Codedown: ``` $ npm install -g codedown ``` -Run codedown: +Run Codedown: ``` -$ codedown -usage: codedown -ex: codedown haskell +Usage: codedown [...] + +Options: +--separator +--section
+ +Example: +cat README.md | codedown haskell --separator=----- --section 1.3 ``` Codedown reads Markdown from stin, extracts the code blocks designated -as language ``, and outputs them to stdout. +as language ``, and outputs them to stdout. The example above +extracts the Haskell code from section 1.3 of this file, and outputs it +with five dashes in between each block: + +``` +x :: Int +x = 42 +----- +main :: IO () +main = putStrLn $ show x +``` -You can pipe the output of codedown to a language interpreter: +We can pipe the output of Codedown to a language interpreter: ``` $ cat README.md | codedown haskell | runhaskell @@ -61,9 +80,11 @@ $ cat README.md | codedown scala | xargs -0 scala -e ## Examples -This readme is a Markdown file, so we can use codedown to extract code +This readme is a Markdown file, so we can use Codedown to extract code from it. +### Variables in different languages + In the following code blocks, let's set `x` to 42 in different languages: @@ -86,9 +107,10 @@ var x = 42; val x = 42 ``` -Now let's print `x` it to stdout in different languages. +### Console output in different languages -This time, the code blocks are nested within an unordered list: +Now let's print `x` it to stdout in different languages. This time, the +code blocks are nested within an unordered list: * *Haskell:* @@ -109,9 +131,37 @@ This time, the code blocks are nested within an unordered list: println(x) ``` +## 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: + +``` +$ cat README.md | codedown haskell --section 1.3 +x :: Int +x = 42 + +main :: IO () +main = putStrLn $ show x +``` + +``` +$ cat README.md | codedown haskell --section 1.3.1 +x :: Int +x = 42 +``` + +``` +$ cat README.md | codedown haskell --section 1.3.2 +main :: IO () +main = putStrLn $ show x +``` + ## Wildcard matching -Codedown can use wildcards to match file paths, which are used by some markdown implementations: +Codedown can use wildcards to match file paths, which are used by some +markdown implementations: * *lib/codedown.js* @@ -126,23 +176,14 @@ var x = 42 ## Separator -If there are multiple code blocks in the same file, you can specify a separator as the third argument: +If there are multiple code blocks in the same file, we can specify a +separator to insert in between them: -```java -System.out.println("hello") ``` - -```java -System.out.println("world") -``` - -``` -$ cat README.md | codedown java ----- -System.out.println("hello") ------ -System.out.println("world") +$ cat README.md | codedown haskell --separator=----- +x :: Int +x = 42 ----- +main :: IO () +main = putStrLn $ show x ``` - -[1]: https://wiki.haskell.org/Literate_programming -[2]: http://earldouglas.github.io/codedown/ diff --git a/codedown.js b/codedown.js index 0dc1f6b..56b984a 100755 --- a/codedown.js +++ b/codedown.js @@ -1,8 +1,14 @@ #!/usr/bin/env node -var marked = require('marked'); var readline = require('readline'); var codedown = require('./lib/codedown.js'); +var arg = require('arg'); + +var args = + arg({ + '--separator': String, + '--section': String, + }); if (process.argv.length >= 3) { @@ -15,12 +21,19 @@ if (process.argv.length >= 3) { source.push(line); }).on('close', function () { var lang = process.argv[2]; - var separator = process.argv[3]; - output = codedown(source.join('\n'), lang, separator); + var separator = args['--separator']; + var section = args['--section']; + output = codedown(source.join('\n'), lang, separator, section); console.log(output); }); } else { - console.log('usage: codedown []'); - console.log('ex: codedown haskell'); + console.log('Usage: codedown [...]'); + console.log(''); + console.log('Options:'); + console.log('--separator '); + console.log('--section
'); + console.log(''); + console.log('Example:'); + console.log('cat README.md | codedown haskell --separator=----- --section 1.3'); } diff --git a/lib/codedown.js b/lib/codedown.js index 17db77f..61d4bfa 100755 --- a/lib/codedown.js +++ b/lib/codedown.js @@ -6,9 +6,13 @@ var readline = root.readline || require('readline'); var minimatch = root.minimatch || require("minimatch"); - var codedown = function(src, lang, separator) { + var codedown = function(src, lang, separator, section) { - separator = separator || ''; + if (separator === undefined) { + separator = ''; + } + + separator = separator + '\n'; var renderer = new marked.Renderer(); @@ -22,9 +26,31 @@ } } + var tracker = []; + + 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; + } + tracker[index] = tracker[index] + 1; + return ''; + }; + renderer.code = function (src, language, escaped) { - return (language && minimatch(language, lang)) ? src + '\n' + separator + '\n' : ''; + + var result = ''; + + if ( + language && minimatch(language, lang) && + (!section || tracker.join('.').startsWith(section)) + ) { + result = separator + src + '\n'; + } + + return result; }; renderer.listitem = function (text) { return text; }; @@ -35,7 +61,7 @@ var output = marked.parse(src); output = output.replace(/\n+$/g, ''); - return output; + return output.substring(separator.length); }; root.codedown = codedown; diff --git a/package.json b/package.json index 9af5cc4..e1cc7dd 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "node": ">= 0.10.0" }, "dependencies": { + "arg": "5.0.2", "marked": "4.3.0", "minimatch": "3.1.2", "readline": "1.3.0" diff --git a/test/codedown.js b/test/codedown.js index 0ef41e0..b3ab15f 100644 --- a/test/codedown.js +++ b/test/codedown.js @@ -7,8 +7,14 @@ describe('codedown', function(){ process.exec('./codedown.js', function (err, stdout, stderr) { if (!err) { assert.equal( - [ 'usage: codedown []' - , 'ex: codedown haskell' + [ 'Usage: codedown [...]' + , '' + , 'Options:' + , '--separator ' + , '--section
' + , '' + , 'Example:' + , 'cat README.md | codedown haskell --separator=----- --section 1.3' , '' ].join('\n'), stdout @@ -55,14 +61,89 @@ describe('codedown', function(){ }); it('should extract code with separator', function (done) { - process.exec('cat README.md | ./codedown.js java -----', function (err, stdout, stderr) { + process.exec('cat README.md | ./codedown.js haskell --separator=-----', function (err, stdout, stderr) { if (!err) { assert.equal( stdout, - [ 'System.out.println("hello")' - , '-----' - , 'System.out.println("world")' + [ 'x :: Int' + , 'x = 42' , '-----' + , 'main :: IO ()' + , 'main = putStrLn $ show x' + , '' + ].join('\n') + ); + done(); + } else { + console.log(stderr); + } + }); + }); + + it('should extract code by section (1)', function (done) { + process.exec('cat README.md | ./codedown.js haskell --section 1', 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 (1.3)', function (done) { + process.exec('cat README.md | ./codedown.js haskell --section 1.3', 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 (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( + stdout, + [ 'x :: Int' + , 'x = 42' + , '' + ].join('\n') + ); + done(); + } else { + console.log(stderr); + } + }); + }); + + it('should extract code by section (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( + stdout, + [ 'main :: IO ()' + , 'main = putStrLn $ show x' , '' ].join('\n') );