diff --git a/lib/coffee_script/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage b/lib/coffee_script/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage
index 5a60ab24..31187244 100644
--- a/lib/coffee_script/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage
+++ b/lib/coffee_script/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage
@@ -93,6 +93,30 @@
name
constant.numeric.coffee
+
+ name
+ string.quoted.heredoc.coffee
+ begin
+ ("""|''')
+ end
+ ("""|''')
+ beginCaptures
+
+ 0
+
+ name
+ punctuation.definition.string.begin.coffee
+
+
+ endCaptures
+
+ 0
+
+ name
+ punctuation.definition.string.end.coffee
+
+
+
begin
'
diff --git a/lib/coffee_script/lexer.rb b/lib/coffee_script/lexer.rb
index 2abe4b8a..6c41dde1 100644
--- a/lib/coffee_script/lexer.rb
+++ b/lib/coffee_script/lexer.rb
@@ -22,6 +22,7 @@ module CoffeeScript
IDENTIFIER = /\A([a-zA-Z$_](\w|\$)*)/
NUMBER = /\A(\b((0(x|X)[0-9a-fA-F]+)|([0-9]+(\.[0-9]+)?(e[+\-]?[0-9]+)?)))\b/i
STRING = /\A(""|''|"(.*?)([^\\]|\\\\)"|'(.*?)([^\\]|\\\\)')/m
+ HEREDOC = /\A("{6}|'{6}|"{3}\n?(\s*)(.*?)\n?(\s*)"{3}|'{3}\n?(\s*)(.*?)\n?(\s*)'{3})/m
JS = /\A(``|`(.*?)([^\\]|\\\\)`)/m
OPERATOR = /\A([+\*&|\/\-%=<>:!]+)/
WHITESPACE = /\A([ \t]+)/
@@ -69,6 +70,7 @@ module CoffeeScript
def extract_next_token
return if identifier_token
return if number_token
+ return if heredoc_token
return if string_token
return if js_token
return if regex_token
@@ -103,14 +105,24 @@ module CoffeeScript
# Matches strings, including multi-line strings.
def string_token
return false unless string = @chunk[STRING, 1]
- escaped = string.gsub(MULTILINER) do |match|
- @line += 1
- " \\\n"
- end
+ escaped = string.gsub(MULTILINER, " \\\n")
token(:STRING, escaped)
+ @line += string.count("\n")
@i += string.length
end
+ # Matches heredocs, adjusting indentation to the correct level.
+ def heredoc_token
+ return false unless match = @chunk.match(HEREDOC)
+ indent = match[2] || match[5]
+ doc = match[3] || match[6]
+ doc.gsub!(/\n#{indent}/, "\\n")
+ doc.gsub!('"', '\\"')
+ token(:STRING, "\"#{doc}\"")
+ @line += match[1].count("\n")
+ @i += match[1].length
+ end
+
# Matches interpolated JavaScript.
def js_token
return false unless script = @chunk[JS, 1]
diff --git a/test/fixtures/execution/test_heredocs.coffee b/test/fixtures/execution/test_heredocs.coffee
new file mode 100644
index 00000000..07a55d05
--- /dev/null
+++ b/test/fixtures/execution/test_heredocs.coffee
@@ -0,0 +1,28 @@
+a: """
+ basic heredoc
+ on two lines
+ """
+
+print(a is "basic heredoc\non two lines")
+
+
+a: '''
+ a
+ b
+ c
+ '''
+
+print(a is "a\n b\nc")
+
+
+a: '''one-liner'''
+
+print(a is 'one-liner')
+
+
+a: """
+ out
+ here
+"""
+
+print(a is "out\nhere")
\ No newline at end of file