Skip to content

Commit

Permalink
support multiline CoffeeScript comments
Browse files Browse the repository at this point in the history
  • Loading branch information
davidchambers committed Jan 25, 2024
1 parent 45b327b commit 0cbdc1d
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 42 deletions.
76 changes: 48 additions & 28 deletions lib/doctest.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,12 @@ const rewriteJs = sourceType => ({
if (type === 'Line') {
maybePushLine (value, start, loc.start.line);
} else {
let offset = start;
let number = loc.start.line;
for (const text of value.split ('\n')) {
maybePushLine (text.replace (/^\s*[*]/, ''), offset, number);
offset += '\n'.length;
number += 1;
for (let from = 0, number = loc.start.line; ; number += 1) {
const to = value.indexOf ('\n', from);
const text = to < 0 ? value.slice (from) : value.slice (from, to);
maybePushLine (text.replace (/^\s*[*]/, ''), start + from, number);
if (to < 0) break;
from = to + '\n'.length;
}
}
}
Expand Down Expand Up @@ -198,41 +198,61 @@ const rewriteJs = sourceType => ({
};

const rewriteCoffee = ({
prefix: _prefix,
prefix,
openingDelimiter,
closingDelimiter,
}) => input => {
// 1a: Extract prefixed comment lines
const lines = [];
// 1b: Preserve other lines
// 1: Lex source text to extract comments
const tokens = CoffeeScript.tokens (input);
const comments = CoffeeScript.helpers.extractAllCommentTokens (tokens);

// 2: Preserve source text between comments
const chunks = [];
{
const prefix = '#' + _prefix;
let number = 0;
for (const [text, indent, rest] of input.matchAll (/^([ \t]*)(.*)\n?/gm)) {
number += 1;
if (rest.startsWith (prefix)) {
const unprefixed = (rest.slice (prefix.length)).trimStart ();
const line = Line (number) (unprefixed);
lines.push ([indent, line]);
} else {
chunks.push ([number, text]);
let offset = 0;
for (const {locationData: {range: [start, end]}} of comments) {
chunks.push ([offset, input.slice (offset, start)]);
offset = end;
}
chunks.push ([offset, input.slice (offset)]);
}

// 3: Extract prefixed comment lines
const lines = [];
for (const {content, locationData} of comments) {
const indent = ' '.repeat (locationData.first_column);
const offset = locationData.range[0];
let number = locationData.first_line + 1;
if (locationData.last_line > locationData.first_line) {
for (let from = 0; ; number += 1) {
const to = content.indexOf ('\n', from);
const text = to < 0 ? content.slice (from) : content.slice (from, to);
const line = Line (number) (text.trimStart ());
lines.push ([offset + from, indent, line]);
if (to < 0) break;
from = to + '\n'.length;
}
} else {
const text = content.trimStart ();
if (text.startsWith (prefix)) {
const unprefixed = (text.slice (prefix.length)).trimStart ();
lines.push ([offset, indent, Line (number) (unprefixed)]);
}
}
}

// 2: Coalesce related input and output lines
// 4: Coalesce related input and output lines
const tests = [];
{
let test;
let state = openingDelimiter == null ? 'open' : 'closed';
for (const [indent, line] of lines) {
for (const [offset, indent, line] of lines) {
if (state === 'closed') {
if (line.text === openingDelimiter) state = 'open';
} else if (line.text === closingDelimiter) {
state = 'closed';
} else if (line.text.startsWith ('>')) {
tests.push ([line.number, test = {indent, input: {lines: [line]}}]);
tests.push ([offset, test = {indent, input: {lines: [line]}}]);
state = 'input';
} else if (line.text.startsWith ('.')) {
test[state].lines.push (line);
Expand All @@ -249,15 +269,15 @@ const rewriteCoffee = ({
}
}

// 3: Convert doctests to source text
for (const [number, test] of tests) {
chunks.push ([number, wrapCoffee (test)]);
// 5: Convert doctests to source text
for (const [offset, test] of tests) {
chunks.push ([offset, wrapCoffee (test)]);
}

// 4: Sort verbatim and generated source text by original line numbers
// 6: Sort verbatim and generated source text by original offsets
chunks.sort (([a], [b]) => a - b);

// 5: Concatenate source text
// 7: Concatenate source text
let sourceText = '';
for (const [, text] of chunks) sourceText += text;
return CoffeeScript.compile (sourceText);
Expand Down
28 changes: 14 additions & 14 deletions test/shared/index.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -104,20 +104,20 @@ do ->
# ..... .5
# 1234.5

23: 'TODO: multiline comment'
#
# > 3 ** 3 - 2 ** 2
# 23
#

24: 'TODO: multiline comment with wrapped input'
#
# > (["foo", "bar", "baz"]
# . .slice(0, -1)
# . .join(" ")
# . .toUpperCase())
# "FOO BAR"
#
23: 'multiline comment'
###
> 3 ** 3 - 2 ** 2
23
###

24: 'multiline comment with wrapped input'
###
> (["foo", "bar", "baz"]
. .slice(0, -1)
. .join(" ")
. .toUpperCase())
"FOO BAR"
###



Expand Down

0 comments on commit 0cbdc1d

Please sign in to comment.