Skip to content

Commit

Permalink
Allow extraction from a single section (#27)
Browse files Browse the repository at this point in the history
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`.
  • Loading branch information
earldouglas authored Dec 25, 2023
1 parent f4e37e7 commit 0324d1c
Show file tree
Hide file tree
Showing 5 changed files with 207 additions and 45 deletions.
101 changes: 71 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 <lang>
ex: codedown haskell
Usage: codedown <lang> [...]
Options:
--separator <separator line>
--section <section number>
Example:
cat README.md | codedown haskell --separator=----- --section 1.3
```

Codedown reads Markdown from stin, extracts the code blocks designated
as language `<lang>`, and outputs them to stdout.
as language `<lang>`, 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
Expand All @@ -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:

Expand All @@ -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:*

Expand All @@ -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*

Expand All @@ -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/
23 changes: 18 additions & 5 deletions codedown.js
Original file line number Diff line number Diff line change
@@ -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) {

Expand All @@ -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 <lang> [<separator>]');
console.log('ex: codedown haskell');
console.log('Usage: codedown <lang> [...]');
console.log('');
console.log('Options:');
console.log('--separator <separator line>');
console.log('--section <section number>');
console.log('');
console.log('Example:');
console.log('cat README.md | codedown haskell --separator=----- --section 1.3');
}
34 changes: 30 additions & 4 deletions lib/codedown.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -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; };
Expand All @@ -35,7 +61,7 @@
var output = marked.parse(src);
output = output.replace(/\n+$/g, '');

return output;
return output.substring(separator.length);
};

root.codedown = codedown;
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
93 changes: 87 additions & 6 deletions test/codedown.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,14 @@ describe('codedown', function(){
process.exec('./codedown.js', function (err, stdout, stderr) {
if (!err) {
assert.equal(
[ 'usage: codedown <lang> [<separator>]'
, 'ex: codedown haskell'
[ 'Usage: codedown <lang> [...]'
, ''
, 'Options:'
, '--separator <separator line>'
, '--section <section number>'
, ''
, 'Example:'
, 'cat README.md | codedown haskell --separator=----- --section 1.3'
, ''
].join('\n'),
stdout
Expand Down Expand Up @@ -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')
);
Expand Down

0 comments on commit 0324d1c

Please sign in to comment.