1
0
Fork 0
mirror of https://github.com/jashkenas/coffeescript.git synced 2022-11-09 12:23:24 -05:00

[CS2] Literate CoffeeScript without dependencies (#4509)

* Reimplement `invertLiterate` without any dependency, tracking indentation levels (including inside lists); update literate test files to also check that no tests are skipped

* Drop Literate CoffeeScript’s support for executable code blocks inside list items (and also for second paragraphs or blockquotes in list items)

* Update Literate CoffeeScript docs to reflect current supported syntax
This commit is contained in:
Geoffrey Booth 2017-04-17 23:30:40 -07:00 committed by GitHub
parent ae096a331a
commit 0e8feb7ee9
8 changed files with 110 additions and 131 deletions

View file

@ -122,14 +122,6 @@ task 'build:browser', 'merge the built scripts into a single file for use in a b
return #{fs.readFileSync "./package.json"};
})();
"""
for {name, src} in [{name: 'markdown-it', src: 'dist/markdown-it.min.js'}]
code += """
require['#{name}'] = (function() {
var exports = {}, module = {exports: exports};
#{fs.readFileSync "node_modules/#{name}/#{src}"}
return module.exports;
})();
"""
for name in ['helpers', 'rewriter', 'lexer', 'parser', 'scope', 'nodes', 'sourcemap', 'coffeescript', 'browser']
code += """
require['./#{name}'] = (function() {

View file

@ -3009,8 +3009,14 @@ Object.defineProperty(screen, 'height', {
</section>
<section id="literate">
<h2>Literate CoffeeScript</h2>
<p>Besides being used as an ordinary programming language, CoffeeScript may also be written in “literate” mode. If you name your file with a <code>.litcoffee</code> extension, you can write it as a Markdown document — a document that also happens to be executable CoffeeScript code. The compiler will treat any indented blocks (Markdowns way of indicating source code) as code, and ignore the rest as comments.</p>
<p>Besides being used as an ordinary programming language, CoffeeScript may also be written in “literate” mode. If you name your file with a <code>.litcoffee</code> extension, you can write it as a Markdown document — a document that also happens to be executable CoffeeScript code. The compiler will treat any indented blocks (Markdowns way of indicating source code) as executable code, and ignore the rest as comments. Code blocks must also be separated from comments by at least one blank line.</p>
<p>Just for kicks, a little bit of the compiler is currently implemented in this fashion: See it <a href="https://gist.github.com/jashkenas/3fc3c1a8b1009c00d9df">as a document</a>, <a href="https://raw.githubusercontent.com/jashkenas/coffeescript/master/src/scope.litcoffee">raw</a>, and <a href="http://cl.ly/LxEu">properly highlighted in a text editor</a>.</p>
<p>A few caveats:</p>
<ul>
<li>Code blocks need to maintain consistent indentation relative to each other. When the compiler parses your Literate CoffeeScript file, it first discards all the non-code block lines and then parses the remainder as a regular CoffeeScript file. Therefore the code blocks need to be written as if the comment lines dont exist, with consistent indentation (including whether they are indented with tabs or spaces).</li>
<li>Along those lines, code blocks within list items or blockquotes are not treated as executable code. Since list items and blockquotes imply their own indentation, it would be ambiguous how to treat indentation between successive code blocks when some are within these other blocks and some are not.</li>
<li>List items can be at most only one paragraph long. The second paragraph of a list item would be indented after a blank line, and therefore indistinguishable from a code block.</li>
</ul>
</section>
<section id="source-maps">

View file

@ -1,5 +1,11 @@
## Literate CoffeeScript
Besides being used as an ordinary programming language, CoffeeScript may also be written in “literate” mode. If you name your file with a `.litcoffee` extension, you can write it as a Markdown document — a document that also happens to be executable CoffeeScript code. The compiler will treat any indented blocks (Markdowns way of indicating source code) as code, and ignore the rest as comments.
Besides being used as an ordinary programming language, CoffeeScript may also be written in “literate” mode. If you name your file with a `.litcoffee` extension, you can write it as a Markdown document — a document that also happens to be executable CoffeeScript code. The compiler will treat any indented blocks (Markdowns way of indicating source code) as executable code, and ignore the rest as comments. Code blocks must also be separated from comments by at least one blank line.
Just for kicks, a little bit of the compiler is currently implemented in this fashion: See it [as a document](https://gist.github.com/jashkenas/3fc3c1a8b1009c00d9df), [raw](https://raw.githubusercontent.com/jashkenas/coffeescript/master/src/scope.litcoffee), and [properly highlighted in a text editor](http://cl.ly/LxEu).
A few caveats:
* Code blocks need to maintain consistent indentation relative to each other. When the compiler parses your Literate CoffeeScript file, it first discards all the non-code block lines and then parses the remainder as a regular CoffeeScript file. Therefore the code blocks need to be written as if the comment lines dont exist, with consistent indentation (including whether they are indented with tabs or spaces).
* Along those lines, code blocks within list items or blockquotes are not treated as executable code. Since list items and blockquotes imply their own indentation, it would be ambiguous how to treat indentation between successive code blocks when some are within these other blocks and some are not.
* List items can be at most only one paragraph long. The second paragraph of a list item would be indented after a blank line, and therefore indistinguishable from a code block.

View file

@ -1,8 +1,6 @@
// Generated by CoffeeScript 2.0.0-beta1
(function() {
var buildLocationData, extend, flatten, md, ref, repeat, syntaxErrorToString;
md = require('markdown-it')();
var buildLocationData, extend, flatten, ref, repeat, syntaxErrorToString;
exports.starts = function(string, literal, start) {
return literal === string.substr(start, literal.length);
@ -28,10 +26,10 @@
};
exports.compact = function(array) {
var item, j, len1, results;
var i, item, len1, results;
results = [];
for (j = 0, len1 = array.length; j < len1; j++) {
item = array[j];
for (i = 0, len1 = array.length; i < len1; i++) {
item = array[i];
if (item) {
results.push(item);
}
@ -65,10 +63,10 @@
};
exports.flatten = flatten = function(array) {
var element, flattened, j, len1;
var element, flattened, i, len1;
flattened = [];
for (j = 0, len1 = array.length; j < len1; j++) {
element = array[j];
for (i = 0, len1 = array.length; i < len1; i++) {
element = array[i];
if ('[object Array]' === Object.prototype.toString.call(element)) {
flattened = flattened.concat(flatten(element));
} else {
@ -86,10 +84,10 @@
};
exports.some = (ref = Array.prototype.some) != null ? ref : function(fn) {
var e, j, len1, ref1;
var e, i, len1, ref1;
ref1 = this;
for (j = 0, len1 = ref1.length; j < len1; j++) {
e = ref1[j];
for (i = 0, len1 = ref1.length; i < len1; i++) {
e = ref1[i];
if (fn(e)) {
return true;
}
@ -98,22 +96,28 @@
};
exports.invertLiterate = function(code) {
var out;
var blankLine, i, indented, insideComment, len1, line, listItemStart, out, ref1;
out = [];
md.renderer.rules = {
code_block: function(tokens, idx, options, env, slf) {
var i, j, len1, line, lines, results, startLine;
startLine = tokens[idx].map[0];
lines = tokens[idx].content.split('\n');
results = [];
for (i = j = 0, len1 = lines.length; j < len1; i = ++j) {
line = lines[i];
results.push(out[startLine + i] = line);
}
return results;
blankLine = /^\s*$/;
indented = /^[\t ]/;
listItemStart = /^(?:\t?| {0,3})(?:[\*\-\+]|[0-9]{1,9}\.)[ \t]/;
insideComment = false;
ref1 = code.split('\n');
for (i = 0, len1 = ref1.length; i < len1; i++) {
line = ref1[i];
if (blankLine.test(line)) {
insideComment = false;
out.push(line);
} else if (insideComment || listItemStart.test(line)) {
insideComment = true;
out.push(`# ${line}`);
} else if (!insideComment && indented.test(line)) {
out.push(line);
} else {
insideComment = true;
out.push(`# ${line}`);
}
};
md.render(code);
}
return out.join('\n');
};

View file

@ -43,9 +43,8 @@
"google-closure-compiler-js": "^20170409.0.0",
"highlight.js": "~9.10.0",
"jison": ">=0.4.17",
"markdown-it": "^8.3.1",
"underscore": "~1.8.3"
},
"dependencies": {
"markdown-it": "^8.3.1"
}
"dependencies": {}
}

View file

@ -2,8 +2,6 @@
# the **Lexer**, **Rewriter**, and the **Nodes**. Merge objects, flatten
# arrays, count characters, that sort of thing.
md = require('markdown-it')()
# Peek at the beginning of a given string to see if it matches a sequence.
exports.starts = (string, literal, start) ->
literal is string.substr start, literal.length
@ -69,20 +67,34 @@ exports.some = Array::some ? (fn) ->
return true for e in this when fn e
false
# Simple function for extracting code from Literate CoffeeScript by stripping
# Helper function for extracting code from Literate CoffeeScript by stripping
# out all non-code blocks, producing a string of CoffeeScript code that can
# be compiled normally. Uses [MarkdownIt](https://markdown-it.github.io/)
# to tell the difference between Markdown and code blocks.
# be compiled normally.
exports.invertLiterate = (code) ->
out = []
md.renderer.rules =
# Delete all other rules, since all we want are the code blocks.
code_block: (tokens, idx, options, env, slf) ->
startLine = tokens[idx].map[0]
lines = tokens[idx].content.split '\n'
for line, i in lines
out[startLine + i] = line
md.render code
blankLine = /^\s*$/
indented = /^[\t ]/
listItemStart = /// ^
(?:\t?|\ {0,3}) # Up to one tab, or up to three spaces, or neither;
(?:
[\*\-\+] | # followed by `*`, `-` or `+`;
[0-9]{1,9}\. # or by an integer up to 9 digits long, followed by a period;
)
[\ \t] # followed by a space or a tab.
///
insideComment = no
for line in code.split('\n')
if blankLine.test(line)
insideComment = no
out.push line
else if insideComment or listItemStart.test(line)
insideComment = yes
out.push "# #{line}"
else if not insideComment and indented.test(line)
out.push line
else
insideComment = yes
out.push "# #{line}"
out.join '\n'
# Merge two jison-style location data objects together.

View file

@ -2,8 +2,11 @@
comment comment
testsCount = 0 # Track the number of tests run in this file, to make sure they all run
test "basic literate CoffeeScript parsing", ->
ok yes
testsCount++
now with a...
@ -16,6 +19,7 @@ now with a...
... nested block.
ok yes
testsCount++
Code must be separated from text by a blank line.
@ -25,6 +29,7 @@ The next line is part of the text and will not be executed.
fail()
ok yes
testsCount++
Code in `backticks is not parsed` and...
@ -38,6 +43,7 @@ Code in `backticks is not parsed` and...
###
ok yes
testsCount++
Regular [Markdown](http://example.com/markdown) features, like links
and unordered lists, are fine:
@ -50,11 +56,6 @@ and unordered lists, are fine:
* List
Tabs work too:
test "tabbed code", ->
ok yes
---
# keep track of whether code blocks are executed or not
@ -69,13 +70,15 @@ if true
test "should ignore code blocks inside HTML", ->
eq executed, false
testsCount++
---
* A list item with a code block:
* A list item followed by a code block:
test "basic literate CoffeeScript parsing", ->
ok yes
test "basic literate CoffeeScript parsing", ->
ok yes
testsCount++
---
@ -88,37 +91,6 @@ if true
---
1. This is a list item with two paragraphs. Lorem ipsum dolor
sit amet, consectetuer adipiscing elit. Aliquam hendrerit
mi posuere lectus.
Vestibulum enim wisi, viverra nec, fringilla in, laoreet
vitae, risus. Donec sit amet nisl. Aliquam semper ipsum
sit amet velit.
2. Suspendisse id sem consectetuer libero luctus adipiscing.
---
1. This is a list item with two paragraphs. Lorem ipsum dolor
sit amet, consectetuer adipiscing elit. Aliquam hendrerit
mi posuere lectus.
Vestibulum enim wisi, viverra nec, fringilla in, laoreet
vitae, risus. Donec sit amet nisl. Aliquam semper ipsum
sit amet velit.
2. Suspendisse id sem consectetuer libero luctus adipiscing.
---
* A list item with a blockquote:
> This is a blockquote
> inside a list item.
---
This is [an example][id] reference-style link.
[id]: http://example.com/ "Optional Title Here"
@ -128,12 +100,13 @@ This is [an example][id] reference-style link.
1986. What a great season.
executed = yes
executed = yes
and test...
test "should recognize indented code blocks in lists with empty line as separator", ->
ok executed
testsCount++
---
@ -146,6 +119,7 @@ and test...
test "should ignore indented code in escaped list like number", ->
eq executed, no
testsCount++
one last test!
@ -155,3 +129,9 @@ one last test!
and bar!
'''
eq quote, 'foo\n and bar!'
testsCount++
and finally, how did we do?
test "all spaced literate CoffeeScript tests executed", ->
eq testsCount, 9

View file

@ -2,8 +2,11 @@
comment comment
testsCount = 0 # Track the number of tests run in this file, to make sure they all run
test "basic literate CoffeeScript parsing", ->
ok yes
testsCount++
now with a...
@ -16,6 +19,7 @@ now with a...
... nested block.
ok yes
testsCount++
Code must be separated from text by a blank line.
@ -25,6 +29,7 @@ The next line is part of the text and will not be executed.
fail()
ok yes
testsCount++
Code in `backticks is not parsed` and...
@ -38,6 +43,7 @@ Code in `backticks is not parsed` and...
###
ok yes
testsCount++
Regular [Markdown](http://example.com/markdown) features, like links
and unordered lists, are fine:
@ -50,11 +56,6 @@ and unordered lists, are fine:
* List
Spaces work too:
test "spaced code", ->
ok yes
---
# keep track of whether code blocks are executed or not
@ -69,13 +70,15 @@ if true
test "should ignore code blocks inside HTML", ->
eq executed, false
testsCount++
---
* A list item with a code block:
* A list item followed by a code block:
test "basic literate CoffeeScript parsing", ->
ok yes
test "basic literate CoffeeScript parsing", ->
ok yes
testsCount++
---
@ -88,37 +91,6 @@ if true
---
1. This is a list item with two paragraphs. Lorem ipsum dolor
sit amet, consectetuer adipiscing elit. Aliquam hendrerit
mi posuere lectus.
Vestibulum enim wisi, viverra nec, fringilla in, laoreet
vitae, risus. Donec sit amet nisl. Aliquam semper ipsum
sit amet velit.
2. Suspendisse id sem consectetuer libero luctus adipiscing.
---
1. This is a list item with two paragraphs. Lorem ipsum dolor
sit amet, consectetuer adipiscing elit. Aliquam hendrerit
mi posuere lectus.
Vestibulum enim wisi, viverra nec, fringilla in, laoreet
vitae, risus. Donec sit amet nisl. Aliquam semper ipsum
sit amet velit.
2. Suspendisse id sem consectetuer libero luctus adipiscing.
---
* A list item with a blockquote:
> This is a blockquote
> inside a list item.
---
This is [an example][id] reference-style link.
[id]: http://example.com/ "Optional Title Here"
@ -134,6 +106,7 @@ and test...
test "should recognize indented code blocks in lists with empty line as separator", ->
ok executed
testsCount++
---
@ -146,6 +119,7 @@ and test...
test "should ignore indented code in escaped list like number", ->
eq executed, no
testsCount++
one last test!
@ -155,3 +129,9 @@ one last test!
and bar!
'''
eq quote, 'foo\n\t\tand bar!'
testsCount++
and finally, how did we do?
test "all tabbed literate CoffeeScript tests executed", ->
eq testsCount, 9