Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CS2] Fix handling of tabbed code blocks in .litcoffee files #4485

Merged
merged 10 commits into from
Apr 6, 2017
Merged
30 changes: 17 additions & 13 deletions lib/coffeescript/helpers.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 20 additions & 14 deletions src/helpers.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -82,20 +82,26 @@ exports.some = Array::some ? (fn) ->
# out all non-code blocks, producing a string of CoffeeScript code that can
# be compiled “normally.”
exports.invertLiterate = (code) ->
# Create a placeholder for tabs, that isn’t used anywhere in `code`, and then
# re-insert the tabs after code extraction.
generateRandomToken = ->
"#{Math.random() * Date.now()}"
while token is undefined or code.indexOf(token) isnt -1
token = generateRandomToken()

code = code.replace "\t", token
# Parse as markdown, discard everything except code blocks.
out = ""
for item in marked.lexer code, {}
out += "#{item.text}\n" if item.type is 'code'
# Put the tabs back in.
out.replace token, "\t"
# `marked` really doesn’t process tabs too well. All this code is headed
# into JavaScript anyway, so replace the tabs with two spaces to make
# `marked`’s job a little easier.
code = code.replace /^(\t)+/gm, (match) ->
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about multiline strings and regexes?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code here is the source code of an entire file. This function is intending to replace all leading tabs (between the start of any line and the first non-tab character) with 2 spaces × the number of leading tabs on that line. Does it not do that?

Copy link
Collaborator

@lydell lydell Apr 5, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m worried that it changes the meaning of mulitline strings and regexes.

Here’s a multiline string:

	myString = """
		line one
			line two
	"""

	eq myString, "line one\n\tline two"

Copy link
Collaborator

@lydell lydell Apr 5, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps only replacing the first tab on each line is enough? code.replace /^\t/gm, ' '

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was the previous approach, replacing the first tab with a tab plus a magic token. Then after the parsing by Marked, the tab plus token were replaced back with a tab. For some reason I was getting lots of compiler errors regarding indentation when I did that, that I couldn't figure out. I'll look into it some more.

spaces = ''
spaces += ' ' for tab in [0..match.length]
spaces

out = ''
for section in marked.lexer code, {}
if section.type is 'code'
out += section.text + '\n\n'
# Keep the non-code sections (that have text) so as to hopefully
# preserve the correct line numbers. Lists can screw this up. For better
# stack traces, don’t wrap your own list items like our tests do but rather
# let them run free and rely on your editor to wrap your lines for you.
else if section.text?
out += '# ' + section.text.trim().replace(/\n/g, '\n# ') + '\n\n'
else if section.type is 'hr'
out += '# ---\n\n'
out

# Merge two jison-style location data objects together.
Expand Down
3 changes: 1 addition & 2 deletions test/literate.litcoffee
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
Literate CoffeeScript Test
--------------------------
# Literate CoffeeScript Test

comment comment

Expand Down
164 changes: 164 additions & 0 deletions test/literate_tabbed.litcoffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# Tabbed Literate CoffeeScript Test

comment comment

test "basic literate CoffeeScript parsing", ->
ok yes

now with a...

test "broken up indentation", ->

... broken up ...

do ->

... nested block.

ok yes

Code must be separated from text by a blank line.

test "code blocks must be preceded by a blank line", ->

The next line is part of the text and will not be executed.
fail()

ok yes

Code in `backticks is not parsed` and...

test "comments in indented blocks work", ->
do ->
do ->
# Regular comment.

###
Block comment.
###

ok yes

Regular [Markdown](http://example.com/markdown) features, like links
and unordered lists, are fine:

* I

* Am

* A

* List

Spaces work too:

test "spaced code", ->
ok yes

---

# keep track of whether code blocks are executed or not
executed = false

<p>

executed = true # should not execute, this is just HTML para, not code!

</p>

test "should ignore indented sections inside HTML", ->
eq executed, false

---

* A list item with a code block:

test "basic literate CoffeeScript parsing", ->
ok yes

---

* 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.
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.

---

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 next one probably passes because a string is inoffensive in compiled js, also, can't get `marked` to parse it correctly, and not sure if empty line is permitted between title and reference

This is [an example][id] reference-style link.
[id]: http://example.com/

"Optional Title Here"

---

executed = no

1986. What a great season.
executed = yes

and test...

test "should recognise indented code blocks in lists", ->
ok executed

---

executed = no

1986. What a great season.

executed = yes

and test...

test "should recognise indented code blocks in lists with empty line as separator", ->
ok executed

---

executed = no

1986\. What a great season.
executed = yes

and test...

test "should ignore indented code in escaped list like number", ->
eq executed, no