summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbscan <10503608+bscan@users.noreply.github.com>2023-10-08 12:29:54 -0400
committerbscan <10503608+bscan@users.noreply.github.com>2023-10-08 12:29:54 -0400
commitb10cf7b92e4cd133acf8e117f891f2bbfe759589 (patch)
tree6aeca8b40fce8b878ee0f06b37b142e94fcf04a4
parent103e8f3ae76ccfe7dc47ebba82f36bc2f8fca4e3 (diff)
downloadPerlNavigator-b10cf7b92e4cd133acf8e117f891f2bbfe759589.zip
Large refactor. Moving tagging from Perl to TypeScript. Leverages TextMate instead of Text::Balanced for finding scopes
-rw-r--r--package-lock.json4
-rw-r--r--server/package-lock.json59
-rw-r--r--server/package.json4
-rw-r--r--server/perl.tmLanguage.json2977
-rw-r--r--server/src/completion.ts16
-rw-r--r--server/src/diagnostics.ts31
-rw-r--r--server/src/hover.ts12
-rw-r--r--server/src/parseTags.ts (renamed from server/src/parseDocument.ts)2
-rw-r--r--server/src/parser.ts570
-rw-r--r--server/src/perl/Inquisitor.pm1
-rw-r--r--server/src/perl/criticWrapper.pl1
-rw-r--r--server/src/perl/defaultCriticProfile7
-rw-r--r--server/src/perl/lib_bs22/pltags.pm311
-rw-r--r--server/src/server.ts15
-rw-r--r--server/src/symbols.ts120
-rw-r--r--server/src/types.ts3
-rw-r--r--server/src/utils.ts2
-rw-r--r--server/tsconfig.json4
-rw-r--r--testWorkspace/mainTest.pl15
19 files changed, 3729 insertions, 425 deletions
diff --git a/package-lock.json b/package-lock.json
index f2cf087..661406d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "perlnavigator",
- "version": "0.5.4",
+ "version": "0.6.3",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "perlnavigator",
- "version": "0.5.4",
+ "version": "0.6.3",
"hasInstallScript": true,
"license": "MIT",
"bin": {
diff --git a/server/package-lock.json b/server/package-lock.json
index bab08c0..02fb6aa 100644
--- a/server/package-lock.json
+++ b/server/package-lock.json
@@ -1,20 +1,25 @@
{
"name": "perlnavigator-server",
- "version": "0.5.4",
+ "version": "0.6.3",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "perlnavigator-server",
- "version": "0.5.4",
+ "version": "0.6.3",
"license": "MIT",
"dependencies": {
"lru-cache": "^6.0.0",
"nanoid": "^3.1.20",
"vscode-languageserver": "^7.0.0",
"vscode-languageserver-textdocument": "^1.0.1",
+ "vscode-oniguruma": "^2.0.1",
+ "vscode-textmate": "^9.0.0",
"vscode-uri": "^1.0.1"
},
+ "bin": {
+ "perlnavigator": "bin/perlnavigator"
+ },
"devDependencies": {},
"engines": {
"node": "*"
@@ -32,9 +37,15 @@
}
},
"node_modules/nanoid": {
- "version": "3.3.4",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
- "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
+ "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
"bin": {
"nanoid": "bin/nanoid.cjs"
},
@@ -71,15 +82,25 @@
}
},
"node_modules/vscode-languageserver-textdocument": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.1.tgz",
- "integrity": "sha512-UIcJDjX7IFkck7cSkNNyzIz5FyvpQfY7sdzVy+wkKN/BLaD4DQ0ppXQrKePomCxTS7RrolK1I0pey0bG9eh8dA=="
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.8.tgz",
+ "integrity": "sha512-1bonkGqQs5/fxGT5UchTgjGVnfysL0O8v1AYMBjqTbWQTFn721zaPGDYFkOKtfDgFiSgXM3KwaG3FMGfW4Ed9Q=="
},
"node_modules/vscode-languageserver-types": {
"version": "3.16.0",
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz",
"integrity": "sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA=="
},
+ "node_modules/vscode-oniguruma": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-2.0.1.tgz",
+ "integrity": "sha512-poJU8iHIWnC3vgphJnrLZyI3YdqRlR27xzqDmpPXYzA93R4Gk8z7T6oqDzDoHjoikA2aS82crdXFkjELCdJsjQ=="
+ },
+ "node_modules/vscode-textmate": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-9.0.0.tgz",
+ "integrity": "sha512-Cl65diFGxz7gpwbav10HqiY/eVYTO1sjQpmRmV991Bj7wAoOAjGQ97PpQcXorDE2Uc4hnGWLY17xme+5t6MlSg=="
+ },
"node_modules/vscode-uri": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.8.tgz",
@@ -101,9 +122,9 @@
}
},
"nanoid": {
- "version": "3.3.4",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
- "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw=="
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
+ "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA=="
},
"vscode-jsonrpc": {
"version": "6.0.0",
@@ -128,15 +149,25 @@
}
},
"vscode-languageserver-textdocument": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.1.tgz",
- "integrity": "sha512-UIcJDjX7IFkck7cSkNNyzIz5FyvpQfY7sdzVy+wkKN/BLaD4DQ0ppXQrKePomCxTS7RrolK1I0pey0bG9eh8dA=="
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.8.tgz",
+ "integrity": "sha512-1bonkGqQs5/fxGT5UchTgjGVnfysL0O8v1AYMBjqTbWQTFn721zaPGDYFkOKtfDgFiSgXM3KwaG3FMGfW4Ed9Q=="
},
"vscode-languageserver-types": {
"version": "3.16.0",
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz",
"integrity": "sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA=="
},
+ "vscode-oniguruma": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-2.0.1.tgz",
+ "integrity": "sha512-poJU8iHIWnC3vgphJnrLZyI3YdqRlR27xzqDmpPXYzA93R4Gk8z7T6oqDzDoHjoikA2aS82crdXFkjELCdJsjQ=="
+ },
+ "vscode-textmate": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-9.0.0.tgz",
+ "integrity": "sha512-Cl65diFGxz7gpwbav10HqiY/eVYTO1sjQpmRmV991Bj7wAoOAjGQ97PpQcXorDE2Uc4hnGWLY17xme+5t6MlSg=="
+ },
"vscode-uri": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.8.tgz",
diff --git a/server/package.json b/server/package.json
index 68a8aa3..050ed26 100644
--- a/server/package.json
+++ b/server/package.json
@@ -16,7 +16,9 @@
"vscode-languageserver-textdocument": "^1.0.1",
"vscode-uri": "^1.0.1",
"lru-cache": "^6.0.0",
- "nanoid": "^3.1.20"
+ "nanoid": "^3.1.20",
+ "vscode-textmate": "^9.0.0",
+ "vscode-oniguruma": "^2.0.1"
},
"scripts": {},
"main": "./src/out/server.js",
diff --git a/server/perl.tmLanguage.json b/server/perl.tmLanguage.json
new file mode 100644
index 0000000..a6a84a5
--- /dev/null
+++ b/server/perl.tmLanguage.json
@@ -0,0 +1,2977 @@
+{
+ "information_for_contributors": [
+ "This file has been converted from https://github.com/textmate/perl.tmbundle/blob/master/Syntaxes/Perl.plist",
+ "If you want to provide a fix or improvement, please create a pull request against the original repository.",
+ "Once accepted there, we are happy to receive an update request."
+ ],
+ "version": "https://github.com/textmate/perl.tmbundle/commit/a85927a902d6e5d7805f56a653f324d34dfad53a",
+ "name": "Perl",
+ "scopeName": "source.perl",
+ "comment": "\n\tTODO:\tInclude RegExp syntax\n",
+ "patterns": [
+ {
+ "include": "#line_comment"
+ },
+ {
+ "begin": "^(?==[a-zA-Z]+)",
+ "end": "^(=cut\\b.*$)",
+ "endCaptures": {
+ "1": {
+ "patterns": [
+ {
+ "include": "#pod"
+ }
+ ]
+ }
+ },
+ "name": "comment.block.documentation.perl",
+ "patterns": [
+ {
+ "include": "#pod"
+ }
+ ]
+ },
+ {
+ "include": "#variable"
+ },
+ {
+ "applyEndPatternLast": 1,
+ "begin": "\\b(?=qr\\s*[^\\s\\w])",
+ "comment": "string.regexp.compile.perl",
+ "end": "((([egimosxradlupcn]*)))(?=(\\s+\\S|\\s*[;\\,\\#\\{\\}\\)]|\\s*$))",
+ "endCaptures": {
+ "1": {
+ "name": "string.regexp.compile.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.string.perl"
+ },
+ "3": {
+ "name": "keyword.control.regexp-option.perl"
+ }
+ },
+ "patterns": [
+ {
+ "begin": "(qr)\\s*\\{",
+ "captures": {
+ "0": {
+ "name": "punctuation.definition.string.perl"
+ },
+ "1": {
+ "name": "support.function.perl"
+ }
+ },
+ "end": "\\}",
+ "name": "string.regexp.compile.nested_braces.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ },
+ {
+ "include": "#nested_braces_interpolated"
+ }
+ ]
+ },
+ {
+ "begin": "(qr)\\s*\\[",
+ "captures": {
+ "0": {
+ "name": "punctuation.definition.string.perl"
+ },
+ "1": {
+ "name": "support.function.perl"
+ }
+ },
+ "end": "\\]",
+ "name": "string.regexp.compile.nested_brackets.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ },
+ {
+ "include": "#nested_brackets_interpolated"
+ }
+ ]
+ },
+ {
+ "begin": "(qr)\\s*<",
+ "captures": {
+ "0": {
+ "name": "punctuation.definition.string.perl"
+ },
+ "1": {
+ "name": "support.function.perl"
+ }
+ },
+ "end": ">",
+ "name": "string.regexp.compile.nested_ltgt.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ },
+ {
+ "include": "#nested_ltgt_interpolated"
+ }
+ ]
+ },
+ {
+ "begin": "(qr)\\s*\\(",
+ "captures": {
+ "0": {
+ "name": "punctuation.definition.string.perl"
+ },
+ "1": {
+ "name": "support.function.perl"
+ }
+ },
+ "end": "\\)",
+ "name": "string.regexp.compile.nested_parens.perl",
+ "patterns": [
+ {
+ "comment": "This is to prevent thinks like qr/foo$/ to treat $/ as a variable",
+ "match": "\\$(?=[^\\s\\w\\\\'\\{\\[\\(\\<])"
+ },
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ },
+ {
+ "include": "#nested_parens_interpolated"
+ }
+ ]
+ },
+ {
+ "begin": "(qr)\\s*'",
+ "captures": {
+ "0": {
+ "name": "punctuation.definition.string.perl"
+ },
+ "1": {
+ "name": "support.function.perl"
+ }
+ },
+ "end": "'",
+ "name": "string.regexp.compile.single-quote.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ }
+ ]
+ },
+ {
+ "begin": "(qr)\\s*([^\\s\\w'\\{\\[\\(\\<])",
+ "captures": {
+ "0": {
+ "name": "punctuation.definition.string.perl"
+ },
+ "1": {
+ "name": "support.function.perl"
+ }
+ },
+ "end": "\\2",
+ "name": "string.regexp.compile.simple-delimiter.perl",
+ "patterns": [
+ {
+ "comment": "This is to prevent thinks like qr/foo$/ to treat $/ as a variable",
+ "match": "\\$(?=[^\\s\\w'\\{\\[\\(\\<])",
+ "name": "keyword.control.anchor.perl"
+ },
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ },
+ {
+ "include": "#nested_parens_interpolated"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "applyEndPatternLast": 1,
+ "begin": "(?<!\\{|\\+|\\-)\\b(?=m\\s*[^\\sa-zA-Z0-9])",
+ "comment": "string.regexp.find-m.perl",
+ "end": "((([egimosxradlupcn]*)))(?=(\\s+\\S|\\s*[;\\,\\#\\{\\}\\)]|\\s*$))",
+ "endCaptures": {
+ "1": {
+ "name": "string.regexp.find-m.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.string.perl"
+ },
+ "3": {
+ "name": "keyword.control.regexp-option.perl"
+ }
+ },
+ "patterns": [
+ {
+ "begin": "(m)\\s*\\{",
+ "captures": {
+ "0": {
+ "name": "punctuation.definition.string.perl"
+ },
+ "1": {
+ "name": "support.function.perl"
+ }
+ },
+ "end": "\\}",
+ "name": "string.regexp.find-m.nested_braces.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ },
+ {
+ "include": "#nested_braces_interpolated"
+ }
+ ]
+ },
+ {
+ "begin": "(m)\\s*\\[",
+ "captures": {
+ "0": {
+ "name": "punctuation.definition.string.perl"
+ },
+ "1": {
+ "name": "support.function.perl"
+ }
+ },
+ "end": "\\]",
+ "name": "string.regexp.find-m.nested_brackets.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ },
+ {
+ "include": "#nested_brackets_interpolated"
+ }
+ ]
+ },
+ {
+ "begin": "(m)\\s*<",
+ "captures": {
+ "0": {
+ "name": "punctuation.definition.string.perl"
+ },
+ "1": {
+ "name": "support.function.perl"
+ }
+ },
+ "end": ">",
+ "name": "string.regexp.find-m.nested_ltgt.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ },
+ {
+ "include": "#nested_ltgt_interpolated"
+ }
+ ]
+ },
+ {
+ "begin": "(m)\\s*\\(",
+ "captures": {
+ "0": {
+ "name": "punctuation.definition.string.perl"
+ },
+ "1": {
+ "name": "support.function.perl"
+ }
+ },
+ "end": "\\)",
+ "name": "string.regexp.find-m.nested_parens.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ },
+ {
+ "include": "#nested_parens_interpolated"
+ }
+ ]
+ },
+ {
+ "begin": "(m)\\s*'",
+ "captures": {
+ "0": {
+ "name": "punctuation.definition.string.perl"
+ },
+ "1": {
+ "name": "support.function.perl"
+ }
+ },
+ "end": "'",
+ "name": "string.regexp.find-m.single-quote.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ }
+ ]
+ },
+ {
+ "begin": "\\G(?<!\\{|\\+|\\-)(m)(?!_)\\s*([^\\sa-zA-Z0-9'\\{\\[\\(\\<])",
+ "captures": {
+ "0": {
+ "name": "punctuation.definition.string.perl"
+ },
+ "1": {
+ "name": "support.function.perl"
+ }
+ },
+ "end": "\\2",
+ "name": "string.regexp.find-m.simple-delimiter.perl",
+ "patterns": [
+ {
+ "comment": "This is to prevent thinks like qr/foo$/ to treat $/ as a variable",
+ "match": "\\$(?=[^\\sa-zA-Z0-9'\\{\\[\\(\\<])",
+ "name": "keyword.control.anchor.perl"
+ },
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ },
+ {
+ "begin": "\\[",
+ "beginCaptures": {
+ "1": {
+ "name": "punctuation.definition.character-class.begin.perl"
+ }
+ },
+ "end": "\\]",
+ "endCaptures": {
+ "1": {
+ "name": "punctuation.definition.character-class.end.perl"
+ }
+ },
+ "name": "constant.other.character-class.set.perl",
+ "patterns": [
+ {
+ "comment": "This is to prevent thinks like qr/foo$/ to treat $/ as a variable",
+ "match": "\\$(?=[^\\s\\w'\\{\\[\\(\\<])",
+ "name": "keyword.control.anchor.perl"
+ },
+ {
+ "include": "#escaped_char"
+ }
+ ]
+ },
+ {
+ "include": "#nested_parens_interpolated"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "applyEndPatternLast": 1,
+ "begin": "\\b(?=(?<!\\&)(s)(\\s+\\S|\\s*[;\\,\\{\\}\\(\\)\\[<]|$))",
+ "comment": "string.regexp.replace.perl",
+ "end": "((([egimosxradlupcn]*)))(?=(\\s+\\S|\\s*[;\\,\\{\\}\\)\\]>]|\\s*$))",
+ "endCaptures": {
+ "1": {
+ "name": "string.regexp.replace.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.string.perl"
+ },
+ "3": {
+ "name": "keyword.control.regexp-option.perl"
+ }
+ },
+ "patterns": [
+ {
+ "begin": "(s)\\s*\\{",
+ "captures": {
+ "0": {
+ "name": "punctuation.definition.string.perl"
+ },
+ "1": {
+ "name": "support.function.perl"
+ }
+ },
+ "end": "\\}",
+ "name": "string.regexp.nested_braces.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#nested_braces"
+ }
+ ]
+ },
+ {
+ "begin": "(s)\\s*\\[",
+ "captures": {
+ "0": {
+ "name": "punctuation.definition.string.perl"
+ },
+ "1": {
+ "name": "support.function.perl"
+ }
+ },
+ "end": "\\]",
+ "name": "string.regexp.nested_brackets.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#nested_brackets"
+ }
+ ]
+ },
+ {
+ "begin": "(s)\\s*<",
+ "captures": {
+ "0": {
+ "name": "punctuation.definition.string.perl"
+ },
+ "1": {
+ "name": "support.function.perl"
+ }
+ },
+ "end": ">",
+ "name": "string.regexp.nested_ltgt.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#nested_ltgt"
+ }
+ ]
+ },
+ {
+ "begin": "(s)\\s*\\(",
+ "captures": {
+ "0": {
+ "name": "punctuation.definition.string.perl"
+ },
+ "1": {
+ "name": "support.function.perl"
+ }
+ },
+ "end": "\\)",
+ "name": "string.regexp.nested_parens.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#nested_parens"
+ }
+ ]
+ },
+ {
+ "begin": "\\{",
+ "captures": {
+ "0": {
+ "name": "punctuation.definition.string.perl"
+ }
+ },
+ "end": "\\}",
+ "name": "string.regexp.format.nested_braces.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ },
+ {
+ "include": "#nested_braces_interpolated"
+ }
+ ]
+ },
+ {
+ "begin": "\\[",
+ "captures": {
+ "0": {
+ "name": "punctuation.definition.string.perl"
+ }
+ },
+ "end": "\\]",
+ "name": "string.regexp.format.nested_brackets.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ },
+ {
+ "include": "#nested_brackets_interpolated"
+ }
+ ]
+ },
+ {
+ "begin": "<",
+ "captures": {
+ "0": {
+ "name": "punctuation.definition.string.perl"
+ }
+ },
+ "end": ">",
+ "name": "string.regexp.format.nested_ltgt.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ },
+ {
+ "include": "#nested_ltgt_interpolated"
+ }
+ ]
+ },
+ {
+ "begin": "\\(",
+ "captures": {
+ "0": {
+ "name": "punctuation.definition.string.perl"
+ }
+ },
+ "end": "\\)",
+ "name": "string.regexp.format.nested_parens.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ },
+ {
+ "include": "#nested_parens_interpolated"
+ }
+ ]
+ },
+ {
+ "begin": "'",
+ "captures": {
+ "0": {
+ "name": "punctuation.definition.string.perl"
+ }
+ },
+ "end": "'",
+ "name": "string.regexp.format.single_quote.perl",
+ "patterns": [
+ {
+ "match": "\\\\['\\\\]",
+ "name": "constant.character.escape.perl"
+ }
+ ]
+ },
+ {
+ "begin": "([^\\s\\w\\[({<;])",
+ "captures": {
+ "0": {
+ "name": "punctuation.definition.string.perl"
+ }
+ },
+ "end": "\\1",
+ "name": "string.regexp.format.simple_delimiter.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ }
+ ]
+ },
+ {
+ "match": "\\s+"
+ }
+ ]
+ },
+ {
+ "begin": "\\b(?=s([^\\sa-zA-Z0-9\\[({<]).*\\1([egimosxradlupcn]*)([\\}\\)\\;\\,]|\\s+))",
+ "comment": "string.regexp.replaceXXX",
+ "end": "((([egimosxradlupcn]*)))(?=([\\}\\)\\;\\,]|\\s+|\\s*$))",
+ "endCaptures": {
+ "1": {
+ "name": "string.regexp.replace.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.string.perl"
+ },
+ "3": {
+ "name": "keyword.control.regexp-option.perl"
+ }
+ },
+ "patterns": [
+ {
+ "begin": "(s\\s*)([^\\sa-zA-Z0-9\\[({<])",
+ "captures": {
+ "0": {
+ "name": "punctuation.definition.string.perl"
+ },
+ "1": {
+ "name": "support.function.perl"
+ }
+ },
+ "end": "(?=\\2)",
+ "name": "string.regexp.replaceXXX.simple_delimiter.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ }
+ ]
+ },
+ {
+ "begin": "'",
+ "captures": {
+ "0": {
+ "name": "punctuation.definition.string.perl"
+ }
+ },
+ "end": "'",
+ "name": "string.regexp.replaceXXX.format.single_quote.perl",
+ "patterns": [
+ {
+ "match": "\\\\['\\\\]",
+ "name": "constant.character.escape.perl.perl"
+ }
+ ]
+ },
+ {
+ "begin": "([^\\sa-zA-Z0-9\\[({<])",
+ "captures": {
+ "0": {
+ "name": "punctuation.definition.string.perl"
+ }
+ },
+ "end": "\\1",
+ "name": "string.regexp.replaceXXX.format.simple_delimiter.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "begin": "\\b(?=(?<!\\\\)s\\s*([^\\s\\w\\[({<>]))",
+ "comment": "string.regexp.replace.extended",
+ "end": "((([egimosradlupc]*x[egimosradlupc]*)))\\b",
+ "endCaptures": {
+ "1": {
+ "name": "string.regexp.replace.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.string.perl"
+ },
+ "3": {
+ "name": "keyword.control.regexp-option.perl"
+ }
+ },
+ "patterns": [
+ {
+ "begin": "(s)\\s*(.)",
+ "captures": {
+ "0": {
+ "name": "punctuation.definition.string.perl"
+ },
+ "1": {
+ "name": "support.function.perl"
+ }
+ },
+ "end": "(?=\\2)",
+ "name": "string.regexp.replace.extended.simple_delimiter.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ }
+ ]
+ },
+ {
+ "begin": "'",
+ "captures": {
+ "0": {
+ "name": "punctuation.definition.string.perl"
+ }
+ },
+ "end": "'(?=[egimosradlupc]*x[egimosradlupc]*)\\b",
+ "name": "string.regexp.replace.extended.simple_delimiter.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ }
+ ]
+ },
+ {
+ "begin": "(.)",
+ "captures": {
+ "0": {
+ "name": "punctuation.definition.string.perl"
+ }
+ },
+ "end": "\\1(?=[egimosradlupc]*x[egimosradlupc]*)\\b",
+ "name": "string.regexp.replace.extended.simple_delimiter.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "begin": "(?<=\\(|\\{|~|&|\\||if|unless|^)\\s*((\\/))",
+ "beginCaptures": {
+ "1": {
+ "name": "string.regexp.find.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.string.perl"
+ }
+ },
+ "contentName": "string.regexp.find.perl",
+ "end": "((\\1([egimosxradlupcn]*)))(?=(\\s+\\S|\\s*[;\\,\\#\\{\\}\\)]|\\s*$))",
+ "endCaptures": {
+ "1": {
+ "name": "string.regexp.find.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.string.perl"
+ },
+ "3": {
+ "name": "keyword.control.regexp-option.perl"
+ }
+ },
+ "patterns": [
+ {
+ "comment": "This is to prevent thinks like /foo$/ to treat $/ as a variable",
+ "match": "\\$(?=\\/)",
+ "name": "keyword.control.anchor.perl"
+ },
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ }
+ ]
+ },
+ {
+ "captures": {
+ "1": {
+ "name": "constant.other.key.perl"
+ }
+ },
+ "match": "\\b(\\w+)\\s*(?==>)"
+ },
+ {
+ "match": "(?<={)\\s*\\w+\\s*(?=})",
+ "name": "constant.other.bareword.perl"
+ },
+ {
+ "captures": {
+ "1": {
+ "name": "keyword.control.perl"
+ },
+ "2": {
+ "name": "entity.name.type.class.perl"
+ }
+ },
+ "match": "^\\s*(package)\\s+([^\\s;]+)",
+ "name": "meta.class.perl"
+ },
+ {
+ "captures": {
+ "1": {
+ "name": "storage.type.sub.perl"
+ },
+ "2": {
+ "name": "entity.name.function.perl"
+ },
+ "3": {
+ "name": "storage.type.method.perl"
+ }
+ },
+ "match": "\\b(sub)(?:\\s+([-a-zA-Z0-9_]+))?\\s*(?:\\([\\$\\@\\*;]*\\))?[^\\w\\{]",
+ "name": "meta.function.perl"
+ },
+ {
+ "captures": {
+ "1": {
+ "name": "entity.name.function.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.parameters.perl"
+ },
+ "3": {
+ "name": "variable.parameter.function.perl"
+ }
+ },
+ "match": "^\\s*(BEGIN|UNITCHECK|CHECK|INIT|END|DESTROY)\\b",
+ "name": "meta.function.perl"
+ },
+ {
+ "begin": "^(?=(\\t| {4}))",
+ "end": "(?=[^\\t\\s])",
+ "name": "meta.leading-tabs",
+ "patterns": [
+ {
+ "captures": {
+ "1": {
+ "name": "meta.odd-tab"
+ },
+ "2": {
+ "name": "meta.even-tab"
+ }
+ },
+ "match": "(\\t| {4})(\\t| {4})?"
+ }
+ ]
+ },
+ {
+ "captures": {
+ "1": {
+ "name": "support.function.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.string.perl"
+ },
+ "5": {
+ "name": "punctuation.definition.string.perl"
+ },
+ "8": {
+ "name": "punctuation.definition.string.perl"
+ }
+ },
+ "match": "\\b(tr|y)\\s*([^A-Za-z0-9\\s])(.*?)(?<!\\\\)(\\\\{2})*(\\2)(.*?)(?<!\\\\)(\\\\{2})*(\\2)",
+ "name": "string.regexp.replace.perl"
+ },
+ {
+ "match": "\\b(__FILE__|__LINE__|__PACKAGE__|__SUB__)\\b",
+ "name": "constant.language.perl"
+ },
+ {
+ "begin": "\\b(__DATA__|__END__)\\n?",
+ "beginCaptures": {
+ "1": {
+ "name": "constant.language.perl"
+ }
+ },
+ "contentName": "comment.block.documentation.perl",
+ "end": "\\z",
+ "patterns": [
+ {
+ "include": "#pod"
+ }
+ ]
+ },
+ {
+ "match": "(?<!->)\\b(continue|default|die|do|else|elsif|exit|for|foreach|given|goto|if|last|next|redo|return|select|unless|until|wait|when|while|switch|case|require|use|eval)\\b",
+ "name": "keyword.control.perl"
+ },
+ {
+ "match": "\\b(my|our|local)\\b",
+ "name": "storage.modifier.perl"
+ },
+ {
+ "match": "(?<!\\w)\\-[rwxoRWXOezsfdlpSbctugkTBMAC]\\b",
+ "name": "keyword.operator.filetest.perl"
+ },
+ {
+ "match": "\\b(and|or|xor|as|not)\\b",
+ "name": "keyword.operator.logical.perl"
+ },
+ {
+ "match": "(<=>|=>|->)",
+ "name": "keyword.operator.comparison.perl"
+ },
+ {
+ "include": "#heredoc"
+ },
+ {
+ "begin": "\\bqq\\s*([^\\(\\{\\[\\<\\w\\s])",
+ "beginCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.begin.perl"
+ }
+ },
+ "end": "\\1",
+ "endCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "string.quoted.other.qq.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ }
+ ]
+ },
+ {
+ "begin": "\\bqx\\s*([^'\\(\\{\\[\\<\\w\\s])",
+ "beginCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.begin.perl"
+ }
+ },
+ "end": "\\1",
+ "endCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "string.interpolated.qx.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ }
+ ]
+ },
+ {
+ "begin": "\\bqx\\s*'",
+ "beginCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.begin.perl"
+ }
+ },
+ "end": "'",
+ "endCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "string.interpolated.qx.single-quote.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ }
+ ]
+ },
+ {
+ "begin": "\"",
+ "beginCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.begin.perl"
+ }
+ },
+ "end": "\"",
+ "endCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "string.quoted.double.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ }
+ ]
+ },
+ {
+ "begin": "(?<!->)\\bqw?\\s*([^\\(\\{\\[\\<\\w\\s])",
+ "beginCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.begin.perl"
+ }
+ },
+ "end": "\\1",
+ "endCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "string.quoted.other.q.perl"
+ },
+ {
+ "begin": "'",
+ "beginCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.begin.perl"
+ }
+ },
+ "end": "'",
+ "endCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "string.quoted.single.perl",
+ "patterns": [
+ {
+ "match": "\\\\['\\\\]",
+ "name": "constant.character.escape.perl"
+ }
+ ]
+ },
+ {
+ "begin": "`",
+ "beginCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.begin.perl"
+ }
+ },
+ "end": "`",
+ "endCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "string.interpolated.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ }
+ ]
+ },
+ {
+ "begin": "(?<!->)\\bqq\\s*\\(",
+ "beginCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.begin.perl"
+ }
+ },
+ "end": "\\)",
+ "endCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "string.quoted.other.qq-paren.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#nested_parens_interpolated"
+ },
+ {
+ "include": "#variable"
+ }
+ ]
+ },
+ {
+ "begin": "\\bqq\\s*\\{",
+ "beginCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.begin.perl"
+ }
+ },
+ "end": "\\}",
+ "endCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "string.quoted.other.qq-brace.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#nested_braces_interpolated"
+ },
+ {
+ "include": "#variable"
+ }
+ ]
+ },
+ {
+ "begin": "\\bqq\\s*\\[",
+ "beginCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.begin.perl"
+ }
+ },
+ "end": "\\]",
+ "endCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "string.quoted.other.qq-bracket.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#nested_brackets_interpolated"
+ },
+ {
+ "include": "#variable"
+ }
+ ]
+ },
+ {
+ "begin": "\\bqq\\s*\\<",
+ "beginCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.begin.perl"
+ }
+ },
+ "end": "\\>",
+ "endCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "string.quoted.other.qq-ltgt.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#nested_ltgt_interpolated"
+ },
+ {
+ "include": "#variable"
+ }
+ ]
+ },
+ {
+ "begin": "(?<!->)\\bqx\\s*\\(",
+ "beginCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.begin.perl"
+ }
+ },
+ "end": "\\)",
+ "endCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "string.interpolated.qx-paren.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#nested_parens_interpolated"
+ },
+ {
+ "include": "#variable"
+ }
+ ]
+ },
+ {
+ "begin": "\\bqx\\s*\\{",
+ "beginCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.begin.perl"
+ }
+ },
+ "end": "\\}",
+ "endCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "string.interpolated.qx-brace.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#nested_braces_interpolated"
+ },
+ {
+ "include": "#variable"
+ }
+ ]
+ },
+ {
+ "begin": "\\bqx\\s*\\[",
+ "beginCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.begin.perl"
+ }
+ },
+ "end": "\\]",
+ "endCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "string.interpolated.qx-bracket.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#nested_brackets_interpolated"
+ },
+ {
+ "include": "#variable"
+ }
+ ]
+ },
+ {
+ "begin": "\\bqx\\s*\\<",
+ "beginCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.begin.perl"
+ }
+ },
+ "end": "\\>",
+ "endCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "string.interpolated.qx-ltgt.perl",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#nested_ltgt_interpolated"
+ },
+ {
+ "include": "#variable"
+ }
+ ]
+ },
+ {
+ "begin": "(?<!->)\\bqw?\\s*\\(",
+ "beginCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.begin.perl"
+ }
+ },
+ "end": "\\)",
+ "endCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "string.quoted.other.q-paren.perl",
+ "patterns": [
+ {
+ "include": "#nested_parens"
+ }
+ ]
+ },
+ {
+ "begin": "\\bqw?\\s*\\{",
+ "beginCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.begin.perl"
+ }
+ },
+ "end": "\\}",
+ "endCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "string.quoted.other.q-brace.perl",
+ "patterns": [
+ {
+ "include": "#nested_braces"
+ }
+ ]
+ },
+ {
+ "begin": "\\bqw?\\s*\\[",
+ "beginCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.begin.perl"
+ }
+ },
+ "end": "\\]",
+ "endCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "string.quoted.other.q-bracket.perl",
+ "patterns": [
+ {
+ "include": "#nested_brackets"
+ }
+ ]
+ },
+ {
+ "begin": "\\bqw?\\s*\\<",
+ "beginCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.begin.perl"
+ }
+ },
+ "end": "\\>",
+ "endCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "string.quoted.other.q-ltgt.perl",
+ "patterns": [
+ {
+ "include": "#nested_ltgt"
+ }
+ ]
+ },
+ {
+ "begin": "^__\\w+__",
+ "beginCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.begin.perl"
+ }
+ },
+ "end": "$",
+ "endCaptures": {
+ "0": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "string.unquoted.program-block.perl"
+ },
+ {
+ "begin": "\\b(format)\\s+(\\w+)\\s*=",
+ "beginCaptures": {
+ "1": {
+ "name": "support.function.perl"
+ },
+ "2": {
+ "name": "entity.name.function.format.perl"
+ }
+ },
+ "end": "^\\.\\s*$",
+ "name": "meta.format.perl",
+ "patterns": [
+ {
+ "include": "#line_comment"
+ },
+ {
+ "include": "#variable"
+ }
+ ]
+ },
+ {
+ "captures": {
+ "1": {
+ "name": "support.function.perl"
+ },
+ "2": {
+ "name": "entity.name.function.perl"
+ }
+ },
+ "match": "\\b(x)\\s*(\\d+)\\b"
+ },
+ {
+ "match": "\\b(ARGV|DATA|ENV|SIG|STDERR|STDIN|STDOUT|atan2|bind|binmode|bless|caller|chdir|chmod|chomp|chop|chown|chr|chroot|close|closedir|cmp|connect|cos|crypt|dbmclose|dbmopen|defined|delete|dump|each|endgrent|endhostent|endnetent|endprotoent|endpwent|endservent|eof|eq|eval|exec|exists|exp|fcntl|fileno|flock|fork|formline|ge|getc|getgrent|getgrgid|getgrnam|gethostbyaddr|gethostbyname|gethostent|getlogin|getnetbyaddr|getnetbyname|getnetent|getpeername|getpgrp|getppid|getpriority|getprotobyname|getprotobynumber|getprotoent|getpwent|getpwnam|getpwuid|getservbyname|getservbyport|getservent|getsockname|getsockopt|glob|gmtime|grep|gt|hex|import|index|int|ioctl|join|keys|kill|lc|lcfirst|le|length|link|listen|local|localtime|log|lstat|lt|m|map|mkdir|msgctl|msgget|msgrcv|msgsnd|ne|no|oct|open|opendir|ord|pack|pipe|pop|pos|print|printf|push|quotemeta|rand|read|readdir|readlink|recv|ref|rename|reset|reverse|rewinddir|rindex|rmdir|s|say|scalar|seek|seekdir|semctl|semget|semop|send|setgrent|sethostent|setnetent|setpgrp|setpriority|setprotoent|setpwent|setservent|setsockopt|shift|shmctl|shmget|shmread|shmwrite|shutdown|sin|sleep|socket|socketpair|sort|splice|split|sprintf|sqrt|srand|stat|study|substr|symlink|syscall|sysopen|sysread|system|syswrite|tell|telldir|tie|tied|time|times|tr|truncate|uc|ucfirst|umask|undef|unlink|unpack|unshift|untie|utime|values|vec|waitpid|wantarray|warn|write|y)\\b",
+ "name": "support.function.perl"
+ },
+ {
+ "captures": {
+ "1": {
+ "name": "punctuation.section.scope.begin.perl"
+ },
+ "2": {
+ "name": "punctuation.section.scope.end.perl"
+ }
+ },
+ "comment": "Match empty brackets for ↩ snippet",
+ "match": "(\\{)(\\})"
+ },
+ {
+ "captures": {
+ "1": {
+ "name": "punctuation.section.scope.begin.perl"
+ },
+ "2": {
+ "name": "punctuation.section.scope.end.perl"
+ }
+ },
+ "comment": "Match empty parenthesis for ↩ snippet",
+ "match": "(\\()(\\))"
+ }
+ ],
+ "repository": {
+ "escaped_char": {
+ "patterns": [
+ {
+ "match": "\\\\\\d+",
+ "name": "constant.character.escape.perl"
+ },
+ {
+ "match": "\\\\c[^\\s\\\\]",
+ "name": "constant.character.escape.perl"
+ },
+ {
+ "match": "\\\\g(?:\\{(?:\\w*|-\\d+)\\}|\\d+)",
+ "name": "constant.character.escape.perl"
+ },
+ {
+ "match": "\\\\k(?:\\{\\w*\\}|<\\w*>|'\\w*')",
+ "name": "constant.character.escape.perl"
+ },
+ {
+ "match": "\\\\N\\{[^\\}]*\\}",
+ "name": "constant.character.escape.perl"
+ },
+ {
+ "match": "\\\\o\\{\\d*\\}",
+ "name": "constant.character.escape.perl"
+ },
+ {
+ "match": "\\\\(?:p|P)(?:\\{\\w*\\}|P)",
+ "name": "constant.character.escape.perl"
+ },
+ {
+ "match": "\\\\x(?:[0-9a-zA-Z]{2}|\\{\\w*\\})?",
+ "name": "constant.character.escape.perl"
+ },
+ {
+ "match": "\\\\.",
+ "name": "constant.character.escape.perl"
+ }
+ ]
+ },
+ "heredoc": {
+ "patterns": [
+ {
+ "begin": "((((<<(~)?) *')(HTML)(')))(.*)\\n?",
+ "beginCaptures": {
+ "1": {
+ "name": "string.unquoted.heredoc.raw.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.string.begin.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.delimiter.begin.perl"
+ },
+ "7": {
+ "name": "punctuation.definition.delimiter.end.perl"
+ },
+ "8": {
+ "patterns": [
+ {
+ "include": "$self"
+ }
+ ]
+ }
+ },
+ "contentName": "string.unquoted.heredoc.raw.perl",
+ "end": "^((?!\\5)\\s+)?((\\6))$",
+ "endCaptures": {
+ "2": {
+ "name": "string.unquoted.heredoc.raw.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "meta.embedded.block.html",
+ "patterns": [
+ {
+ "begin": "^",
+ "end": "\\n",
+ "name": "text.html.basic",
+ "patterns": [
+ {
+ "include": "text.html.basic"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "begin": "((((<<(~)?) *')(XML)(')))(.*)\\n?",
+ "beginCaptures": {
+ "1": {
+ "name": "string.unquoted.heredoc.raw.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.string.begin.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.delimiter.begin.perl"
+ },
+ "7": {
+ "name": "punctuation.definition.delimiter.end.perl"
+ },
+ "8": {
+ "patterns": [
+ {
+ "include": "$self"
+ }
+ ]
+ }
+ },
+ "contentName": "string.unquoted.heredoc.raw.perl",
+ "end": "^((?!\\5)\\s+)?((\\6))$",
+ "endCaptures": {
+ "2": {
+ "name": "string.unquoted.heredoc.raw.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "meta.embedded.block.xml",
+ "patterns": [
+ {
+ "begin": "^",
+ "end": "\\n",
+ "name": "text.xml",
+ "patterns": [
+ {
+ "include": "text.xml"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "begin": "((((<<(~)?) *')(CSS)(')))(.*)\\n?",
+ "beginCaptures": {
+ "1": {
+ "name": "string.unquoted.heredoc.raw.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.string.begin.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.delimiter.begin.perl"
+ },
+ "7": {
+ "name": "punctuation.definition.delimiter.end.perl"
+ },
+ "8": {
+ "patterns": [
+ {
+ "include": "$self"
+ }
+ ]
+ }
+ },
+ "contentName": "string.unquoted.heredoc.raw.perl",
+ "end": "^((?!\\5)\\s+)?((\\6))$",
+ "endCaptures": {
+ "2": {
+ "name": "string.unquoted.heredoc.raw.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "meta.embedded.block.css",
+ "patterns": [
+ {
+ "begin": "^",
+ "end": "\\n",
+ "name": "source.css",
+ "patterns": [
+ {
+ "include": "source.css"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "begin": "((((<<(~)?) *')(JAVASCRIPT)(')))(.*)\\n?",
+ "beginCaptures": {
+ "1": {
+ "name": "string.unquoted.heredoc.raw.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.string.begin.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.delimiter.begin.perl"
+ },
+ "7": {
+ "name": "punctuation.definition.delimiter.end.perl"
+ },
+ "8": {
+ "patterns": [
+ {
+ "include": "$self"
+ }
+ ]
+ }
+ },
+ "contentName": "string.unquoted.heredoc.raw.perl",
+ "end": "^((?!\\5)\\s+)?((\\6))$",
+ "endCaptures": {
+ "2": {
+ "name": "string.unquoted.heredoc.raw.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "meta.embedded.block.js",
+ "patterns": [
+ {
+ "begin": "^",
+ "end": "\\n",
+ "name": "source.js",
+ "patterns": [
+ {
+ "include": "source.js"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "begin": "((((<<(~)?) *')(SQL)(')))(.*)\\n?",
+ "beginCaptures": {
+ "1": {
+ "name": "string.unquoted.heredoc.raw.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.string.begin.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.delimiter.begin.perl"
+ },
+ "7": {
+ "name": "punctuation.definition.delimiter.end.perl"
+ },
+ "8": {
+ "patterns": [
+ {
+ "include": "$self"
+ }
+ ]
+ }
+ },
+ "contentName": "string.unquoted.heredoc.raw.perl",
+ "end": "^((?!\\5)\\s+)?((\\6))$",
+ "endCaptures": {
+ "2": {
+ "name": "string.unquoted.heredoc.raw.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "meta.embedded.block.sql",
+ "patterns": [
+ {
+ "begin": "^",
+ "end": "\\n",
+ "name": "source.sql",
+ "patterns": [
+ {
+ "include": "source.sql"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "begin": "((((<<(~)?) *')(POSTSCRIPT)(')))(.*)\\n?",
+ "beginCaptures": {
+ "1": {
+ "name": "string.unquoted.heredoc.raw.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.string.begin.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.delimiter.begin.perl"
+ },
+ "7": {
+ "name": "punctuation.definition.delimiter.end.perl"
+ },
+ "8": {
+ "patterns": [
+ {
+ "include": "$self"
+ }
+ ]
+ }
+ },
+ "contentName": "string.unquoted.heredoc.raw.perl",
+ "end": "^((?!\\5)\\s+)?((\\6))$",
+ "endCaptures": {
+ "2": {
+ "name": "string.unquoted.heredoc.raw.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "meta.embedded.block.postscript",
+ "patterns": [
+ {
+ "begin": "^",
+ "end": "\\n",
+ "name": "source.postscript",
+ "patterns": [
+ {
+ "include": "source.postscript"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "begin": "((((<<(~)?) *')([^']*)(')))(.*)\\n?",
+ "beginCaptures": {
+ "1": {
+ "name": "string.unquoted.heredoc.raw.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.string.begin.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.delimiter.begin.perl"
+ },
+ "7": {
+ "name": "punctuation.definition.delimiter.end.perl"
+ },
+ "8": {
+ "patterns": [
+ {
+ "include": "$self"
+ }
+ ]
+ }
+ },
+ "contentName": "string.unquoted.heredoc.raw.perl",
+ "end": "^((?!\\5)\\s+)?((\\6))$",
+ "endCaptures": {
+ "2": {
+ "name": "string.unquoted.heredoc.raw.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ }
+ },
+ {
+ "begin": "((((<<(~)?) *\\\\)((?![=\\d\\$\\( ])[^;,'\"`\\s\\)]*)()))(.*)\\n?",
+ "beginCaptures": {
+ "1": {
+ "name": "string.unquoted.heredoc.raw.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.string.begin.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.delimiter.begin.perl"
+ },
+ "7": {
+ "name": "punctuation.definition.delimiter.end.perl"
+ },
+ "8": {
+ "patterns": [
+ {
+ "include": "$self"
+ }
+ ]
+ }
+ },
+ "contentName": "string.unquoted.heredoc.raw.perl",
+ "end": "^((?!\\5)\\s+)?((\\6))$",
+ "endCaptures": {
+ "2": {
+ "name": "string.unquoted.heredoc.raw.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ }
+ },
+ {
+ "begin": "((((<<(~)?) *\")(HTML)(\")))(.*)\\n?",
+ "beginCaptures": {
+ "1": {
+ "name": "string.unquoted.heredoc.interpolated.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.string.begin.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.delimiter.begin.perl"
+ },
+ "7": {
+ "name": "punctuation.definition.delimiter.end.perl"
+ },
+ "8": {
+ "patterns": [
+ {
+ "include": "$self"
+ }
+ ]
+ }
+ },
+ "contentName": "string.unquoted.heredoc.interpolated.perl",
+ "end": "^((?!\\5)\\s+)?((\\6))$",
+ "endCaptures": {
+ "2": {
+ "name": "string.unquoted.heredoc.interpolated.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "meta.embedded.block.html",
+ "patterns": [
+ {
+ "begin": "^",
+ "end": "\\n",
+ "name": "text.html.basic",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ },
+ {
+ "include": "text.html.basic"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "begin": "((((<<(~)?) *\")(XML)(\")))(.*)\\n?",
+ "beginCaptures": {
+ "1": {
+ "name": "string.unquoted.heredoc.interpolated.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.string.begin.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.delimiter.begin.perl"
+ },
+ "7": {
+ "name": "punctuation.definition.delimiter.end.perl"
+ },
+ "8": {
+ "patterns": [
+ {
+ "include": "$self"
+ }
+ ]
+ }
+ },
+ "contentName": "string.unquoted.heredoc.interpolated.perl",
+ "end": "^((?!\\5)\\s+)?((\\6))$",
+ "endCaptures": {
+ "2": {
+ "name": "string.unquoted.heredoc.interpolated.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "meta.embedded.block.xml",
+ "patterns": [
+ {
+ "begin": "^",
+ "end": "\\n",
+ "name": "text.xml",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ },
+ {
+ "include": "text.xml"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "begin": "((((<<(~)?) *\")(CSS)(\")))(.*)\\n?",
+ "beginCaptures": {
+ "1": {
+ "name": "string.unquoted.heredoc.interpolated.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.string.begin.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.delimiter.begin.perl"
+ },
+ "7": {
+ "name": "punctuation.definition.delimiter.end.perl"
+ },
+ "8": {
+ "patterns": [
+ {
+ "include": "$self"
+ }
+ ]
+ }
+ },
+ "contentName": "string.unquoted.heredoc.interpolated.perl",
+ "end": "^((?!\\5)\\s+)?((\\6))$",
+ "endCaptures": {
+ "2": {
+ "name": "string.unquoted.heredoc.interpolated.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "meta.embedded.block.css",
+ "patterns": [
+ {
+ "begin": "^",
+ "end": "\\n",
+ "name": "source.css",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ },
+ {
+ "include": "source.css"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "begin": "((((<<(~)?) *\")(JAVASCRIPT)(\")))(.*)\\n?",
+ "beginCaptures": {
+ "1": {
+ "name": "string.unquoted.heredoc.interpolated.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.string.begin.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.delimiter.begin.perl"
+ },
+ "7": {
+ "name": "punctuation.definition.delimiter.end.perl"
+ },
+ "8": {
+ "patterns": [
+ {
+ "include": "$self"
+ }
+ ]
+ }
+ },
+ "contentName": "string.unquoted.heredoc.interpolated.perl",
+ "end": "^((?!\\5)\\s+)?((\\6))$",
+ "endCaptures": {
+ "2": {
+ "name": "string.unquoted.heredoc.interpolated.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "meta.embedded.block.js",
+ "patterns": [
+ {
+ "begin": "^",
+ "end": "\\n",
+ "name": "source.js",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ },
+ {
+ "include": "source.js"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "begin": "((((<<(~)?) *\")(SQL)(\")))(.*)\\n?",
+ "beginCaptures": {
+ "1": {
+ "name": "string.unquoted.heredoc.interpolated.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.string.begin.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.delimiter.begin.perl"
+ },
+ "7": {
+ "name": "punctuation.definition.delimiter.end.perl"
+ },
+ "8": {
+ "patterns": [
+ {
+ "include": "$self"
+ }
+ ]
+ }
+ },
+ "contentName": "string.unquoted.heredoc.interpolated.perl",
+ "end": "^((?!\\5)\\s+)?((\\6))$",
+ "endCaptures": {
+ "2": {
+ "name": "string.unquoted.heredoc.interpolated.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "meta.embedded.block.sql",
+ "patterns": [
+ {
+ "begin": "^",
+ "end": "\\n",
+ "name": "source.sql",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ },
+ {
+ "include": "source.sql"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "begin": "((((<<(~)?) *\")(POSTSCRIPT)(\")))(.*)\\n?",
+ "beginCaptures": {
+ "1": {
+ "name": "string.unquoted.heredoc.interpolated.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.string.begin.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.delimiter.begin.perl"
+ },
+ "7": {
+ "name": "punctuation.definition.delimiter.end.perl"
+ },
+ "8": {
+ "patterns": [
+ {
+ "include": "$self"
+ }
+ ]
+ }
+ },
+ "contentName": "string.unquoted.heredoc.interpolated.perl",
+ "end": "^((?!\\5)\\s+)?((\\6))$",
+ "endCaptures": {
+ "2": {
+ "name": "string.unquoted.heredoc.interpolated.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "meta.embedded.block.postscript",
+ "patterns": [
+ {
+ "begin": "^",
+ "end": "\\n",
+ "name": "source.postscript",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ },
+ {
+ "include": "source.postscript"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "begin": "((((<<(~)?) *\")([^\"]*)(\")))(.*)\\n?",
+ "beginCaptures": {
+ "1": {
+ "name": "string.unquoted.heredoc.interpolated.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.string.begin.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.delimiter.begin.perl"
+ },
+ "7": {
+ "name": "punctuation.definition.delimiter.end.perl"
+ },
+ "8": {
+ "patterns": [
+ {
+ "include": "$self"
+ }
+ ]
+ }
+ },
+ "contentName": "string.unquoted.heredoc.interpolated.perl",
+ "end": "^((?!\\5)\\s+)?((\\6))$",
+ "endCaptures": {
+ "2": {
+ "name": "string.unquoted.heredoc.interpolated.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ }
+ ]
+ },
+ {
+ "begin": "((((<<(~)?) *)(HTML)()))(.*)\\n?",
+ "beginCaptures": {
+ "1": {
+ "name": "string.unquoted.heredoc.interpolated.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.string.begin.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.delimiter.begin.perl"
+ },
+ "7": {
+ "name": "punctuation.definition.delimiter.end.perl"
+ },
+ "8": {
+ "patterns": [
+ {
+ "include": "$self"
+ }
+ ]
+ }
+ },
+ "contentName": "string.unquoted.heredoc.interpolated.perl",
+ "end": "^((?!\\5)\\s+)?((\\6))$",
+ "endCaptures": {
+ "2": {
+ "name": "string.unquoted.heredoc.interpolated.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "meta.embedded.block.html",
+ "patterns": [
+ {
+ "begin": "^",
+ "end": "\\n",
+ "name": "text.html.basic",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ },
+ {
+ "include": "text.html.basic"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "begin": "((((<<(~)?) *)(XML)()))(.*)\\n?",
+ "beginCaptures": {
+ "1": {
+ "name": "string.unquoted.heredoc.interpolated.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.string.begin.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.delimiter.begin.perl"
+ },
+ "7": {
+ "name": "punctuation.definition.delimiter.end.perl"
+ },
+ "8": {
+ "patterns": [
+ {
+ "include": "$self"
+ }
+ ]
+ }
+ },
+ "contentName": "string.unquoted.heredoc.interpolated.perl",
+ "end": "^((?!\\5)\\s+)?((\\6))$",
+ "endCaptures": {
+ "2": {
+ "name": "string.unquoted.heredoc.interpolated.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "meta.embedded.block.xml",
+ "patterns": [
+ {
+ "begin": "^",
+ "end": "\\n",
+ "name": "text.xml",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ },
+ {
+ "include": "text.xml"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "begin": "((((<<(~)?) *)(CSS)()))(.*)\\n?",
+ "beginCaptures": {
+ "1": {
+ "name": "string.unquoted.heredoc.interpolated.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.string.begin.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.delimiter.begin.perl"
+ },
+ "7": {
+ "name": "punctuation.definition.delimiter.end.perl"
+ },
+ "8": {
+ "patterns": [
+ {
+ "include": "$self"
+ }
+ ]
+ }
+ },
+ "contentName": "string.unquoted.heredoc.interpolated.perl",
+ "end": "^((?!\\5)\\s+)?((\\6))$",
+ "endCaptures": {
+ "2": {
+ "name": "string.unquoted.heredoc.interpolated.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "meta.embedded.block.css",
+ "patterns": [
+ {
+ "begin": "^",
+ "end": "\\n",
+ "name": "source.css",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ },
+ {
+ "include": "source.css"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "begin": "((((<<(~)?) *)(JAVASCRIPT)()))(.*)\\n?",
+ "beginCaptures": {
+ "1": {
+ "name": "string.unquoted.heredoc.interpolated.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.string.begin.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.delimiter.begin.perl"
+ },
+ "7": {
+ "name": "punctuation.definition.delimiter.end.perl"
+ },
+ "8": {
+ "patterns": [
+ {
+ "include": "$self"
+ }
+ ]
+ }
+ },
+ "contentName": "string.unquoted.heredoc.interpolated.perl",
+ "end": "^((?!\\5)\\s+)?((\\6))$",
+ "endCaptures": {
+ "2": {
+ "name": "string.unquoted.heredoc.interpolated.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "meta.embedded.block.js",
+ "patterns": [
+ {
+ "begin": "^",
+ "end": "\\n",
+ "name": "source.js",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ },
+ {
+ "include": "source.js"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "begin": "((((<<(~)?) *)(SQL)()))(.*)\\n?",
+ "beginCaptures": {
+ "1": {
+ "name": "string.unquoted.heredoc.interpolated.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.string.begin.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.delimiter.begin.perl"
+ },
+ "7": {
+ "name": "punctuation.definition.delimiter.end.perl"
+ },
+ "8": {
+ "patterns": [
+ {
+ "include": "$self"
+ }
+ ]
+ }
+ },
+ "contentName": "string.unquoted.heredoc.interpolated.perl",
+ "end": "^((?!\\5)\\s+)?((\\6))$",
+ "endCaptures": {
+ "2": {
+ "name": "string.unquoted.heredoc.interpolated.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "meta.embedded.block.sql",
+ "patterns": [
+ {
+ "begin": "^",
+ "end": "\\n",
+ "name": "source.sql",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ },
+ {
+ "include": "source.sql"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "begin": "((((<<(~)?) *)(POSTSCRIPT)()))(.*)\\n?",
+ "beginCaptures": {
+ "1": {
+ "name": "string.unquoted.heredoc.interpolated.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.string.begin.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.delimiter.begin.perl"
+ },
+ "7": {
+ "name": "punctuation.definition.delimiter.end.perl"
+ },
+ "8": {
+ "patterns": [
+ {
+ "include": "$self"
+ }
+ ]
+ }
+ },
+ "contentName": "string.unquoted.heredoc.interpolated.perl",
+ "end": "^((?!\\5)\\s+)?((\\6))$",
+ "endCaptures": {
+ "2": {
+ "name": "string.unquoted.heredoc.interpolated.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "name": "meta.embedded.block.postscript",
+ "patterns": [
+ {
+ "begin": "^",
+ "end": "\\n",
+ "name": "source.postscript",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ },
+ {
+ "include": "source.postscript"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "begin": "((((<<(~)?) *)((?![=\\d\\$\\( ])[^;,'\"`\\s\\)]*)()))(.*)\\n?",
+ "beginCaptures": {
+ "1": {
+ "name": "string.unquoted.heredoc.interpolated.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.string.begin.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.delimiter.begin.perl"
+ },
+ "7": {
+ "name": "punctuation.definition.delimiter.end.perl"
+ },
+ "8": {
+ "patterns": [
+ {
+ "include": "$self"
+ }
+ ]
+ }
+ },
+ "contentName": "string.unquoted.heredoc.interpolated.perl",
+ "end": "^((?!\\5)\\s+)?((\\6))$",
+ "endCaptures": {
+ "2": {
+ "name": "string.unquoted.heredoc.interpolated.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ }
+ ]
+ },
+ {
+ "begin": "((((<<(~)?) *`)([^`]*)(`)))(.*)\\n?",
+ "beginCaptures": {
+ "1": {
+ "name": "string.unquoted.heredoc.interpolated.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.string.begin.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.delimiter.begin.perl"
+ },
+ "7": {
+ "name": "punctuation.definition.delimiter.end.perl"
+ },
+ "8": {
+ "patterns": [
+ {
+ "include": "$self"
+ }
+ ]
+ }
+ },
+ "contentName": "string.unquoted.heredoc.shell.perl",
+ "end": "^((?!\\5)\\s+)?((\\6))$",
+ "endCaptures": {
+ "2": {
+ "name": "string.unquoted.heredoc.interpolated.perl"
+ },
+ "3": {
+ "name": "punctuation.definition.string.end.perl"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ }
+ ]
+ }
+ ]
+ },
+ "line_comment": {
+ "patterns": [
+ {
+ "begin": "(^[ \\t]+)?(?=#)",
+ "beginCaptures": {
+ "1": {
+ "name": "punctuation.whitespace.comment.leading.perl"
+ }
+ },
+ "end": "(?!\\G)",
+ "patterns": [
+ {
+ "begin": "#",
+ "beginCaptures": {
+ "0": {
+ "name": "punctuation.definition.comment.perl"
+ }
+ },
+ "end": "\\n",
+ "name": "comment.line.number-sign.perl"
+ }
+ ]
+ }
+ ]
+ },
+ "nested_braces": {
+ "begin": "\\{",
+ "captures": {
+ "1": {
+ "name": "punctuation.section.scope.perl"
+ }
+ },
+ "end": "\\}",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#nested_braces"
+ }
+ ]
+ },
+ "nested_braces_interpolated": {
+ "begin": "\\{",
+ "captures": {
+ "1": {
+ "name": "punctuation.section.scope.perl"
+ }
+ },
+ "end": "\\}",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ },
+ {
+ "include": "#nested_braces_interpolated"
+ }
+ ]
+ },
+ "nested_brackets": {
+ "begin": "\\[",
+ "captures": {
+ "1": {
+ "name": "punctuation.section.scope.perl"
+ }
+ },
+ "end": "\\]",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#nested_brackets"
+ }
+ ]
+ },
+ "nested_brackets_interpolated": {
+ "begin": "\\[",
+ "captures": {
+ "1": {
+ "name": "punctuation.section.scope.perl"
+ }
+ },
+ "end": "\\]",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ },
+ {
+ "include": "#nested_brackets_interpolated"
+ }
+ ]
+ },
+ "nested_ltgt": {
+ "begin": "<",
+ "captures": {
+ "1": {
+ "name": "punctuation.section.scope.perl"
+ }
+ },
+ "end": ">",
+ "patterns": [
+ {
+ "include": "#nested_ltgt"
+ }
+ ]
+ },
+ "nested_ltgt_interpolated": {
+ "begin": "<",
+ "captures": {
+ "1": {
+ "name": "punctuation.section.scope.perl"
+ }
+ },
+ "end": ">",
+ "patterns": [
+ {
+ "include": "#variable"
+ },
+ {
+ "include": "#nested_ltgt_interpolated"
+ }
+ ]
+ },
+ "nested_parens": {
+ "begin": "\\(",
+ "captures": {
+ "1": {
+ "name": "punctuation.section.scope.perl"
+ }
+ },
+ "end": "\\)",
+ "patterns": [
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#nested_parens"
+ }
+ ]
+ },
+ "nested_parens_interpolated": {
+ "begin": "\\(",
+ "captures": {
+ "1": {
+ "name": "punctuation.section.scope.perl"
+ }
+ },
+ "end": "\\)",
+ "patterns": [
+ {
+ "comment": "This is to prevent thinks like qr/foo$/ to treat $/ as a variable",
+ "match": "\\$(?=[^\\s\\w'\\{\\[\\(\\<])",
+ "name": "keyword.control.anchor.perl"
+ },
+ {
+ "include": "#escaped_char"
+ },
+ {
+ "include": "#variable"
+ },
+ {
+ "include": "#nested_parens_interpolated"
+ }
+ ]
+ },
+ "pod": {
+ "patterns": [
+ {
+ "match": "^=(pod|back|cut)\\b",
+ "name": "storage.type.class.pod.perl"
+ },
+ {
+ "begin": "^(=begin)\\s+(html)\\s*$",
+ "beginCaptures": {
+ "1": {
+ "name": "storage.type.class.pod.perl"
+ },
+ "2": {
+ "name": "variable.other.pod.perl"
+ }
+ },
+ "contentName": "text.embedded.html.basic",
+ "end": "^(=end)\\s+(html)|^(?==cut)",
+ "endCaptures": {
+ "1": {
+ "name": "storage.type.class.pod.perl"
+ },
+ "2": {
+ "name": "variable.other.pod.perl"
+ }
+ },
+ "name": "meta.embedded.pod.perl",
+ "patterns": [
+ {
+ "include": "text.html.basic"
+ }
+ ]
+ },
+ {
+ "captures": {
+ "1": {
+ "name": "storage.type.class.pod.perl"
+ },
+ "2": {
+ "name": "variable.other.pod.perl",
+ "patterns": [
+ {
+ "include": "#pod-formatting"
+ }
+ ]
+ }
+ },
+ "match": "^(=(?:head[1-4]|item|over|encoding|begin|end|for))\\b\\s*(.*)"
+ },
+ {
+ "include": "#pod-formatting"
+ }
+ ]
+ },
+ "pod-formatting": {
+ "patterns": [
+ {
+ "captures": {
+ "1": {
+ "name": "markup.italic.pod.perl"
+ },
+ "2": {
+ "name": "markup.italic.pod.perl"
+ }
+ },
+ "match": "I(?:<([^<>]+)>|<+(\\s+(?:(?<!\\s)>|[^>])+\\s+)>+)",
+ "name": "entity.name.type.instance.pod.perl"
+ },
+ {
+ "captures": {
+ "1": {
+ "name": "markup.bold.pod.perl"
+ },
+ "2": {
+ "name": "markup.bold.pod.perl"
+ }
+ },
+ "match": "B(?:<([^<>]+)>|<+(\\s+(?:(?<!\\s)>|[^>])+\\s+)>+)",
+ "name": "entity.name.type.instance.pod.perl"
+ },
+ {
+ "captures": {
+ "1": {
+ "name": "markup.raw.pod.perl"
+ },
+ "2": {
+ "name": "markup.raw.pod.perl"
+ }
+ },
+ "match": "C(?:<([^<>]+)>|<+(\\\\s+(?:(?<!\\\\s)>|[^>])+\\\\s+)>+)",
+ "name": "entity.name.type.instance.pod.perl"
+ },
+ {
+ "captures": {
+ "1": {
+ "name": "markup.underline.link.hyperlink.pod.perl"
+ }
+ },
+ "match": "L<([^>]+)>",
+ "name": "entity.name.type.instance.pod.perl"
+ },
+ {
+ "match": "[EFSXZ]<[^>]*>",
+ "name": "entity.name.type.instance.pod.perl"
+ }
+ ]
+ },
+ "variable": {
+ "patterns": [
+ {
+ "captures": {
+ "1": {
+ "name": "punctuation.definition.variable.perl"
+ }
+ },
+ "match": "(\\$)&(?![A-Za-z0-9_])",
+ "name": "variable.other.regexp.match.perl"
+ },
+ {
+ "captures": {
+ "1": {
+ "name": "punctuation.definition.variable.perl"
+ }
+ },
+ "match": "(\\$)`(?![A-Za-z0-9_])",
+ "name": "variable.other.regexp.pre-match.perl"
+ },
+ {
+ "captures": {
+ "1": {
+ "name": "punctuation.definition.variable.perl"
+ }
+ },
+ "match": "(\\$)'(?![A-Za-z0-9_])",
+ "name": "variable.other.regexp.post-match.perl"
+ },
+ {
+ "captures": {
+ "1": {
+ "name": "punctuation.definition.variable.perl"
+ }
+ },
+ "match": "(\\$)\\+(?![A-Za-z0-9_])",
+ "name": "variable.other.regexp.last-paren-match.perl"
+ },
+ {
+ "captures": {
+ "1": {
+ "name": "punctuation.definition.variable.perl"
+ }
+ },
+ "match": "(\\$)\"(?![A-Za-z0-9_])",
+ "name": "variable.other.readwrite.list-separator.perl"
+ },
+ {
+ "captures": {
+ "1": {
+ "name": "punctuation.definition.variable.perl"
+ }
+ },
+ "match": "(\\$)0(?![A-Za-z0-9_])",
+ "name": "variable.other.predefined.program-name.perl"
+ },
+ {
+ "captures": {
+ "1": {
+ "name": "punctuation.definition.variable.perl"
+ }
+ },
+ "match": "(\\$)[_ab\\*\\.\\/\\|,\\\\;#%=\\-~^:?!\\$<>\\(\\)\\[\\]@](?![A-Za-z0-9_])",
+ "name": "variable.other.predefined.perl"
+ },
+ {
+ "captures": {
+ "1": {
+ "name": "punctuation.definition.variable.perl"
+ }
+ },
+ "match": "(\\$)[0-9]+(?![A-Za-z0-9_])",
+ "name": "variable.other.subpattern.perl"
+ },
+ {
+ "captures": {
+ "1": {
+ "name": "punctuation.definition.variable.perl"
+ }
+ },
+ "match": "([\\$\\@\\%](#)?)([a-zA-Zx7f-xff\\$]|::)([a-zA-Z0-9_x7f-xff\\$]|::)*\\b",
+ "name": "variable.other.readwrite.global.perl"
+ },
+ {
+ "captures": {
+ "1": {
+ "name": "punctuation.definition.variable.perl"
+ },
+ "2": {
+ "name": "punctuation.definition.variable.perl"
+ }
+ },
+ "match": "(\\$\\{)(?:[a-zA-Zx7f-xff\\$]|::)(?:[a-zA-Z0-9_x7f-xff\\$]|::)*(\\})",
+ "name": "variable.other.readwrite.global.perl"
+ },
+ {
+ "captures": {
+ "1": {
+ "name": "punctuation.definition.variable.perl"
+ }
+ },
+ "match": "([\\$\\@\\%](#)?)[0-9_]\\b",
+ "name": "variable.other.readwrite.global.special.perl"
+ }
+ ]
+ }
+ }
+} \ No newline at end of file
diff --git a/server/src/completion.ts b/server/src/completion.ts
index c4e48fc..f32188e 100644
--- a/server/src/completion.ts
+++ b/server/src/completion.ts
@@ -110,7 +110,7 @@ function getMatches(perlDoc: PerlDocument, symbol: string, replace: Range): Com
if(knownObject){
const targetVar = perlDoc.canonicalElems.get(knownObject[1]);
if(targetVar){
- qualifiedSymbol = qualifiedSymbol.replace(/^\$\w+(?=:)/, targetVar.type);
+ qualifiedSymbol = qualifiedSymbol.replace(/^\$\w+(?=:)/, targetVar.typeDetail);
bKnownObj = true;
}
}
@@ -191,15 +191,13 @@ function buildMatches(lookupName: string, elem: PerlElem, range: Range): Complet
let documentation: MarkupContent | undefined = undefined;
let docs: string[] = [];
- if (elem.type.length > 1 || ( ["v", "c"].includes(elem.type) && lookupName == '$self')) {
- // We either know the object type, or it's $self
+ if ( ["v", "c", "1"].includes(elem.type) && elem.typeDetail.length > 0) {
kind = CompletionItemKind.Variable;
- if(elem.type.length > 1 ){
- detail = `${lookupName}: ${elem.type}`;
- } else if (lookupName == '$self') {
- // elem.package can be misleading if you use $self in two different packages in the same module. Get scoped matches will address this
- detail = `${lookupName}: ${elem.package}`;
- }
+ detail = `${lookupName}: ${elem.typeDetail}`;
+ } else if ( ["v", "c", "1"].includes(elem.type) && lookupName == '$self' ) {
+ kind = CompletionItemKind.Variable;
+ // elem.package can be misleading if you use $self in two different packages in the same module. Get scoped matches will address this
+ detail = `${lookupName}: ${elem.package}`;
} else if(elem.type == PerlSymbolKind.LocalVar){
kind = CompletionItemKind.Variable;
} else if(elem.type == PerlSymbolKind.ImportedVar){
diff --git a/server/src/diagnostics.ts b/server/src/diagnostics.ts
index d15cf49..ce8d4d5 100644
--- a/server/src/diagnostics.ts
+++ b/server/src/diagnostics.ts
@@ -9,8 +9,9 @@ import {
import { dirname, join } from 'path';
import Uri from 'vscode-uri';
import { getIncPaths, getPerlimportsProfile, async_execFile, nLog } from './utils';
-import { buildNav } from "./parseDocument";
+import { buildNav } from "./parseTags";
import { getPerlAssetsPath } from "./assets";
+import { parseDocument } from './parser';
import {
TextDocument
@@ -25,6 +26,8 @@ export async function perlcompile(textDocument: TextDocument, workspaceFolders:
perlParams = perlParams.concat(getInquisitor());
nLog("Starting perl compilation check with the equivalent of: " + settings.perlPath + " " + perlParams.join(" ") + " " + filePath, settings);
+ const parsingPromise = parseDocument(textDocument);
+
let output: string;
let stdout: string;
let severity: DiagnosticSeverity;
@@ -56,15 +59,17 @@ export async function perlcompile(textDocument: TextDocument, workspaceFolders:
}
}
- const perlDoc = await buildNav(stdout, filePath, textDocument.uri);
+ const compiledDoc = buildNav(stdout, filePath, textDocument.uri);
+ const parsedDoc = await parsingPromise;
+ const mergedDoc = mergeDocs(parsedDoc, compiledDoc);
output.split("\n").forEach(violation => {
- maybeAddCompDiag(violation, severity, diagnostics, filePath, perlDoc);
+ maybeAddCompDiag(violation, severity, diagnostics, filePath, mergedDoc);
});
// If a base object throws a warning multiple times, we want to deduplicate it to declutter the problems tab.
const uniq_diagnostics = Array.from(new Set(diagnostics.map(diag => JSON.stringify(diag)))).map(str => JSON.parse(str));
- return {diags: uniq_diagnostics, perlDoc: perlDoc};
+ return {diags: uniq_diagnostics, perlDoc: mergedDoc};
}
function getInquisitor(): string[]{
@@ -323,3 +328,21 @@ function getCriticDiagnosticSeverity(severity_num: string, settings: NavigatorSe
return DiagnosticSeverity.Error;
}
}
+
+
+
+function mergeDocs(doc1: PerlDocument, doc2: PerlDocument){
+ // TODO: Redo this code. Instead of merging sources, you should keep track of where symbols came from
+
+ doc1.autoloads = new Map([...doc1.autoloads, ...doc2.autoloads]);
+ doc1.canonicalElems = new Map([...doc1.canonicalElems, ...doc2.canonicalElems]);
+
+ // TODO: Should elems be merged? Probably. Or tagged doc and compilation results are totally split
+ doc1.elems = new Map([...doc2.elems, ...doc1.elems]); // Tagged docs have priority?
+ doc1.imported = new Map([...doc1.imported, ...doc2.imported]);
+ doc1.parents = new Map([...doc1.parents, ...doc2.parents]);
+ doc1.filePath = doc2.filePath;
+ doc1.uri = doc2.uri;
+
+ return doc1;
+} \ No newline at end of file
diff --git a/server/src/hover.ts b/server/src/hover.ts
index 242759f..46534da 100644
--- a/server/src/hover.ts
+++ b/server/src/hover.ts
@@ -30,14 +30,12 @@ export function getHover(params: TextDocumentPositionParams, perlDoc: PerlDocume
function buildHoverDoc(symbol: string, elem: PerlElem){
let desc = "";
- if (elem.type.length > 1 || ( ["v", "c"].includes(elem.type) && /^\$self/.test(symbol))) {
+
+ if ( ["v", "c", "1"].includes(elem.type) && elem.typeDetail.length > 0) {
+ desc = "(object) " + `${elem.typeDetail}`;
+ } else if ( ["v", "c", "1"].includes(elem.type) && /^\$self/.test(symbol) ) {
// We either know the object type, or it's $self
- desc = "(object) ";
- if(elem.type.length > 1 ){
- desc += `${elem.type}`;
- } else if (/^\$self/.test(symbol)) {
- desc += `${elem.package}`;
- }
+ desc = "(object) " + `${elem.package}`;
} else if(elem.type == 'v'){
// desc = `(variable) ${symbol}`; // Not very interesting info
} else if (elem.type == 'n'){
diff --git a/server/src/parseDocument.ts b/server/src/parseTags.ts
index 54d79db..68847f1 100644
--- a/server/src/parseDocument.ts
+++ b/server/src/parseTags.ts
@@ -2,7 +2,7 @@
import { PerlDocument, PerlElem, PerlImport, PerlSymbolKind} from "./types";
-export async function buildNav(stdout: string, filePath: string, fileuri: string): Promise<PerlDocument> {
+export function buildNav(stdout: string, filePath: string, fileuri: string): PerlDocument {
stdout = stdout.replace(/\r/g, ""); // Windows
diff --git a/server/src/parser.ts b/server/src/parser.ts
new file mode 100644
index 0000000..05e0b4f
--- /dev/null
+++ b/server/src/parser.ts
@@ -0,0 +1,570 @@
+
+
+import { start } from "repl";
+import { PerlDocument, PerlElem, PerlImport, PerlSymbolKind} from "./types";
+import { TextDocument } from 'vscode-languageserver-textdocument';
+import Uri from 'vscode-uri';
+import fs = require('fs');
+import path = require('path');
+import vsctm = require('vscode-textmate');
+import oniguruma = require('vscode-oniguruma');
+import { nextTick } from "process";
+
+function init_doc (textDocument: TextDocument): PerlDocument {
+
+ // We probably dont need this
+ const filePath = Uri.parse(textDocument.uri).fsPath;
+
+
+ let perlDoc: PerlDocument = {
+ elems: new Map(),
+ canonicalElems: new Map(),
+ autoloads: new Map(),
+ imported: new Map(),
+ parents: new Map(),
+ filePath: filePath,
+ uri: textDocument.uri,
+ };
+
+ return perlDoc;
+}
+
+export async function parseDocument(textDocument: TextDocument ): Promise<PerlDocument> {
+
+ let perlDoc = init_doc(textDocument);
+
+ const codeArray = await cleanCode(textDocument, perlDoc);
+ let sActiveOO: Map<string, boolean> = new Map(); // Keep track of OO frameworks in use to keep down false alarms on field vs has vs attr
+ // Loop through file
+ const file = Uri.parse(textDocument.uri).fsPath;
+ let package_name = "";
+ let var_continues: boolean = false;
+
+ for (let line_num = 0; line_num < codeArray.length; line_num++) {
+ let stmt = codeArray[line_num];
+ // Nothing left? Never mind.
+ if (!stmt) {
+ continue;
+ }
+
+ // TODO, allow specifying list of constructor names as config
+ // Declaring an object. Let's store the type
+ let match;
+ // my $constructors = qr/(?:new|connect)/;
+
+ if ((match = stmt.match(/^(?:my|our|local|state)\s+(\$\w+)\s*\=\s*([\w\:]+)\-\>new\s*(?:\((?!.*\)\->)|;)/ )) ||
+ (match = stmt.match(/^(?:my|our|local|state)\s+(\$\w+)\s*\=\s*new (\w[\w\:]+)\s*(?:\((?!.*\)\->)|;)/))) {
+ let varName = match[1];
+ let objName = match[2];
+ MakeElem(varName, PerlSymbolKind.LocalVar, objName, file, package_name, line_num, perlDoc);
+
+ var_continues = false; // We skipped ahead of the line here.
+ }
+ // This is a variable declaration if one was started on the previous
+ // line, or if this line starts with my or local
+ else if (var_continues || (match = stmt.match(/^(?:my|our|local|state)\b/))) {
+ // The declaration continues if the line does not end with ;
+ var_continues = (!stmt.endsWith(";") && !stmt.match(/[\)\=\}\{]/));
+
+ // Remove my or local from statement, if present
+ stmt = stmt.replace(/^(my|our|local|state)\s+/, "");
+
+ // Remove any assignment piece
+ stmt = stmt.replace(/\s*=.*/, "");
+
+ // Remove part where sub starts (for signatures). Consider other options here.
+ stmt = stmt.replace(/\s*\}.*/, "");
+
+ // Now find all variable names, i.e. "words" preceded by $, @ or %
+ let vars = stmt.matchAll(/([\$\@\%][\w:]+)\b/g);
+
+ for (let match of vars) {
+ MakeElem(match[1], PerlSymbolKind.LocalVar, '', file, package_name, line_num, perlDoc);
+ }
+ }
+
+ // Lexical loop variables, potentially with labels in front. foreach my $foo
+ else if ((match = stmt.match(/^(?:(\w+)\s*:(?!\:))?\s*(?:for|foreach)\s+my\s+(\$[\w]+)\b/))) {
+ if (match[1]) {
+ MakeElem(match[1], PerlSymbolKind.Label, '', file, package_name, line_num, perlDoc);
+ }
+ MakeElem(match[2], PerlSymbolKind.LocalVar, '', file, package_name, line_num, perlDoc);
+ }
+
+ // Lexical match variables if(my ($foo, $bar) ~= ). Optional to detect (my $newstring = $oldstring) =~ s/foo/bar/g;
+ else if ((match = stmt.match(/^(?:\}\s*elsif|if|unless|while|until|for)?\s*\(\s*my\b(.*)$/))) {
+ // Remove any assignment piece
+ stmt = stmt.replace(/\s*=.*/, "");
+ let vars = stmt.matchAll(/([\$\@\%][\w]+)\b/g);
+ for (let match of vars) {
+ MakeElem(match[1], PerlSymbolKind.LocalVar, '', file, package_name, line_num, perlDoc);
+ }
+ }
+
+ // Try-catch exception variables
+ else if ((match = stmt.match(/^\}?\s*catch\s*\(\s*(\$\w+)\s*\)\s*\{?$/))) {
+ MakeElem(match[1], PerlSymbolKind.LocalVar, '', file, package_name, line_num, perlDoc);
+ }
+
+ // This is a package declaration if the line starts with package
+ else if ((match = stmt.match(/^package\s+([\w:]+)/))) {
+ // Get name of the package
+ package_name = match[1];
+ const endLine = PackageEndLine(line_num, codeArray);
+ MakeElem(package_name, PerlSymbolKind.Package, '', file, package_name, line_num, perlDoc, endLine);
+ }
+
+ // This is a class decoration for Object::Pad, Corinna, or Moops
+ else if((match = stmt.match(/^class\s+([\w:]+)/))){
+ let class_name = match[1];
+ const endLine = PackageEndLine(line_num, codeArray);
+ MakeElem(class_name, PerlSymbolKind.Class, '', file, package_name, line_num, perlDoc, endLine);
+ }
+
+ else if((match = stmt.match(/^role\s+([\w:]+)/))){
+ const roleName = match[1];
+ const endLine = SubEndLine(line_num, codeArray);
+ MakeElem(roleName, PerlSymbolKind.Role, '', file, package_name, line_num, perlDoc, endLine);
+ }
+
+ // This is a sub declaration if the line starts with sub
+ else if ((match = stmt.match(/^(?:async\s+)?(sub)\s+([\w:]+)(\s+:method)?([^{]*)/)) ||
+ (match = stmt.match(/^(?:async\s+)?(method)\s+\$?([\w:]+)()([^{]*)/)) ||
+ (sActiveOO.get("Function::Parameters") && (match = stmt.match(/^(fun)\s+([\w:]+)()([^{]*)/ )))
+ ) {
+ const subName = match[2];
+ const signature = match[4];
+ const kind = (match[1] === 'method' || match[3]) ? PerlSymbolKind.LocalMethod : PerlSymbolKind.LocalSub;
+ const endLine = SubEndLine(line_num, codeArray);
+
+ MakeElem(subName, kind, '', file, package_name, line_num, perlDoc, endLine);
+ // Match the after the sub declaration and before the start of the actual sub for signatures (if any)
+ const vars = signature.matchAll(/([\$\@\%][\w:]+)\b/g);
+
+ // Define subrountine signatures, but exclude prototypes
+ // The declaration continues if the line does not end with ;
+ var_continues = !(stmt.match(/;$/) || stmt.match(/[\)\=\}\{]/));
+
+ for (const matchvar of vars) {
+ MakeElem(matchvar[1], PerlSymbolKind.LocalVar,'', file, package_name, line_num, perlDoc);
+ }
+ }
+
+ // Phaser block
+ else if ((match = stmt.match(/^(BEGIN|INIT|CHECK|UNITCHECK|END)\s*\{/))) {
+ const phaser = match[1];
+ const endLine = SubEndLine(line_num, codeArray);
+
+ MakeElem(phaser, PerlSymbolKind.Phaser, '', file, package_name, line_num, perlDoc, endLine);
+ }
+
+ // Label line
+ else if ((match = stmt.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*:[^:].*{\s*$/))) {
+ const label = match[1];
+ const endLine = SubEndLine(line_num, codeArray);
+
+ MakeElem(label, PerlSymbolKind.Label, '', file, package_name, line_num, perlDoc, endLine);
+ }
+
+ // Constants. Important because they look like subs (and technically are), so I'll tags them as such
+ else if ((match = stmt.match(/^use\s+constant\s+(\w+)\b/))) {
+ MakeElem(match[1], PerlSymbolKind.Constant, '', file, package_name, line_num, perlDoc);
+ MakeElem("constant", 'u', '', file, package_name, line_num, perlDoc);
+ }
+
+ // Moo/Moose/Object::Pad/Moops/Corinna attributes
+ else if ((match = stmt.match(/^(?:has|field)(?:\s+|\()["']?([\$@%]?\w+)\b/))) {
+ const attr = match[1];
+ let type;
+ if(attr.match(/^\w/)){
+ type = PerlSymbolKind.Field;
+ // If you have a locally defined package/class Foo want to reference the attributes as Foo::attr or foo->attr, you need the full path.
+ // Subs don't need this since we find them at compile time. We also find "d" types from imported packages in Inquisitor.pm
+ MakeElem(package_name + "::" + attr, PerlSymbolKind.PathedField, '', file, package_name, line_num, perlDoc);
+ } else {
+ type = PerlSymbolKind.LocalVar;
+ }
+ // TODO: Define new type. Class variables should probably be shown in the Outline view even though lexical variables are not
+ MakeElem(attr, type, '', file, package_name, line_num, perlDoc);
+ }
+
+ // Is this capture above?
+ // else if (sActiveOO.get("Object::Pad") &&
+ // (match = stmt.match(/^field\s+([\$@%]\w+)\b/))) { // Object::Pad field
+ // const attr = match[1];
+ // MakeElem(attr, PerlSymbolKind.LocalVar, '', file, package_name, line_num, perlDoc);
+ // }
+
+ else if ((sActiveOO.get("Mars::Class") || sActiveOO.get("Venus::Class"))
+ && (match = stmt.match(/^attr\s+["'](\w+)\b/))) { // Mars attributes
+ const attr = match[1];
+ MakeElem(attr, PerlSymbolKind.Field, '', file, package_name, line_num, perlDoc);
+ MakeElem(package_name + "::" + attr, PerlSymbolKind.PathedField, '', file, package_name, line_num, perlDoc);
+ }
+
+ else if ((match = stmt.match(/^around\s+["']?(\w+)\b/))) { // Moo/Moose overriding subs.
+ MakeElem(match[1], PerlSymbolKind.LocalSub, '', file, package_name, line_num, perlDoc);
+ }
+
+ else if ((match = stmt.match(/^use\s+([\w:]+)\b/))) { // Keep track of explicit imports for filtering
+ const importPkg = match[1];
+ MakeElem(importPkg, "u", '', file, package_name, line_num, perlDoc);
+ sActiveOO.set(importPkg, true);
+ }
+
+ else if(MatchDancer(stmt, line_num, sActiveOO, file, package_name, perlDoc, codeArray)) {
+ // Self contained
+ }
+
+ else if ((match = stmt.match(/^\$self\->\{\s*(['"]|)_(\w+)\1\s*\}\s*=/))) { // Common paradigm is for autoloaders to basically just point to the class variable
+ const variable = match[2];
+ MakeElem("get_" + variable, PerlSymbolKind.AutoLoadVar, '', file, package_name, line_num, perlDoc);
+ }
+
+ }
+ return perlDoc;
+}
+
+
+function MatchDancer(stmt: string, line_num: number, sActiveOO: Map<string, boolean>,
+ file: string, package_name: string, perlDoc: PerlDocument, codeArray: string[]): boolean {
+
+
+ if(!(sActiveOO.has("Dancer") || sActiveOO.has("Dancer2") || sActiveOO.has("Mojolicious::Lite"))) {
+ return false;
+ }
+
+ //const rFilter = /qr\{[^\}]+\}/ ;
+ let match;
+ if( (match = stmt.match(/^(?:any|before\_route)\s+\[([^\]]+)\]\s+(?:=>\s*)?(['"])([^"']+)\2\s*=>\s*sub/))) {
+ // Multiple request routing paths
+ let requests = match[1];
+ let route = match[3];
+ // TODO: Put this back
+ requests = requests.replace(/['"\s\n]+/g, "");
+ route = `${requests} ${route}`;
+ const endLine = SubEndLine(line_num, codeArray);
+ MakeElem(route, PerlSymbolKind.HttpRoute, '', file, package_name, line_num, perlDoc, endLine);
+
+ // TODO: I think this is a bug with [^\2] not working
+ // any ['get', 'post'] => '/login' => sub {
+
+ } else if ((match = stmt.match(/^(get|any|post|put|patch|delete|del|options|ajax|before_route)\s+(?:[\s\w,\[\]'"]+=>\s*)?(['"])([^'"]+)\2\s*=>\s*sub/))) {
+ // Routing paths
+ let route = match[1] + " " + match[3];
+ const endLine = SubEndLine(line_num, codeArray);
+ MakeElem(route, PerlSymbolKind.HttpRoute, '', file, package_name, line_num, perlDoc, endLine);
+ } else if ((match = stmt.match(/^(get|any|post|put|patch|delete|del|options|ajax|before_route)\s+(qr\{[^\}]+\})\s+\s*=>\s*sub/))) {
+ // Regexp routing paths
+ let route = match[1] + " " + match[2];
+ const endLine = SubEndLine(line_num, codeArray);
+ MakeElem(route, PerlSymbolKind.HttpRoute, '', file, package_name, line_num, perlDoc, endLine);
+ } else if ((match = stmt.match(/^(?:hook)\s+(['"]|)(\w+)\1\s*=>\s*sub/))) {
+ // Hooks
+ let hook = match[2];
+ const endLine = SubEndLine(line_num, codeArray);
+ MakeElem(hook, PerlSymbolKind.HttpRoute, '', file, package_name, line_num, perlDoc, endLine);
+ } else {
+ return false;
+ }
+ return true; // Must've matched
+}
+
+
+async function cleanCode(textDocument: TextDocument, perlDoc: PerlDocument): Promise<string[]> {
+ let code = textDocument.getText();
+
+ const codeArray = code.split("\n");
+ // const offset = textDocument.offsetAt(textDocument.positionAt(0));
+ let codeClean = [];
+
+ for (let i=0; i<codeArray.length;i++){
+ let stmt = codeArray[i];
+
+ let match;
+ if ((match = stmt.match(/#.*(\$\w+) isa ([\w:]+)\b/))){
+ const pvar = match[1];
+ const typeName = match[2];
+ // TODO: Do I need a file or package here? Canonical variables are weird
+ MakeElem(pvar, PerlSymbolKind.Canonical, typeName, "", "", i, perlDoc);
+ }
+
+ stmt = stmt.replace(/^\s*/, "");
+ stmt = stmt.replace(/\s*$/, "");
+
+ codeClean.push(stmt);
+ }
+
+ codeClean = await stripCommentsAndQuotes(codeClean);
+
+ return codeClean;
+}
+
+
+
+
+
+function MakeElem(name: string, type: PerlSymbolKind | 'u' | '2',
+ typeDetail: string, file: string, pack: string, line: number, perlDoc: PerlDocument,
+ lineEnd: number = 0): void {
+
+ if(!name) return; // Don't store empty names (shouldn't happen)
+
+ if(lineEnd == 0){
+ lineEnd = line;
+ }
+
+ if (type == 'u'){
+ // Explictly loaded module. Helpful for focusing autocomplete results
+ perlDoc.imported.set(name, line);
+ // if(/\bDBI$/.exec(name)) perlDoc.imported.set(name + "::db", true); // TODO: Build mapping of common constructors to types
+ return; // Don't store it as an element
+ }
+
+ if (type == '2'){
+ perlDoc.parents.set(name, typeDetail);
+ return; // Don't store it as an element
+ }
+
+ const newElem: PerlElem = {
+ name: name,
+ type: type,
+ typeDetail: typeDetail,
+ file: file,
+ package: pack,
+ line: line,
+ lineEnd: lineEnd,
+ value: "",
+ };
+
+ if (type == '3'){
+ perlDoc.autoloads.set(name, newElem);
+ return; // Don't store it as an element
+ }
+
+
+ if(typeDetail.length > 0){
+ // TODO: The canonicalElems don't need to be PerlElems, they might be just a string.
+ // We overwrite, so the last typed element is the canonical one. No reason for this.
+ perlDoc.canonicalElems.set(name, newElem);
+ if (type == '1'){
+ // This object is only intended as the canonicalLookup, not for anything else.
+ return;
+ }
+ }
+
+ let array = perlDoc.elems.get(name) || [];
+ array.push(newElem)
+ perlDoc.elems.set(name, array);
+
+ return;
+}
+
+
+
+function SubEndLine (start_line: number, code: String[], rFilter: RegExp | null = null) : number {
+
+ let pos = 0;
+ let found = false;
+
+ for (let i = start_line; i < code.length; i++) {
+ // Perhaps limit the max depth?
+ let stmt = code[i];
+
+
+ if(i == start_line){
+ if(rFilter){
+ stmt.replace(rFilter, "");
+ }
+ // Default argument of empty hash. Other types of hashes may still trip this up
+ stmt.replace(/\$\w+\s*=\s*\{\s*\}/,"");
+ }
+
+ stmt.split('').forEach((char: string) => {
+ if(char == '{'){
+ // You may just be finding default function args = {}
+ found = true;
+ pos++;
+ } else if(char == '}') {
+ pos--;
+ }
+ });
+ // Checking outside the statement is faster, but less accurate
+ if(found && pos == 0){
+ return i;
+ }
+ }
+ return start_line;
+}
+
+
+function PackageEndLine (start_line: number, code: String[]) {
+
+ if (code[start_line].match(/(class|package)[^#]+;/)){
+
+ // Single line package definition.
+ if (code[start_line].match(/{.*(class|package)/)){
+ // Will need to hunt for the end
+ }else if (start_line > 0 && code[start_line-1].match(/\{[^}]*$/)){
+ start_line -= 1;
+ }
+ }
+
+ let pos = 0;
+ let found = false;
+
+ for (let i = start_line; i < code.length; i++) {
+ // Perhaps limit the max depth?
+ let stmt = code[i];
+ stmt.split('').forEach((char: string) => {
+ if(char == '{') {
+ found = true;
+ pos++;
+ } else if(char == '}') {
+ pos--;
+ }
+ });
+
+ if(found == false){
+ // If we haven't found the start of the package block, there probably isn't one.
+ if(stmt.match(/;/) || (i - start_line > 1)){
+ break;
+ }
+ }
+
+ // Checking outside the forEach statement is faster, but less accurate
+ if(found && pos == 0){
+ return i;
+ }
+ }
+
+ for (let i = start_line+1; i < code.length; i++) {
+ // TODO: update with class inheritance / version numbers, etc
+ // Although should we do with nested packages/classes? (e.g. Pack A -> Pack B {} -> A)
+ if(code[i].match(/^\s*(class|package)\s+([\w:]+)/)){
+ return i-1;
+ }
+ }
+
+ // If we didn't find an end, run until end of file
+ return code.length;
+}
+
+
+const wasmBin = fs.readFileSync(path.join(__dirname, './../node_modules/vscode-oniguruma/release/onig.wasm')).buffer;
+const vscodeOnigurumaLib = oniguruma.loadWASM(wasmBin).then(() => {
+ return {
+ createOnigScanner(patterns: any) { return new oniguruma.OnigScanner(patterns); },
+ createOnigString(s: any) { return new oniguruma.OnigString(s); }
+ };
+});
+
+const registry = new vsctm.Registry({
+ onigLib: vscodeOnigurumaLib,
+ loadGrammar: async (scopeName) => {
+ const grammarpath = path.join(__dirname,'./../perl.tmLanguage.json');
+ const grammar = await fs.promises.readFile(grammarpath, 'utf8');
+ return vsctm.parseRawGrammar(grammar, grammarpath);
+ }
+});
+
+
+async function stripCommentsAndQuotes(code: string[]): Promise<string[]> {
+ const grammar = await registry.loadGrammar('source.perl');
+ if (!grammar) {
+ throw new Error("Couldn't load Textmate grammar");
+ }
+
+ let ruleStack: vsctm.StateStack | null = vsctm.INITIAL;
+ let codeStripped = [];
+
+ for (const line of code) {
+ const result = grammar.tokenizeLine(line, ruleStack);
+ ruleStack = result.ruleStack;
+ let strippedCode = '';
+
+ let lastEndIndex = 0;
+ for (const token of result.tokens) {
+ const content = line.substring(lastEndIndex, token.endIndex);
+ lastEndIndex = token.endIndex;
+
+ // This includes regexes and pod too
+ const isComment = token.scopes.some(scope => scope.startsWith('comment') );
+
+ if(isComment){
+ // Remove all comments
+ continue;
+ }
+
+ const isString = token.scopes.some(scope => scope.startsWith('string') );
+ const isPunc = token.scopes.some(scope => scope.startsWith('punctuation') );
+
+ if(isString && !isPunc){
+ if(strippedCode == ''){
+ // The 2nd-Nth lines of multi-line strings should be stripped
+ strippedCode += "___";
+ continue;
+ }else if(content.match(/[\{\}]/)) {
+ // In-line strings that contains {} need to be stripped regardless of position
+ continue;
+ }
+ }
+ strippedCode += content;
+ }
+ codeStripped.push(strippedCode);
+ }
+
+ return codeStripped;
+}
+
+
+
+// import Parser = require('tree-sitter');
+// import { SyntaxNode } from 'tree-sitter';
+// const Perl = require('./../tree-sitter-perl');
+
+
+// export function tagSource () {
+// return "Example";
+// }
+// // const parser = new Parser();
+// // parser.setLanguage(Perl);
+
+// const sourceCode = `
+// # This is a comment
+// my $foo = "Hello, world"; # Another comment
+// my $bar = "
+// Multiline string
+// ";
+// `;
+
+// const tree = parser.parse(sourceCode);
+
+// // Define a function to strip comments and quotes but preserve line breaks
+// function stripCommentsAndQuotes(node: SyntaxNode, code: string[]) {
+// if (node.type === 'comment') {
+// const start = node.startIndex;
+// const end = node.endIndex;
+// for (let i = start; i < end; i++) {
+// if (code[i] !== '\n') { // Preserve line breaks
+// code[i] = ' ';
+// }
+// }
+// } else if (node.type === 'string') {
+// // TODO: if the opening and closing quotes are actually quotes.
+// const start = node.startIndex + 1; // Start after the opening quote
+// const end = node.endIndex - 1; // Stop before the closing quote
+// for (let i = start; i < end; i++) {
+// if (code[i] !== '\n') { // Preserve line breaks
+// code[i] = ' ';
+// }
+// }
+// }
+// for (const child of node.children) {
+// stripCommentsAndQuotes(child, code);
+// }
+// }
+
+// const codeArray = Array.from(sourceCode);
+// stripCommentsAndQuotes(tree.rootNode, codeArray);
+// const strippedCode = codeArray.join(''); \ No newline at end of file
diff --git a/server/src/perl/Inquisitor.pm b/server/src/perl/Inquisitor.pm
index c544046..dafa1b3 100644
--- a/server/src/perl/Inquisitor.pm
+++ b/server/src/perl/Inquisitor.pm
@@ -337,6 +337,7 @@ sub dump_loaded_mods {
foreach my $key_to_print (@$filtered_modules) {
my $path = $displays->{$key_to_print};
+ next if !$path; # If we don't have a path, the modHunter module would be better
print_tag("$key_to_print", "m", "", $path, $key_to_print, 0, "");
}
return;
diff --git a/server/src/perl/criticWrapper.pl b/server/src/perl/criticWrapper.pl
index d5543e1..0428520 100644
--- a/server/src/perl/criticWrapper.pl
+++ b/server/src/perl/criticWrapper.pl
@@ -71,6 +71,7 @@ sub adjustForKeywords {
# classes become packages (which they are) to support RequireExplicitPackage and RequireFilenameMatchesPackage
$sSource =~ s/^(\h*)class\h(?=\h*\w)/${1}package /gm;
+ # Should these be mangled? Subroutines::ProhibitBuiltinHomonyms triggers on these
# ADJUST blocks and similar are not processed correctly since they aren't recognized. Important for Modules::RequireEndWithOne
$sSource =~ s/^(\h*)(ADJUST|ADJUST\h+:params|ADJUSTPARAMS|BUILD)(?=\h*\s?(\{|\())/${1}sub $2/gm;
diff --git a/server/src/perl/defaultCriticProfile b/server/src/perl/defaultCriticProfile
index baf2af4..926db41 100644
--- a/server/src/perl/defaultCriticProfile
+++ b/server/src/perl/defaultCriticProfile
@@ -35,6 +35,11 @@ allow = vars subs refs
[Modules::RequireExplicitPackage]
allow_import_of = Object::Pad feature Feature::Compat::Class experimental
+# This is an odd one. Currently, the ADJUST is not recognized as a special Block by PPI, so I magled the code to "sub ADJUST", which then causes other issues.
+# Perhaps there's a better mangling, or PPI can be updated
+[Subroutines::ProhibitBuiltinHomonyms]
+allow = ADJUST
+
# Not a default policy, but these exemptions are useful. I'm sure many others exist as well.
[Subroutines::ProhibitCallsToUndeclaredSubs]
-exempt_subs = Object::Pad::class Object::Pad::field Object::Pad::role Object::Pad::has Object::Pad::param Object::Pad::reader Object::Pad::writer Object::Pad::mutator Object::Pad::accessor experimental::field experimental::class experimental::param feature::field feature::class feature::param Future::AsyncAwait::await Future::AsyncAwait::async feature::try feature::catch Feature::Compat::Try::try Feature::Compat::Try::catch \ No newline at end of file
+exempt_subs = Moo::has Moose::has Object::Pad::class Object::Pad::field Object::Pad::role Object::Pad::has Object::Pad::param Object::Pad::reader Object::Pad::writer Object::Pad::mutator Object::Pad::accessor experimental::field experimental::class experimental::param feature::field feature::class feature::param Future::AsyncAwait::await Future::AsyncAwait::async feature::try feature::catch Feature::Compat::Try::try Feature::Compat::Try::catch \ No newline at end of file
diff --git a/server/src/perl/lib_bs22/pltags.pm b/server/src/perl/lib_bs22/pltags.pm
index 3238929..4080a6a 100644
--- a/server/src/perl/lib_bs22/pltags.pm
+++ b/server/src/perl/lib_bs22/pltags.pm
@@ -27,111 +27,6 @@ sub new {
return $self;
}
-# Create a tag file line and push it on the list of found tags
-sub MakeTag {
- my (
- $self,
- $tag, # Tag name
- $type, # Type of tag
- $typeDetails, # Additional details on type
- $line_number,
- ) = @_; # Line in which tag appears
-
- # Only process tag if not empty
- if ($tag) {
-
- my $package_name = $self->{package};
- my $file = $self->{file};
- # Create a tag line
- my $tagline = "$tag\t$type\t$typeDetails\t$file\t$package_name\t$line_number\t";
-
- # Push it on the stack
- push(@{$self->{tags}}, $tagline);
- }
-}
-
-sub _sub_end_line {
- my ($self, $line_num, $first_line_regex) = @_;
-
- my $paCode = $self->{code_for_balanced};
- my $offset = $self->{offset};
- my $end = $#$paCode - $line_num > 700 ? $line_num + 700 : $#$paCode; # Limit to 700 line subroutines for speed. Will still display otherwise, but won't have depth
- # Contains workaraound for https://rt.cpan.org/Public/Bug/Display.html?id=78313
- my $smallCopy = [ @{$paCode}[$line_num..$end] ];
-
- if($first_line_regex){
- $smallCopy->[0] =~ s/$first_line_regex//;
- }
-
- my $toInpect = join("\n", @$smallCopy); # All code from sub { through end of file
- my ($extracted, undef, $prefix) = Text::Balanced::extract_codeblock($toInpect, '{', '(?s).*?(?=[{;])'); # Will ignore up to start of sub, and then match through to the end
- return $line_num - $offset + 1 if (!$extracted); # if we didn't find the end, mark the end at the beginning.
- $extracted = $prefix . $extracted;
- my $length = $extracted =~ tr/\n//; # Count lines in sub definition
- return $line_num + $length - $offset + 1;
-}
-
-# Finding the end of a package is hard because of the different syntaxes:
-# package foo; contents
-# package { contents }
-# { package foo; contents }
-sub _package_end_line {
- my ($self, $line_num) = @_;
- my $paCode = $self->{code_for_balanced};
- my $offset = $self->{offset};
-
- my $end = $#$paCode - $line_num > 1200 ? $line_num + 1200 : $#$paCode; # Limit to 1200 line subroutines for speed
- my @smallCopy = @{$paCode}[$line_num..$end];
- my $toInpect = join("\n", @smallCopy); # Maybe we're already in a scope, so look for the end of it.
- my $prefixRg = undef;
- if ($paCode->[$line_num] =~ /package[^#]+;/){ # Single line package definition.
- if ($paCode->[$line_num] =~ /{.*package/ or ( ($line_num - $offset + 1) > 0 and $paCode->[$line_num-1] =~ /{/)){
- # Text::Balanced is pretty unreliable, so don't hunt for the closing } unless you absolutely need to.
- $toInpect = "{" . $toInpect;
- } else {
- $toInpect = "";
- }
- } else {
- $prefixRg = '(?s).*?(?=[{;])'; # Start new block either now or on upcoming lines
- }
- my $extracted = Text::Balanced::extract_codeblock($toInpect, '{', $prefixRg);
- my $length;
- if ($extracted){
- $length = $extracted =~ tr/\n//; # Count lines in sub definition
- }
-
- my $count = 1;
- shift @smallCopy; # Remove first line to avoid finding itself
- foreach my $stmt (@smallCopy){
- last if (defined($length) and $count > $length);
- if($stmt =~ /^\s*package\s+([\w:]+)/){
- $length = $count-1;
- last;
- }
- $count++;
- }
-
- if ($length){
- # If we found a delimited package
- return $line_num + $length - $offset + 1;
- } else {
- # Run until end of package
- return $line_num + $#$paCode - $offset + 1;
- }
-}
-
-sub CleanForBalanced {
- # Text::Balanced seems to struggle on some of the newer constructs of Perl such as the // operator introduced in Perl 5.10 https://rt.cpan.org/Public/Bug/Display.html?id=78313
- # and postfix dereferencing, and unicode characters. For finding codeblocks though, we can generally simply strip these characters out.
- my $input = shift;
- $input =~ s@\s//=?\s@\s\s@g;
- # $input =~ s@\s//(\n|\s)@ ||$1@g;
- $input =~ s/[^\x00-\x7F]/ /g;
- $input =~ s/->@\*/ /g;
-
- return $input;
-}
-
sub _init_file {
my ($self, $code, $offset, $file) = @_;
@@ -162,10 +57,6 @@ sub _init_file {
$line = "" if ($line =~ /^=(?:pod|head|head1|head2|head3|head4|over|item|back|begin|end|for|encoding)/ .. $line =~ /^=cut/);
last if ($line =~ /^(__END__|__DATA__)\s*$/);
- if ($line =~ /#.*(\$\w+) isa ([\w:]+)\b/){
- $self->MakeTag($1, $2, '1', $line_number);
- }
-
# Statement will be line with comments, whitespace and POD trimmed
my $stmt;
($stmt = $line) =~ s/^\s*#.*//;
@@ -175,7 +66,6 @@ sub _init_file {
push @codeClean, $stmt;
}
- $self->{code_for_balanced} = [ map { CleanForBalanced($_) } @codeClean ];
return \@codeClean;
}
@@ -196,213 +86,22 @@ sub build_pltags {
# Nothing left? Never mind.
next unless ($stmt);
-
- # TODO, allow specifying list of constructor names as config
- my $constructors = qr/(?:new|connect)/;
- # Declaring an object. Let's store the type
- if ($stmt =~ /^(?:my|our|local|state)\s+(\$\w+)\s*\=\s*([\w\:]+)\-\>$constructors\s*(?:\((?!.*\)\->)|;)/ or
- $stmt =~ /^(?:my|our|local|state)\s+(\$\w+)\s*\=\s*new (\w[\w\:]+)\s*(?:\((?!.*\)\->)|;)/) {
- my ($varName, $objName) = ($1, $2);
- $objName .= "::db" if ($objName =~ /\bDBI$/);
- $self->MakeTag($varName, $objName, '', $line_number);
- $var_continues = 0; # We skipped ahead of the line here.
- }
- # This is a variable declaration if one was started on the previous
- # line, or if this line starts with my or local
- elsif ( $var_continues
- or ($stmt =~ /^(?:my|our|local|state)\b/)) {
- # The declaration continues if the line does not contain ; (comments trip this up for now, so the ; may not be at the end)
- $var_continues = ($stmt !~ /;/ and $stmt !~ /[\)\=\}\{]/);
-
- # Remove my or local from statement, if present
- $stmt =~ s/^(my|our|local|state)\s+//;
-
- # Remove any assignment piece
- $stmt =~ s/\s*=.*//;
-
- # Remove part where sub starts (for signatures). Consider other options here.
- $stmt =~ s/\s*\}.*//;
-
- # Now find all variable names, i.e. "words" preceded by $, @ or %
- my @vars = ($stmt =~ /([\$\@\%][\w]+)\b/g);
-
- foreach my $var (@vars) {
- $self->MakeTag($var, "v", '', $line_number);
- }
- }
-
- # Lexical loop variables, potentially with labels in front. foreach my $foo
- elsif ( $stmt =~ /^(?:(\w+)\s*:(?!\:))?\s*(?:for|foreach)\s+my\s+(\$[\w]+)\b/) {
- if ($1){
- $self->MakeTag($1, "l", '', $line_number);
- }
- $self->MakeTag($2, "v", '', $line_number);
- }
-
- # Lexical match variables if(my ($foo, $bar) ~= ). Optional to detect (my $newstring = $oldstring) =~ s/foo/bar/g;
- elsif ( $stmt =~ /^(?:\}\s*elsif|if|unless|while|until|for)?\s*\(\s*my\b(.*)$/) {
- # Remove any assignment piece
- $stmt =~ s/\s*=.*//;
- my @vars = ($stmt =~ /([\$\@\%][\w]+)\b/g);
- foreach my $var (@vars) {
- $self->MakeTag($var, "v", '', $line_number);
- }
- }
-
- # This is a package declaration if the line starts with package
- elsif ($stmt =~ /^package\s+([\w:]+)/) {
- # Get name of the package
- my $package_name = $1;
- $self->{package_name} = $package_name;
- my $end_line = $self->_package_end_line($i);
- $self->MakeTag($package_name, "p", '', "$line_number;$end_line");
- push(@{$self->{packages}}, $package_name);
+ if ($stmt =~ /^package\s+([\w:]+)/) {
+ push(@{$self->{packages}}, $1);
}
# This is a class decoration for Object::Pad, Corinna, or Moops
elsif ($stmt =~ /^class\s+([\w:]+)/) {
- # Get name of the package
- my $class_name = $1;
- $self->{package_name} = $class_name;
-
- # TODO: Change to _package_end_line to better support unbracketed classes
- my $end_line = $self->_sub_end_line($i);
- $self->MakeTag($class_name, "a", '', "$line_number;$end_line");
- push(@{$self->{packages}}, $class_name);
+ push(@{$self->{packages}}, $1);
}
# This is a role decoration for Object::Pad, Corinna, or Moops
elsif ($stmt =~ /^role\s+([\w:]+)/) {
- # Get name of the package
- my $role_name = $1;
-
- # TODO: Consider changing to _package_end_line to better support unbracketed classes
- my $end_line = $self->_sub_end_line($i);
- $self->MakeTag($role_name, "b", '', "$line_number;$end_line");
- push(@{$self->{packages}}, $role_name);
- }
-
- # This is a sub declaration if the line starts with sub
- elsif ($stmt =~ /^(?:async\s+)?(sub)\s+([\w:]+)(\s+:method)?([^{]*)/ or
- $stmt =~ /^(?:async\s+)?(method)\s+\$?([\w:]+)()([^{]*)/ or
- ($self->{active}->{"Function::Parameters"} and $stmt =~ /^(fun)\s+([\w:]+)()([^{]*)/ )
- ) {
- my $subName = $2;
- my $signature = $4;
- my $kind = ($1 eq 'method' or $3) ? 'o' : 's';
- my $end_line = $self->_sub_end_line($i);
- $self->MakeTag($subName, $kind, '', "$line_number;$end_line");
-
- # Match the after the sub declaration and before the start of the actual sub for signatures (if any)
- my @vars = ($signature =~ /([\$\@\%][\w:]+)\b/g);
-
- # Define subrountine signatures, but exclude prototypes
- # The declaration continues if the line does not end with ;
- $var_continues = ($stmt !~ /;$/ and $stmt !~ /[\)\=\}\{]/);
-
- foreach my $var (@vars) {
- $self->MakeTag($var, "v", '', $line_number);
- }
- }
-
- # Phaser block
- elsif ($stmt=~/^(BEGIN|INIT|CHECK|UNITCHECK|END)\s*\{/) {
- my $phaser = $1;
- my $end_line = $self->_sub_end_line($i);
- $self->MakeTag($phaser, "e", '', "$line_number;$end_line");
- }
-
- # Label line
- elsif ($stmt=~/^([a-zA-Z_][a-zA-Z0-9_]*)\s*:[^:].*{\s*$/) {
- my $label = $1;
- my $end_line = $self->_sub_end_line($i);
- $self->MakeTag($label, "l", '', "$line_number;$end_line");
- }
-
- # Constants. Important because they look like subs (and technically are), so I'll tags them as such
- elsif ($stmt =~/^use\s+constant\s+(\w+)\b/) {
- $self->MakeTag($1, "n", '', $line_number);
- $self->MakeTag("constant", "u", '', $line_number);
- }
-
-
- # TODO: Explicitly split Moo, Moose, Object::Pad, and Corinnna
- elsif ($stmt=~/^(?:has|field)(?:\s+|\()["']?([\$@%]?\w+)\b/) { # Moo/Moose/Object::Pad/Moops/Corinna attributes
- my $attr = $1;
- my $type = $attr =~ /^\w/ ? 'f' : 'v'; # attr looks like a function, $attr is a variable.
- # TODO: Define new type. Class $variables should probably be shown in the Outline view even though lexical variables are not
- $self->MakeTag($attr, $type, '', $line_number);
- # If you have a locally defined package/class Foo want to reference the attributes as Foo::attr or $foo->attr, you need the full path.
- # Subs don't need this since we find them at compile time. We also find "d" types from imported packages in Inquisitor.pm
- if ($type eq 'f'){
- $self->MakeTag( $self->{package_name} . "::$attr", "d", '', $line_number);
- }
+ push(@{$self->{packages}}, $1);
}
-
- # elsif ($sActiveOO->{"Object::Pad"} and $stmt=~/^field\s+([\$@%]\w+)\b/) { # Object::Pad field
- # my $attr = $1;
- # $self->MakeTag($attr, "v", '', $line_number);
- # }
-
- elsif (($self->{active}->{"Mars::Class"} or $self->{active}->{"Venus::Class"}) and $stmt=~/^attr\s+["'](\w+)\b/) { # Mars attributes
- my $attr = $1;
- $self->MakeTag($attr, "f", '', $line_number);
- $self->MakeTag($self->{package_name} . "::$attr", "d", '', $line_number);
- }
-
- elsif ($stmt=~/^around\s+["']?(\w+)\b/) { # Moo/Moose overriding subs.
- $self->MakeTag($1, "s", '', $line_number);
- }
-
- elsif ($stmt=~/^use\s+([\w:]+)\b/) { # Keep track of explicit imports for filtering
- my $import = $1;
- $self->MakeTag("$import", "u", '', $line_number);
- $self->{active}->{$import} = 1;
- }
-
- elsif($self->match_dancer($stmt, $line_number, $i)) {
- # Self contained
- }
- elsif ($stmt=~/^\$self\->\{\s*(['"]|)_(\w+)\1\s*\}\s*=/) { # Common paradigm is for autoloaders to basically just point to the class variable
- my $variable = $2;
- $self->MakeTag("get_$variable", "3", '', $line_number);
- }
-
}
-
+
return $self->{tags}, $self->{packages};
}
-sub match_dancer {
- my ($self, $stmt, $line_number, $i) = @_;
-
- return 0 if !($self->{active}->{"Dancer"} or $self->{active}->{"Dancer2"} or $self->{active}->{"Mojolicious::Lite"});
-
- my $rFilter = qr{qr\{[^\}]+\}} ;
-
- if( $stmt=~/^(?:any|before\_route)\s+\[([^\]]+)\]\s+(?:=>\h*)?(['"])([^\2]+)\2\h*=>\h*sub/) { # Multiple request routing paths
- my $requests = $1;
- my $route = $3;
- $requests =~ s/['"\s\n]+//g;
- $route = "$requests $route";
- my $end_line = $self->_sub_end_line($i, $rFilter);
- $self->MakeTag($route, "g", '', "$line_number;$end_line");
- } elsif ($stmt=~/^(get|any|post|put|patch|delete|del|options|ajax|before_route)\s+(?:[\s\w,\[\]'"]+=>\h*)?(['"])([^\2]+)\2\s*=>\s*sub/) { # Routing paths
- my $route = "$1 $3";
- my $end_line = $self->_sub_end_line($i, $rFilter);
- $self->MakeTag($route, "g", '', "$line_number;$end_line");
- } elsif ( $stmt=~/^(get|any|post|put|patch|delete|del|options|ajax|before_route)\s+(qr\{[^\}]+\})\s+\s*=>\s*sub/) { # Regexp routing paths
- my $route = "$1 $2";
- my $end_line = $self->_sub_end_line($i, $rFilter);
- $self->MakeTag($route, "g", '', "$line_number;$end_line");
- } elsif ( $stmt=~/^(?:hook)\s+(['"]|)(\w+)\1\s*=>\s*sub/) { # Hooks
- my $hook = $2;
- my $end_line = $self->_sub_end_line($i, $rFilter);
- $self->MakeTag($hook, "j", '', "$line_number;$end_line");
- } else {
- return 0;
- }
- return 1; # Must've matched
-}
-
1; \ No newline at end of file
diff --git a/server/src/server.ts b/server/src/server.ts
index 1111976..e91cf6e 100644
--- a/server/src/server.ts
+++ b/server/src/server.ts
@@ -35,6 +35,7 @@ import { getCompletions } from './completion';
import { formatDoc, formatRange } from "./formatting";
import { nLog } from './utils';
import { startProgress, endProgress } from './progress';
+
var LRU = require("lru-cache");
// It the editor doesn't request node-ipc, use stdio instead. Make sure this runs before createConnection
@@ -239,8 +240,7 @@ documents.onDidChangeContent(change => {
async function validatePerlDocument(textDocument: TextDocument): Promise<void> {
- const filePath = Uri.parse(textDocument.uri).fsPath;
- const fileName = basename(filePath);
+ const fileName = basename(Uri.parse(textDocument.uri).fsPath);
const settings = await getDocumentSettings(textDocument.uri);
nLog("Found settings", settings);
@@ -251,10 +251,12 @@ async function validatePerlDocument(textDocument: TextDocument): Promise<void> {
const start = Date.now();
const workspaceFolders = await getWorkspaceFoldersSafe();
+
const pCompile = perlcompile(textDocument, workspaceFolders, settings); // Start compilation
- const pCritic = perlcritic(textDocument, workspaceFolders, settings); // Start perlcritic
+ const pCritic = perlcritic(textDocument, workspaceFolders, settings); // Start perlcritic
const pImports = perlimports(textDocument, workspaceFolders, settings); // Start perlimports
+
let perlOut = await pCompile;
nLog("Compilation Time: " + (Date.now() - start)/1000 + " seconds", settings);
let oldCriticDiags = documentDiags.get(textDocument.uri);
@@ -373,8 +375,11 @@ connection.onDefinition(params => {
});
-connection.onDocumentSymbol(params => {
- return getSymbols(navSymbols, params.textDocument.uri);
+connection.onDocumentSymbol(async params => {
+ let document = documents.get(params.textDocument.uri);
+ // We might need to async wait for the document to be processed, but I suspect the order is fine
+ if(!document) return;
+ return getSymbols(document, params.textDocument.uri);
});
diff --git a/server/src/symbols.ts b/server/src/symbols.ts
index e5895bc..70e46ba 100644
--- a/server/src/symbols.ts
+++ b/server/src/symbols.ts
@@ -5,87 +5,67 @@ import {
Location,
WorkspaceSymbolParams
} from 'vscode-languageserver/node';
+
+import {
+ TextDocument
+} from 'vscode-languageserver-textdocument';
+
import { PerlDocument, PerlElem, PerlSymbolKind } from "./types";
import Uri from 'vscode-uri';
import { realpathSync, existsSync } from 'fs';
import { Console } from 'console';
+import { parseDocument } from './parser';
-
-function waitForDoc (navSymbols: any, uri: string): Promise<PerlDocument> {
- let retries = 0;
+export async function getSymbols (textDocument: TextDocument, uri: string ): Promise<SymbolInformation[]> {
- return new Promise((resolve, reject) => {
- const interval = setInterval(() => {
+ let perlDoc = await parseDocument(textDocument);
- if (++retries > 100) { // Wait for 10 seconds looking for the document.
- reject("Found no document");
- clearInterval(interval);
+ let symbols: SymbolInformation[] = [];
+ perlDoc.elems?.forEach((elements: PerlElem[], elemName: string) => {
+
+ elements.forEach(element => {
+ let kind: SymbolKind;
+ if (element.type == PerlSymbolKind.LocalSub || element.type == PerlSymbolKind.OutlineOnlySub){
+ kind = SymbolKind.Function;
+ } else if (element.type == PerlSymbolKind.LocalMethod){
+ kind = SymbolKind.Method;
+ } else if (element.type == PerlSymbolKind.Package){
+ kind = SymbolKind.Package;
+ } else if (element.type == PerlSymbolKind.Class){
+ kind = SymbolKind.Class;
+ } else if (element.type == PerlSymbolKind.Role){
+ kind = SymbolKind.Interface;
+ } else if (element.type == PerlSymbolKind.Field){
+ kind = SymbolKind.Field;
+ } else if (element.type == PerlSymbolKind.Label){
+ kind = SymbolKind.Key;
+ } else if (element.type == PerlSymbolKind.Phaser){
+ kind = SymbolKind.Event;
+ } else if (element.type == PerlSymbolKind.Constant){
+ kind = SymbolKind.Constant;
+ } else if (element.type == PerlSymbolKind.HttpRoute){
+ kind = SymbolKind.Interface;
+ } else {
+ return;
}
- const perlDoc = navSymbols.get(uri);
-
- if (perlDoc) {
- resolve(perlDoc);
- clearInterval(interval);
+ const location: Location = {
+ range: {
+ start: { line: element.line, character: 0 },
+ end: { line: element.lineEnd, character: 100 }
+ },
+ uri: uri
};
- }, 100);
- });
-}
-
-export function getSymbols (navSymbols: any, uri: string ): Promise<SymbolInformation[]> {
-
- return waitForDoc(navSymbols, uri).then((perlDoc) => {
- let symbols: SymbolInformation[] = [];
- perlDoc.elems?.forEach((elements: PerlElem[], elemName: string) => {
-
- elements.forEach(element => {
- let kind: SymbolKind;
- if (element.type == PerlSymbolKind.LocalSub || element.type == PerlSymbolKind.OutlineOnlySub){
- kind = SymbolKind.Function;
- } else if (element.type == PerlSymbolKind.LocalMethod){
- kind = SymbolKind.Method;
- } else if (element.type == PerlSymbolKind.Package){
- kind = SymbolKind.Package;
- } else if (element.type == PerlSymbolKind.Class){
- kind = SymbolKind.Class;
- } else if (element.type == PerlSymbolKind.Role){
- kind = SymbolKind.Interface;
- } else if (element.type == PerlSymbolKind.Field){
- kind = SymbolKind.Field;
- } else if (element.type == PerlSymbolKind.Label){
- kind = SymbolKind.Key;
- } else if (element.type == PerlSymbolKind.Phaser){
- kind = SymbolKind.Event;
- } else if (element.type == PerlSymbolKind.Constant){
- kind = SymbolKind.Constant;
- } else if (element.type == PerlSymbolKind.HttpRoute){
- kind = SymbolKind.Interface;
- } else {
- return;
- }
- const location: Location = {
- range: {
- start: { line: element.line, character: 0 },
- end: { line: element.lineEnd, character: 100 }
- },
- uri: uri
- };
- const newSymbol: SymbolInformation = {
- kind: kind,
- location: location,
- name: elemName
- }
-
- symbols.push(newSymbol);
- });
- });
+ const newSymbol: SymbolInformation = {
+ kind: kind,
+ location: location,
+ name: elemName
+ }
- return symbols;
- }).catch((reason)=>{
- // TODO: Add logging back, but detect STDIO mode first
- // console.log("Failed in getSymbols");
- //console.log(reason);
- return [];
+ symbols.push(newSymbol);
+ });
});
+
+ return symbols;
}
export function getWorkspaceSymbols (params: WorkspaceSymbolParams, defaultMods: Map<string, string>): Promise<SymbolInformation[]> {
diff --git a/server/src/types.ts b/server/src/types.ts
index 419704d..f8ab5f8 100644
--- a/server/src/types.ts
+++ b/server/src/types.ts
@@ -95,6 +95,7 @@ export enum PerlSymbolKind {
ImportedVar = "c",
ImportedHash = "h",
HttpRoute = "g",
- OutlineOnlySub = "j"
+ OutlineOnlySub = "j",
+ AutoLoadVar = "3"
}
diff --git a/server/src/utils.ts b/server/src/utils.ts
index 6197f1c..569bcd8 100644
--- a/server/src/utils.ts
+++ b/server/src/utils.ts
@@ -165,7 +165,7 @@ export function lookupSymbol(perlDoc: PerlDocument, modMap: Map<string, string>,
let knownObject = /^(\$\w+)\->(?:\w+)$/.exec(symbol);
if(knownObject){
const targetVar = perlDoc.canonicalElems.get(knownObject[1]);
- if(targetVar) qSymbol = qSymbol.replace(/^\$\w+(?=\->)/, targetVar.type);
+ if(targetVar) qSymbol = qSymbol.replace(/^\$\w+(?=\->)/, targetVar.typeDetail);
}
// Add what we mean when someone wants ->new().
diff --git a/server/tsconfig.json b/server/tsconfig.json
index e9b0bcd..34f2982 100644
--- a/server/tsconfig.json
+++ b/server/tsconfig.json
@@ -1,7 +1,7 @@
{
"compilerOptions": {
- "target": "es2019",
- "lib": ["ES2019"],
+ "target": "es2021",
+ "lib": ["es2021", "dom"],
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
diff --git a/testWorkspace/mainTest.pl b/testWorkspace/mainTest.pl
index 1720b26..491fdaf 100644
--- a/testWorkspace/mainTest.pl
+++ b/testWorkspace/mainTest.pl
@@ -52,6 +52,7 @@ print %my_hash;
print $hash_ref->{"Five"};
print $$hash_ref{"Five"};
# print $üτfⅷ;
+
print MYCONSTANT;
INIT {
@@ -115,7 +116,7 @@ my $otherObj = MyLib::MyOtherClass->new();
$otherObj->unique_method_name();
$otherObj->duplicate_method_name();
-my $unknownObj = $otherObj; # Type hints: $unknownObj isa MyLib::MyOtherClass
+my $unknownObj = $otherObj; # Type hints: $unknownObj2 isa MyLib::MyOtherClass
$unknownObj->duplicate_method_name();
my $mooObj = MyLib::MooClass->new();
@@ -161,6 +162,18 @@ sub same_file_package_sub {
print "In same_file_package_sub\n";
}
+package ParseTest {
+ my $foo ; # Unmatched }
+ $foo = "
+ Quoted multiline }
+ ";
+ $foo =~ s/\}//g; # Regexed }
+
+ sub ParseSubTest {
+
+ }
+
+}
package Foo {
use Moo;