Commit e6c0d298a381d945eb9c326aee3fd59560b4a472

Samuel Gordalina 2021-03-02T01:49:34

Elixir: Added missing keyword and other improvements (#2773)

diff --git a/components/prism-elixir.js b/components/prism-elixir.js
index cfad1c8..868e1fc 100644
--- a/components/prism-elixir.js
+++ b/components/prism-elixir.js
@@ -1,5 +1,15 @@
 Prism.languages.elixir = {
-	'comment': /#.*/m,
+	'doc': {
+		pattern: /@(?:doc|moduledoc)\s+(?:("""|''')[\s\S]*?\1|("|')(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2)/,
+		inside: {
+			'attribute': /^@\w+/,
+			'string': /['"][\s\S]+/
+		}
+	},
+	'comment': {
+		pattern: /#.*/m,
+		greedy: true
+	},
 	// ~r"""foo""" (multi-line), ~r'''foo''' (multi-line), ~r/foo/, ~r|foo|, ~r"foo", ~r'foo', ~r(foo), ~r[foo], ~r{foo}, ~r<foo>
 	'regex': {
 		pattern: /~[rR](?:("""|''')(?:\\[\s\S]|(?!\1)[^\\])+\1|([\/|"'])(?:\\.|(?!\2)[^\\\r\n])+\2|\((?:\\.|[^\\)\r\n])+\)|\[(?:\\.|[^\\\]\r\n])+\]|\{(?:\\.|[^\\}\r\n])+\}|<(?:\\.|[^\\>\r\n])+>)[uismxfr]*/,
@@ -36,14 +46,12 @@ Prism.languages.elixir = {
 		lookbehind: true,
 		alias: 'symbol'
 	},
+	'module': {
+		pattern: /\b[A-Z]\w*\b/,
+		alias: 'class-name'
+	},
 	// Look-ahead prevents bad highlighting of the :: operator
 	'attr-name': /\w+\??:(?!:)/,
-	'capture': {
-		// Look-behind prevents bad highlighting of the && operator
-		pattern: /(^|[^&])&(?:[^&\s\d()][^\s()]*|(?=\())/,
-		lookbehind: true,
-		alias: 'function'
-	},
 	'argument': {
 		// Look-behind prevents bad highlighting of the && operator
 		pattern: /(^|[^&])&\d+/,
@@ -54,8 +62,9 @@ Prism.languages.elixir = {
 		pattern: /@\w+/,
 		alias: 'variable'
 	},
+	'function': /\b[_a-zA-Z]\w*[?!]?(?:(?=\s*(?:\.\s*)?\()|(?=\/\d+))/,
 	'number': /\b(?:0[box][a-f\d_]+|\d[\d_]*)(?:\.[\d_]+)?(?:e[+-]?[\d_]+)?\b/i,
-	'keyword': /\b(?:after|alias|and|case|catch|cond|def(?:callback|exception|impl|module|p|protocol|struct|delegate)?|do|else|end|fn|for|if|import|not|or|require|rescue|try|unless|use|when)\b/,
+	'keyword': /\b(?:after|alias|and|case|catch|cond|def(?:callback|exception|impl|module|p|protocol|struct|delegate)?|do|else|end|fn|for|if|import|not|or|raise|require|rescue|try|unless|use|when)\b/,
 	'boolean': /\b(?:true|false|nil)\b/,
 	'operator': [
 		/\bin\b|&&?|\|[|>]?|\\\\|::|\.\.\.?|\+\+?|-[->]?|<[-=>]|>=|!==?|\B!|=(?:==?|[>~])?|[*\/^]/,
@@ -73,18 +82,6 @@ Prism.languages.elixir = {
 	'punctuation': /<<|>>|[.,%\[\]{}()]/
 };
 
-Prism.languages.insertBefore('elixir', 'keyword', {
-	'module': {
-		pattern: /\b(defmodule\s)[A-Z][\w.\\]+/,
-		lookbehind: true,
-		alias: 'class-name'
-	},
-	'function': {
-		pattern: /\b(defp?\s)[\w.\\]+/,
-		lookbehind: true
-	}
-});
-
 Prism.languages.elixir.string.forEach(function(o) {
 	o.inside = {
 		'interpolation': {
diff --git a/components/prism-elixir.min.js b/components/prism-elixir.min.js
index ee6542e..83f6871 100644
--- a/components/prism-elixir.min.js
+++ b/components/prism-elixir.min.js
@@ -1 +1 @@
-Prism.languages.elixir={comment:/#.*/m,regex:{pattern:/~[rR](?:("""|''')(?:\\[\s\S]|(?!\1)[^\\])+\1|([\/|"'])(?:\\.|(?!\2)[^\\\r\n])+\2|\((?:\\.|[^\\)\r\n])+\)|\[(?:\\.|[^\\\]\r\n])+\]|\{(?:\\.|[^\\}\r\n])+\}|<(?:\\.|[^\\>\r\n])+>)[uismxfr]*/,greedy:!0},string:[{pattern:/~[cCsSwW](?:("""|''')(?:\\[\s\S]|(?!\1)[^\\])+\1|([\/|"'])(?:\\.|(?!\2)[^\\\r\n])+\2|\((?:\\.|[^\\)\r\n])+\)|\[(?:\\.|[^\\\]\r\n])+\]|\{(?:\\.|#\{[^}]+\}|#(?!\{)|[^#\\}\r\n])+\}|<(?:\\.|[^\\>\r\n])+>)[csa]?/,greedy:!0,inside:{}},{pattern:/("""|''')[\s\S]*?\1/,greedy:!0,inside:{}},{pattern:/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0,inside:{}}],atom:{pattern:/(^|[^:]):\w+/,lookbehind:!0,alias:"symbol"},"attr-name":/\w+\??:(?!:)/,capture:{pattern:/(^|[^&])&(?:[^&\s\d()][^\s()]*|(?=\())/,lookbehind:!0,alias:"function"},argument:{pattern:/(^|[^&])&\d+/,lookbehind:!0,alias:"variable"},attribute:{pattern:/@\w+/,alias:"variable"},number:/\b(?:0[box][a-f\d_]+|\d[\d_]*)(?:\.[\d_]+)?(?:e[+-]?[\d_]+)?\b/i,keyword:/\b(?:after|alias|and|case|catch|cond|def(?:callback|exception|impl|module|p|protocol|struct|delegate)?|do|else|end|fn|for|if|import|not|or|require|rescue|try|unless|use|when)\b/,boolean:/\b(?:true|false|nil)\b/,operator:[/\bin\b|&&?|\|[|>]?|\\\\|::|\.\.\.?|\+\+?|-[->]?|<[-=>]|>=|!==?|\B!|=(?:==?|[>~])?|[*\/^]/,{pattern:/([^<])<(?!<)/,lookbehind:!0},{pattern:/([^>])>(?!>)/,lookbehind:!0}],punctuation:/<<|>>|[.,%\[\]{}()]/},Prism.languages.insertBefore("elixir","keyword",{module:{pattern:/\b(defmodule\s)[A-Z][\w.\\]+/,lookbehind:!0,alias:"class-name"},function:{pattern:/\b(defp?\s)[\w.\\]+/,lookbehind:!0}}),Prism.languages.elixir.string.forEach(function(e){e.inside={interpolation:{pattern:/#\{[^}]+\}/,inside:{delimiter:{pattern:/^#\{|\}$/,alias:"punctuation"},rest:Prism.languages.elixir}}}});
\ No newline at end of file
+Prism.languages.elixir={doc:{pattern:/@(?:doc|moduledoc)\s+(?:("""|''')[\s\S]*?\1|("|')(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2)/,inside:{attribute:/^@\w+/,string:/['"][\s\S]+/}},comment:{pattern:/#.*/m,greedy:!0},regex:{pattern:/~[rR](?:("""|''')(?:\\[\s\S]|(?!\1)[^\\])+\1|([\/|"'])(?:\\.|(?!\2)[^\\\r\n])+\2|\((?:\\.|[^\\)\r\n])+\)|\[(?:\\.|[^\\\]\r\n])+\]|\{(?:\\.|[^\\}\r\n])+\}|<(?:\\.|[^\\>\r\n])+>)[uismxfr]*/,greedy:!0},string:[{pattern:/~[cCsSwW](?:("""|''')(?:\\[\s\S]|(?!\1)[^\\])+\1|([\/|"'])(?:\\.|(?!\2)[^\\\r\n])+\2|\((?:\\.|[^\\)\r\n])+\)|\[(?:\\.|[^\\\]\r\n])+\]|\{(?:\\.|#\{[^}]+\}|#(?!\{)|[^#\\}\r\n])+\}|<(?:\\.|[^\\>\r\n])+>)[csa]?/,greedy:!0,inside:{}},{pattern:/("""|''')[\s\S]*?\1/,greedy:!0,inside:{}},{pattern:/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0,inside:{}}],atom:{pattern:/(^|[^:]):\w+/,lookbehind:!0,alias:"symbol"},module:{pattern:/\b[A-Z]\w*\b/,alias:"class-name"},"attr-name":/\w+\??:(?!:)/,argument:{pattern:/(^|[^&])&\d+/,lookbehind:!0,alias:"variable"},attribute:{pattern:/@\w+/,alias:"variable"},function:/\b[_a-zA-Z]\w*[?!]?(?:(?=\s*(?:\.\s*)?\()|(?=\/\d+))/,number:/\b(?:0[box][a-f\d_]+|\d[\d_]*)(?:\.[\d_]+)?(?:e[+-]?[\d_]+)?\b/i,keyword:/\b(?:after|alias|and|case|catch|cond|def(?:callback|exception|impl|module|p|protocol|struct|delegate)?|do|else|end|fn|for|if|import|not|or|raise|require|rescue|try|unless|use|when)\b/,boolean:/\b(?:true|false|nil)\b/,operator:[/\bin\b|&&?|\|[|>]?|\\\\|::|\.\.\.?|\+\+?|-[->]?|<[-=>]|>=|!==?|\B!|=(?:==?|[>~])?|[*\/^]/,{pattern:/([^<])<(?!<)/,lookbehind:!0},{pattern:/([^>])>(?!>)/,lookbehind:!0}],punctuation:/<<|>>|[.,%\[\]{}()]/},Prism.languages.elixir.string.forEach(function(e){e.inside={interpolation:{pattern:/#\{[^}]+\}/,inside:{delimiter:{pattern:/^#\{|\}$/,alias:"punctuation"},rest:Prism.languages.elixir}}}});
\ No newline at end of file
diff --git a/tests/languages/elixir/attribute_feature.test b/tests/languages/elixir/attribute_feature.test
index 273c2a4..52c0bc1 100644
--- a/tests/languages/elixir/attribute_feature.test
+++ b/tests/languages/elixir/attribute_feature.test
@@ -8,12 +8,10 @@ foobar
 
 [
 	["attribute", "@vsn"], ["number", "2"],
-	["attribute", "@moduledoc"], ["string", [
-		"\"\"\"\r\nfoobar\r\n\"\"\""
-	]],
+	["doc", [ ["attribute", "@moduledoc" ], [ "string", "\"\"\"\r\nfoobar\r\n\"\"\"" ] ] ],
 	["attribute", "@tag"], ["atom", ":external"]
 ]
 
 ----------------------------------------------------
 
-Checks for module attributes.
\ No newline at end of file
+Checks for module attributes.
diff --git a/tests/languages/elixir/capture_feature.test b/tests/languages/elixir/capture_feature.test
index 8a64b66..caa9473 100644
--- a/tests/languages/elixir/capture_feature.test
+++ b/tests/languages/elixir/capture_feature.test
@@ -1,28 +1,56 @@
+&Math.zero?(0)
 fun = &Math.zero?/1
 (&is_function/1).(fun)
 fun = &(&1 + 1)
 &List.flatten(&1, &2)
 
+fun = &Math.zero?/invalid
+
 ----------------------------------------------------
 
 [
-	"fun ", ["operator", "="],
-	["capture", "&Math.zero?/1"],
+	["operator", "&"],
+	["module", "Math"],
+	["punctuation", "."],
+	["function", "zero?" ],
 	["punctuation", "("],
-	["capture", "&is_function/1"],
+	["number", "0"],
+	["punctuation", ")"],
+	"\r\nfun ", ["operator", "="],
+	["operator", "&"],
+	["module", "Math"],
+	["punctuation", "."],
+	["function", "zero?" ],
+	["operator", "/"],
+	["number", "1"],
+	["punctuation", "("],
+	["operator", "&"],
+	["function", "is_function"],
+	["operator", "/"],
+	["number", "1"],
 	["punctuation", ")"],
 	["punctuation", "."],
 	["punctuation", "("], "fun", ["punctuation", ")"],
 	"\r\nfun ", ["operator", "="],
-	["capture", "&"],
+	["operator", "&"],
 	["punctuation", "("], ["argument", "&1"],
 	["operator", "+"], ["number", "1"], ["punctuation", ")"],
-	["capture", "&List.flatten"],
+	["operator", "&"],
+	["module", "List"],
+	["punctuation", "."], ["function", "flatten"],
 	["punctuation", "("], ["argument", "&1"],
 	["punctuation", ","], ["argument", "&2"],
-	["punctuation", ")"]
+	["punctuation", ")"],
+	"\r\n\r\nfun ",
+	[ "operator", "=" ],
+	[ "operator", "&" ],
+	[ "module", "Math" ],
+	[ "punctuation", "." ],
+	"zero?",
+	[ "operator", "/" ],
+	"invalid"
 ]
 
 ----------------------------------------------------
 
-Checks for function capturing and arguments.
\ No newline at end of file
+Checks for function capturing and arguments.
diff --git a/tests/languages/elixir/doc_feature.test b/tests/languages/elixir/doc_feature.test
new file mode 100644
index 0000000..27e8cea
--- /dev/null
+++ b/tests/languages/elixir/doc_feature.test
@@ -0,0 +1,56 @@
+@doc "single"
+@doc 'single'
+@doc """triple"""
+@doc '''triple'''
+@doc '''
+multiline
+'''
+@doc """
+multiline
+"""
+@doc since: "1.3.0"
+@doc deprecated: "phased out"
+
+@moduledoc "single"
+@moduledoc 'single'
+@moduledoc """triple"""
+@moduledoc '''triple'''
+@moduledoc '''
+multiline
+'''
+@moduledoc """
+multiline
+"""
+@moduledoc since: "1.3.0"
+@moduledoc deprecated: "phased out"
+
+----------------------------------------------------
+
+[
+  [ "doc", [ [ "attribute", "@doc" ], [ "string", "\"single\"" ] ] ],
+  [ "doc", [ [ "attribute", "@doc" ], [ "string", "'single'" ] ] ],
+  [ "doc", [ [ "attribute", "@doc" ], [ "string", "\"\"\"triple\"\"\"" ] ] ],
+  [ "doc", [ [ "attribute", "@doc" ], [ "string", "'''triple'''" ] ] ],
+  [ "doc", [ [ "attribute", "@doc" ], [ "string", "'''\nmultiline\n'''" ] ] ],
+  [ "doc", [ [ "attribute", "@doc" ], [ "string", "\"\"\"\nmultiline\n\"\"\"" ] ] ],
+  [ "attribute", "@doc" ],
+  [ "attr-name", "since:" ],
+  [ "string", [ "\"1.3.0\"" ] ],
+  [ "attribute", "@doc" ],
+  [ "attr-name", "deprecated:" ],
+  [ "string", [ "\"phased out\"" ] ],
+  [ "doc", [ [ "attribute", "@moduledoc" ], [ "string", "\"single\"" ] ] ],
+  [ "doc", [ [ "attribute", "@moduledoc" ], [ "string", "'single'" ] ] ],
+  [ "doc", [ [ "attribute", "@moduledoc" ], [ "string", "\"\"\"triple\"\"\"" ] ] ],
+  [ "doc", [ [ "attribute", "@moduledoc" ], [ "string", "'''triple'''" ] ] ],
+  [ "doc", [ [ "attribute", "@moduledoc" ], [ "string", "'''\nmultiline\n'''" ] ] ],
+  [ "doc", [ [ "attribute", "@moduledoc" ], [ "string", "\"\"\"\nmultiline\n\"\"\"" ] ] ],
+  [ "attribute", "@moduledoc" ],
+  [ "attr-name", "since:" ],
+  [ "string", [ "\"1.3.0\"" ] ],
+  [ "attribute", "@moduledoc" ],
+  [ "attr-name", "deprecated:" ],
+  [ "string", [ "\"phased out\"" ] ]
+]
+
+----------------------------------------------------
diff --git a/tests/languages/elixir/issue1392.test b/tests/languages/elixir/issue1392.test
index c7d20a8..1c0c93b 100644
--- a/tests/languages/elixir/issue1392.test
+++ b/tests/languages/elixir/issue1392.test
@@ -3,9 +3,9 @@ String.upcase(@fixed)
 ----------------------------------------------------
 
 [
-	"String",
+	["module", "String"],
 	["punctuation", "."],
-	"upcase",
+	["function", "upcase"],
 	["punctuation", "("],
 	["attribute", "@fixed"],
 	["punctuation", ")"]
@@ -13,4 +13,4 @@ String.upcase(@fixed)
 
 ----------------------------------------------------
 
-Ensure module attributes don't consume punctuation.
\ No newline at end of file
+Ensure module attributes don't consume punctuation.
diff --git a/tests/languages/elixir/issue775.test b/tests/languages/elixir/issue775.test
index d3243f8..9804bba 100644
--- a/tests/languages/elixir/issue775.test
+++ b/tests/languages/elixir/issue775.test
@@ -5,13 +5,16 @@
 ----------------------------------------------------
 
 [
-	["attribute", "@doc"],
-	["string", [
-		"\"\"\"\r\n## Parameters\r\n\"\"\""
-	]]
+	[
+		"doc",
+		[
+			[ "attribute", "@doc" ],
+			[ "string", "\"\"\"\r\n## Parameters\r\n\"\"\"" ]
+		]
+	]
 ]
 
 ----------------------------------------------------
 
 Ensures that markdown headers are not highlighted as comments inside strings.
-See #775 for details.
\ No newline at end of file
+See #775 for details.
diff --git a/tests/languages/elixir/keyword_feature.test b/tests/languages/elixir/keyword_feature.test
index 6a851f1..c4f56fe 100644
--- a/tests/languages/elixir/keyword_feature.test
+++ b/tests/languages/elixir/keyword_feature.test
@@ -9,7 +9,7 @@ defdelegate
 defstruct do else
 end fn for if
 import not or
-require rescue try
+raise require rescue try
 unless use when
 
 ----------------------------------------------------
@@ -26,7 +26,7 @@ unless use when
 	["keyword", "defstruct"], ["keyword", "do"], ["keyword", "else"],
 	["keyword", "end"], ["keyword", "fn"], ["keyword", "for"], ["keyword", "if"],
 	["keyword", "import"], ["keyword", "not"], ["keyword", "or"],
-	["keyword", "require"], ["keyword", "rescue"], ["keyword", "try"],
+	["keyword", "raise"], ["keyword", "require"], ["keyword", "rescue"], ["keyword", "try"],
 	["keyword", "unless"], ["keyword", "use"], ["keyword", "when"]
 ]