diff options
author | bscan <10503608+bscan@users.noreply.github.com> | 2023-10-08 12:29:54 -0400 |
---|---|---|
committer | bscan <10503608+bscan@users.noreply.github.com> | 2023-10-08 12:29:54 -0400 |
commit | b10cf7b92e4cd133acf8e117f891f2bbfe759589 (patch) | |
tree | 6aeca8b40fce8b878ee0f06b37b142e94fcf04a4 | |
parent | 103e8f3ae76ccfe7dc47ebba82f36bc2f8fca4e3 (diff) | |
download | PerlNavigator-b10cf7b92e4cd133acf8e117f891f2bbfe759589.zip |
Large refactor. Moving tagging from Perl to TypeScript. Leverages TextMate instead of Text::Balanced for finding scopes
-rw-r--r-- | package-lock.json | 4 | ||||
-rw-r--r-- | server/package-lock.json | 59 | ||||
-rw-r--r-- | server/package.json | 4 | ||||
-rw-r--r-- | server/perl.tmLanguage.json | 2977 | ||||
-rw-r--r-- | server/src/completion.ts | 16 | ||||
-rw-r--r-- | server/src/diagnostics.ts | 31 | ||||
-rw-r--r-- | server/src/hover.ts | 12 | ||||
-rw-r--r-- | server/src/parseTags.ts (renamed from server/src/parseDocument.ts) | 2 | ||||
-rw-r--r-- | server/src/parser.ts | 570 | ||||
-rw-r--r-- | server/src/perl/Inquisitor.pm | 1 | ||||
-rw-r--r-- | server/src/perl/criticWrapper.pl | 1 | ||||
-rw-r--r-- | server/src/perl/defaultCriticProfile | 7 | ||||
-rw-r--r-- | server/src/perl/lib_bs22/pltags.pm | 311 | ||||
-rw-r--r-- | server/src/server.ts | 15 | ||||
-rw-r--r-- | server/src/symbols.ts | 120 | ||||
-rw-r--r-- | server/src/types.ts | 3 | ||||
-rw-r--r-- | server/src/utils.ts | 2 | ||||
-rw-r--r-- | server/tsconfig.json | 4 | ||||
-rw-r--r-- | testWorkspace/mainTest.pl | 15 |
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; |