Commit 20b77bffa41bc35864523e421ed9571bbc9a2e6d

Samuel Cochran 2021-05-14T21:04:43

Ruby: Added heredoc literals (#2885) Adds support for heredoc-style string literals in the Ruby syntax. https://ruby-doc.org/core-2.7.0/doc/syntax/literals_rdoc.html#label-Here+Documents+-28heredocs-29 This isn't perfect, but it's a good start.

diff --git a/components/prism-ruby.js b/components/prism-ruby.js
index 9e22014..6c7342b 100644
--- a/components/prism-ruby.js
+++ b/components/prism-ruby.js
@@ -102,6 +102,35 @@
 			inside: {
 				'interpolation': interpolation
 			}
+		},
+		{
+			pattern: /<<[-~]?([a-z_]\w*)[\r\n](?:.*[\r\n])*?[\t ]*\1/i,
+			alias: 'heredoc-string',
+			greedy: true,
+			inside: {
+				'delimiter': {
+					pattern: /^<<[-~]?[a-z_]\w*|[a-z_]\w*$/i,
+					alias: 'symbol',
+					inside: {
+						'punctuation': /^<<[-~]?/
+					}
+				},
+				'interpolation': interpolation
+			}
+		},
+		{
+			pattern: /<<[-~]?'([a-z_]\w*)'[\r\n](?:.*[\r\n])*?[\t ]*\1/i,
+			alias: 'heredoc-string',
+			greedy: true,
+			inside: {
+				'delimiter': {
+					pattern: /^<<[-~]?'[a-z_]\w*'|[a-z_]\w*$/i,
+					alias: 'symbol',
+					inside: {
+						'punctuation': /^<<[-~]?'|'$/,
+					}
+				}
+			}
 		}
 	];
 
diff --git a/components/prism-ruby.min.js b/components/prism-ruby.min.js
index fb967df..4a0ecd4 100644
--- a/components/prism-ruby.min.js
+++ b/components/prism-ruby.min.js
@@ -1 +1 @@
-!function(e){e.languages.ruby=e.languages.extend("clike",{comment:[/#.*/,{pattern:/^=begin\s[\s\S]*?^=end/m,greedy:!0}],"class-name":{pattern:/(\b(?:class)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:alias|and|BEGIN|begin|break|case|class|def|define_method|defined|do|each|else|elsif|END|end|ensure|extend|for|if|in|include|module|new|next|nil|not|or|prepend|protected|private|public|raise|redo|require|rescue|retry|return|self|super|then|throw|undef|unless|until|when|while|yield)\b/});var n={pattern:/#\{[^}]+\}/,inside:{delimiter:{pattern:/^#\{|\}$/,alias:"tag"},rest:e.languages.ruby}};delete e.languages.ruby.function,e.languages.insertBefore("ruby","keyword",{regex:[{pattern:RegExp("%r(?:"+["([^a-zA-Z0-9\\s{(\\[<])(?:(?!\\1)[^\\\\]|\\\\[^])*\\1","\\((?:[^()\\\\]|\\\\[^])*\\)","\\{(?:[^#{}\\\\]|#(?:\\{[^}]+\\})?|\\\\[^])*\\}","\\[(?:[^\\[\\]\\\\]|\\\\[^])*\\]","<(?:[^<>\\\\]|\\\\[^])*>"].join("|")+")[egimnosux]{0,6}"),greedy:!0,inside:{interpolation:n}},{pattern:/(^|[^/])\/(?!\/)(?:\[[^\r\n\]]+\]|\\.|[^[/\\\r\n])+\/[egimnosux]{0,6}(?=\s*(?:$|[\r\n,.;})#]))/,lookbehind:!0,greedy:!0,inside:{interpolation:n}}],variable:/[@$]+[a-zA-Z_]\w*(?:[?!]|\b)/,symbol:{pattern:/(^|[^:]):[a-zA-Z_]\w*(?:[?!]|\b)/,lookbehind:!0},"method-definition":{pattern:/(\bdef\s+)[\w.]+/,lookbehind:!0,inside:{function:/\w+$/,rest:e.languages.ruby}}}),e.languages.insertBefore("ruby","number",{builtin:/\b(?:Array|Bignum|Binding|Class|Continuation|Dir|Exception|FalseClass|File|Stat|Fixnum|Float|Hash|Integer|IO|MatchData|Method|Module|NilClass|Numeric|Object|Proc|Range|Regexp|String|Struct|TMS|Symbol|ThreadGroup|Thread|Time|TrueClass)\b/,constant:/\b[A-Z]\w*(?:[?!]|\b)/}),e.languages.ruby.string=[{pattern:RegExp("%[qQiIwWxs]?(?:"+["([^a-zA-Z0-9\\s{(\\[<])(?:(?!\\1)[^\\\\]|\\\\[^])*\\1","\\((?:[^()\\\\]|\\\\[^])*\\)","\\{(?:[^#{}\\\\]|#(?:\\{[^}]+\\})?|\\\\[^])*\\}","\\[(?:[^\\[\\]\\\\]|\\\\[^])*\\]","<(?:[^<>\\\\]|\\\\[^])*>"].join("|")+")"),greedy:!0,inside:{interpolation:n}},{pattern:/("|')(?:#\{[^}]+\}|#(?!\{)|\\(?:\r\n|[\s\S])|(?!\1)[^\\#\r\n])*\1/,greedy:!0,inside:{interpolation:n}}],e.languages.rb=e.languages.ruby}(Prism);
\ No newline at end of file
+!function(e){e.languages.ruby=e.languages.extend("clike",{comment:[/#.*/,{pattern:/^=begin\s[\s\S]*?^=end/m,greedy:!0}],"class-name":{pattern:/(\b(?:class)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:alias|and|BEGIN|begin|break|case|class|def|define_method|defined|do|each|else|elsif|END|end|ensure|extend|for|if|in|include|module|new|next|nil|not|or|prepend|protected|private|public|raise|redo|require|rescue|retry|return|self|super|then|throw|undef|unless|until|when|while|yield)\b/});var n={pattern:/#\{[^}]+\}/,inside:{delimiter:{pattern:/^#\{|\}$/,alias:"tag"},rest:e.languages.ruby}};delete e.languages.ruby.function,e.languages.insertBefore("ruby","keyword",{regex:[{pattern:RegExp("%r(?:"+["([^a-zA-Z0-9\\s{(\\[<])(?:(?!\\1)[^\\\\]|\\\\[^])*\\1","\\((?:[^()\\\\]|\\\\[^])*\\)","\\{(?:[^#{}\\\\]|#(?:\\{[^}]+\\})?|\\\\[^])*\\}","\\[(?:[^\\[\\]\\\\]|\\\\[^])*\\]","<(?:[^<>\\\\]|\\\\[^])*>"].join("|")+")[egimnosux]{0,6}"),greedy:!0,inside:{interpolation:n}},{pattern:/(^|[^/])\/(?!\/)(?:\[[^\r\n\]]+\]|\\.|[^[/\\\r\n])+\/[egimnosux]{0,6}(?=\s*(?:$|[\r\n,.;})#]))/,lookbehind:!0,greedy:!0,inside:{interpolation:n}}],variable:/[@$]+[a-zA-Z_]\w*(?:[?!]|\b)/,symbol:{pattern:/(^|[^:]):[a-zA-Z_]\w*(?:[?!]|\b)/,lookbehind:!0},"method-definition":{pattern:/(\bdef\s+)[\w.]+/,lookbehind:!0,inside:{function:/\w+$/,rest:e.languages.ruby}}}),e.languages.insertBefore("ruby","number",{builtin:/\b(?:Array|Bignum|Binding|Class|Continuation|Dir|Exception|FalseClass|File|Stat|Fixnum|Float|Hash|Integer|IO|MatchData|Method|Module|NilClass|Numeric|Object|Proc|Range|Regexp|String|Struct|TMS|Symbol|ThreadGroup|Thread|Time|TrueClass)\b/,constant:/\b[A-Z]\w*(?:[?!]|\b)/}),e.languages.ruby.string=[{pattern:RegExp("%[qQiIwWxs]?(?:"+["([^a-zA-Z0-9\\s{(\\[<])(?:(?!\\1)[^\\\\]|\\\\[^])*\\1","\\((?:[^()\\\\]|\\\\[^])*\\)","\\{(?:[^#{}\\\\]|#(?:\\{[^}]+\\})?|\\\\[^])*\\}","\\[(?:[^\\[\\]\\\\]|\\\\[^])*\\]","<(?:[^<>\\\\]|\\\\[^])*>"].join("|")+")"),greedy:!0,inside:{interpolation:n}},{pattern:/("|')(?:#\{[^}]+\}|#(?!\{)|\\(?:\r\n|[\s\S])|(?!\1)[^\\#\r\n])*\1/,greedy:!0,inside:{interpolation:n}},{pattern:/<<[-~]?([a-z_]\w*)[\r\n](?:.*[\r\n])*?[\t ]*\1/i,alias:"heredoc-string",greedy:!0,inside:{delimiter:{pattern:/^<<[-~]?[a-z_]\w*|[a-z_]\w*$/i,alias:"symbol",inside:{punctuation:/^<<[-~]?/}},interpolation:n}},{pattern:/<<[-~]?'([a-z_]\w*)'[\r\n](?:.*[\r\n])*?[\t ]*\1/i,alias:"heredoc-string",greedy:!0,inside:{delimiter:{pattern:/^<<[-~]?'[a-z_]\w*'|[a-z_]\w*$/i,alias:"symbol",inside:{punctuation:/^<<[-~]?'|'$/}}}}],e.languages.rb=e.languages.ruby}(Prism);
\ No newline at end of file
diff --git a/examples/prism-ruby.html b/examples/prism-ruby.html
index 4f1e390..58d43cd 100644
--- a/examples/prism-ruby.html
+++ b/examples/prism-ruby.html
@@ -7,7 +7,10 @@ comment
 
 <h2>Strings</h2>
 <pre><code>"foo \"bar\" baz"
-'foo \'bar\' baz'</code></pre>
+'foo \'bar\' baz'
+&lt;&lt;STRING
+  here's some #{string} contents
+STRING</code></pre>
 
 <h2>Regular expressions</h2>
 <pre><code>/foo?[ ]*bar/</code></pre>
diff --git a/tests/languages/ruby/string_feature.test b/tests/languages/ruby/string_feature.test
index 2703abe..f8a6473 100644
--- a/tests/languages/ruby/string_feature.test
+++ b/tests/languages/ruby/string_feature.test
@@ -36,6 +36,25 @@ bar"
 %x[foo #{ 42 }]
 %x<foo #{ 42 }>
 
+<<STRING
+  foo #{42} bar
+STRING
+  <<-STRING
+    foo #{42} bar
+  STRING
+  <<~STRING
+    foo #{42} bar
+  STRING
+<<'STRING'
+  foo #{42} bar
+STRING
+  <<-'STRING'
+    foo #{42} bar
+  STRING
+  <<~'STRING'
+    foo #{42} bar
+  STRING
+
 ----------------------------------------------------
 
 [
@@ -279,9 +298,78 @@ bar"
             ["delimiter", "}"]
         ]],
         ">"
+    ]],
+    ["string", [
+      ["delimiter", [
+        ["punctuation", "<<"], 
+        "STRING"
+      ]],
+      "\r\n  foo ",
+      ["interpolation", [
+        ["delimiter", "#{"],
+        ["number", "42"],
+        ["delimiter", "}"]
+      ]],
+      " bar\r\n",
+      ["delimiter", ["STRING"]]
+    ]],
+    ["string", [
+      ["delimiter", [
+        ["punctuation", "<<-"], 
+        "STRING"
+      ]],
+      "\r\n    foo ",
+      ["interpolation", [
+        ["delimiter", "#{"],
+        ["number", "42"],
+        ["delimiter", "}"]
+      ]],
+      " bar\r\n  ",
+      ["delimiter", ["STRING"]]
+    ]],
+    ["string", [
+      ["delimiter", [
+        ["punctuation", "<<~"], 
+        "STRING"
+      ]],
+      "\r\n    foo ",
+      ["interpolation", [
+        ["delimiter", "#{"],
+        ["number", "42"],
+        ["delimiter", "}"]
+      ]],
+      " bar\r\n  ",
+      ["delimiter", ["STRING"]]
+    ]],
+    ["string", [
+      ["delimiter", [
+        ["punctuation", "<<'"],
+        "STRING",
+        ["punctuation", "'"]
+      ]],
+      "\r\n  foo #{42} bar\r\n",
+      ["delimiter", ["STRING"]]
+    ]],
+    ["string", [
+      ["delimiter", [
+        ["punctuation", "<<-'"],
+        "STRING",
+        ["punctuation", "'"]
+      ]],
+      "\r\n    foo #{42} bar\r\n  ",
+      ["delimiter", ["STRING"]]
+    ]],
+    ["string", [
+      ["delimiter", [
+        ["punctuation", "<<~'"],
+        "STRING",
+        ["punctuation", "'"]
+      ]],
+      "\r\n    foo #{42} bar\r\n  ",
+      ["delimiter", ["STRING"]]
     ]]
 ]
 
 ----------------------------------------------------
 
-Checks for strings and string interpolation.
\ No newline at end of file
+Checks for strings and string interpolation.