summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--client/src/test/completion.test.ts2
-rw-r--r--client/src/test/diagnostics.test.ts2
-rw-r--r--client/src/test/helper.ts6
-rw-r--r--client/testFixture/completion.txt0
-rw-r--r--client/testFixture/diagnostics.txt1
-rw-r--r--package.json2
-rw-r--r--perlnavigator-0.2.5.vsix (renamed from perlnavigator-0.2.4.vsix)bin1372308 -> 1373963 bytes
-rw-r--r--server/src/completion.ts6
-rw-r--r--server/src/hover.ts6
-rw-r--r--server/src/parseDocument.ts6
-rw-r--r--server/src/perl/Inquisitor.pm29
-rw-r--r--server/src/perl/lib_bs22/pltags.pm6
-rw-r--r--server/src/types.ts10
-rw-r--r--server/src/utils.ts33
-rw-r--r--testWorkspace/MyLib/ObjectPad.pm4
-rw-r--r--testWorkspace/mainTest.pl2
16 files changed, 82 insertions, 33 deletions
diff --git a/client/src/test/completion.test.ts b/client/src/test/completion.test.ts
index f355078..29786f1 100644
--- a/client/src/test/completion.test.ts
+++ b/client/src/test/completion.test.ts
@@ -8,7 +8,7 @@ import * as assert from 'assert';
import { getDocUri, activate } from './helper';
suite('Should do completion', () => {
- const docUri = getDocUri('completion.txt');
+ const docUri = getDocUri('mainTest.pl');
test('Completes JS/TS in txt file', async () => {
await testCompletion(docUri, new vscode.Position(0, 0), {
diff --git a/client/src/test/diagnostics.test.ts b/client/src/test/diagnostics.test.ts
index 1aa8a36..9683eef 100644
--- a/client/src/test/diagnostics.test.ts
+++ b/client/src/test/diagnostics.test.ts
@@ -8,7 +8,7 @@ import * as assert from 'assert';
import { getDocUri, activate } from './helper';
suite('Should get diagnostics', () => {
- const docUri = getDocUri('diagnostics.txt');
+ const docUri = getDocUri('mainTest.pl');
test('Diagnoses uppercase texts', async () => {
await testDiagnostics(docUri, [
diff --git a/client/src/test/helper.ts b/client/src/test/helper.ts
index 6e6724d..b771c9b 100644
--- a/client/src/test/helper.ts
+++ b/client/src/test/helper.ts
@@ -12,11 +12,11 @@ export let documentEol: string;
export let platformEol: string;
/**
- * Activates the vscode.lsp-sample extension
+ * Activates the bscan.perlnavigator extension
*/
export async function activate(docUri: vscode.Uri) {
// The extensionId is `publisher.name` from package.json
- const ext = vscode.extensions.getExtension('vscode-samples.lsp-sample')!;
+ const ext = vscode.extensions.getExtension('bscan.perlnavigator')!;
await ext.activate();
try {
doc = await vscode.workspace.openTextDocument(docUri);
@@ -32,7 +32,7 @@ async function sleep(ms: number) {
}
export const getDocPath = (p: string) => {
- return path.resolve(__dirname, '../../testFixture', p);
+ return path.resolve(__dirname, '../../../testWorkspace', p);
};
export const getDocUri = (p: string) => {
return vscode.Uri.file(getDocPath(p));
diff --git a/client/testFixture/completion.txt b/client/testFixture/completion.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/client/testFixture/completion.txt
diff --git a/client/testFixture/diagnostics.txt b/client/testFixture/diagnostics.txt
new file mode 100644
index 0000000..d910cfb
--- /dev/null
+++ b/client/testFixture/diagnostics.txt
@@ -0,0 +1 @@
+ANY browsers, ANY OS. \ No newline at end of file
diff --git a/package.json b/package.json
index 695f243..970978d 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
"description": "Code navigation, autocompletion, syntax checking, and linting for Perl",
"author": "bscan",
"license": "MIT",
- "version": "0.2.4",
+ "version": "0.2.5",
"icon": "images/camel_icon.png",
"repository": {
"type": "git",
diff --git a/perlnavigator-0.2.4.vsix b/perlnavigator-0.2.5.vsix
index ba1f2d6..871a9c2 100644
--- a/perlnavigator-0.2.4.vsix
+++ b/perlnavigator-0.2.5.vsix
Binary files differ
diff --git a/server/src/completion.ts b/server/src/completion.ts
index 5596565..d2a6b08 100644
--- a/server/src/completion.ts
+++ b/server/src/completion.ts
@@ -139,7 +139,9 @@ function getMatches(perlDoc: PerlDocument, symbol: string, replace: Range): Com
// Don't send invalid constructs
if(/\-\>\w+::/.test(aligned) || // like FOO->BAR::BAZ
- (/\-\>\w+$/.test(aligned) && !["s", "t", "i", "o", "f", "d"].includes(element.type)) || // FOO->BAR if Bar is not a sub/method.
+ (/\-\>\w+$/.test(aligned) && !["s", "t", "i", "o", "x", "f", "d"].includes(element.type)) || // FOO->BAR if Bar is not a sub/method.
+ (!/^\$.*\-\>\w+$/.test(aligned) && ["o", "x", "f", "d"].includes(element.type)) || // FOO::BAR if Bar is a instance method or attribute (I assume them to be instance methods/attributes, not class)
+ (/-:/.test(aligned)) || // We look things up like this, but don't let them slip through
(/^\$.*::/.test(aligned)) // $Foo::Bar, I don't really hunt for these anyway
) return;
@@ -199,7 +201,7 @@ function buildMatches(lookupName: string, elem: PerlElem, range: Range): Complet
} else if (elem.type == PerlSymbolKind.LocalSub){
if(/^\$self\-/.test(lookupName)) docs.push(elem.name); // For consistency with the other $self methods. VScode seems to hide documentation if less populated?
kind = CompletionItemKind.Function;
- } else if (elem.type == PerlSymbolKind.ImportedSub || elem.type == PerlSymbolKind.Inherited || elem.type == PerlSymbolKind.LocalMethod){
+ } else if (elem.type == PerlSymbolKind.ImportedSub || elem.type == PerlSymbolKind.Inherited || elem.type == PerlSymbolKind.Method || elem.type == PerlSymbolKind.LocalMethod){
kind = CompletionItemKind.Method;
docs.push(elem.name);
if(elem.typeDetail && elem.typeDetail != elem.name) docs.push(`\nDefined as:\n ${elem.typeDetail}`);
diff --git a/server/src/hover.ts b/server/src/hover.ts
index c5f9dc9..2b57f63 100644
--- a/server/src/hover.ts
+++ b/server/src/hover.ts
@@ -47,9 +47,11 @@ function buildHoverDoc(symbol: string, elem: PerlElem){
if(elem.package) desc += ` (${elem.package})` ; // Is this ever known?
} else if(elem.type == 'h'){
desc = `${elem.name} (${elem.package})`;
- } else if (elem.type == 's' || elem.type == 'o'){
+ } else if (elem.type == 's'){
desc = `(subroutine) ${symbol}`;
- } else if (elem.type == 't' || elem.type == 'i'){
+ } else if (['o','x'].includes(elem.type)){
+ desc = `(method) ${symbol}`;
+ } else if (['t','i'].includes(elem.type)){ // inherited methods can still be subs (e.g. new from a parent)
desc = `(subroutine) ${elem.name}`;
if(elem.typeDetail && elem.typeDetail != elem.name) desc = desc + ` (${elem.typeDetail})`;
}else if (elem.type == 'p'){
diff --git a/server/src/parseDocument.ts b/server/src/parseDocument.ts
index 5c3dc57..b678147 100644
--- a/server/src/parseDocument.ts
+++ b/server/src/parseDocument.ts
@@ -10,6 +10,7 @@ export async function buildNav(stdout: string): Promise<PerlDocument> {
elems: new Map(),
canonicalElems: new Map(),
imported: new Map(),
+ parents: new Map()
};
stdout.split("\n").forEach(perl_elem => {
@@ -49,6 +50,11 @@ function parseElem(perlTag: string, perlDoc: PerlDocument): void {
return; // Don't store it as an element
}
+ if (type == '2'){
+ perlDoc.parents.set(name, typeDetail);
+ return; // Don't store it as an element
+ }
+
// Add anyway
const newElem: PerlElem = {
diff --git a/server/src/perl/Inquisitor.pm b/server/src/perl/Inquisitor.pm
index 9e431f3..1174ebc 100644
--- a/server/src/perl/Inquisitor.pm
+++ b/server/src/perl/Inquisitor.pm
@@ -2,6 +2,7 @@ package Inquisitor;
# be careful around importing anything since we don't want to pollute the users namespace
use strict;
+use attributes;
no warnings;
my @preloaded; # Check what's loaded before we pollute the namespace
@@ -28,10 +29,12 @@ CHECK {
print "Done with pltags. Now dumping same-file packages\n";
foreach my $package (@$packages){
- print "Inspecting package $package\n";
# This is finding packages in the file we're inspecting, and then dumping them into a single namespace in the file
- dump_vars_to_main($package) if $package;
- dump_inherited_to_main($package) if $package;
+ if ($package) {
+ dump_vars_to_main($package);
+ dump_inherited_to_main($package);
+ tag_parents($package);
+ }
}
1; # For the eval
} or do {
@@ -65,7 +68,7 @@ sub maybe_print_sub_info {
my $meta = B::svref_2object($codeRef);
$meta->isa('B::CV') or return 0;
- my ($file, $line, $subType) = resolve_file($meta, $subType);
+ my ($file, $line, $subType) = resolve_file($meta, $subType, $codeRef);
my $pack = $UNKNOWN;
my $subname = $UNKNOWN;
@@ -86,12 +89,26 @@ sub maybe_print_sub_info {
return 0;
}
+sub tag_parents {
+ my $package = shift;
+
+ no strict 'refs';
+ my @parents = @{"${package}::ISA"};
+ my $primaryGuardian = $parents[0];
+ if($primaryGuardian){
+ print_tag("$package", '2', $primaryGuardian, '', '', '', '');
+ }
+}
+
sub resolve_file {
- my ($meta, $subType) = @_;
+ my ($meta, $subType, $codeRef) = @_;
my $file = '';
my $line = '';
+ # Very few things are tagged method, but we can clean up autocomplete if it is. Can something be both an attribute and a attribute? Also, i and t both become x?
+ $subType = 'x' if (grep /^method$/, attributes::get($codeRef));
+
if ($meta->START->isa('B::COP')){
$file = $meta->START->file;
$line = $meta->START->line - 2;
@@ -108,7 +125,7 @@ sub resolve_file {
$line = $2;
$subType = 'd';
}
-
+
return ($file, $line, $subType);
}
diff --git a/server/src/perl/lib_bs22/pltags.pm b/server/src/perl/lib_bs22/pltags.pm
index 16d8626..be5bd10 100644
--- a/server/src/perl/lib_bs22/pltags.pm
+++ b/server/src/perl/lib_bs22/pltags.pm
@@ -208,11 +208,11 @@ sub build_pltags {
}
# This is a sub declaration if the line starts with sub
- elsif ($stmt =~ /^(sub|method)\s+([\w:]+)/) {
+ elsif ($stmt =~ /^(sub|method)\s+([\w:]+)(\s+:method)?/) {
my $subName = $2;
- my $kind = $1 eq 'sub' ? 's' : 'o';
+ my $kind = ($1 eq 'method' or $3) ? 'o' : 's';
my $end_line = SubEndLine(\@code, $i, $offset);
- MakeTag($subName, "s", '', $file, "$line_number;$end_line", $package_name, \@tags);
+ MakeTag($subName, $kind, '', $file, "$line_number;$end_line", $package_name, \@tags);
# Match the after the sub declaration and before the start of the actual sub for signatures
if($stmt =~ /^sub\s+[\w:]+([^{]*)/){
diff --git a/server/src/types.ts b/server/src/types.ts
index dee2961..c6aed6e 100644
--- a/server/src/types.ts
+++ b/server/src/types.ts
@@ -45,6 +45,7 @@ export interface PerlDocument {
elems: Map<string, PerlElem[]>;
canonicalElems: Map<string, PerlElem>;
imported: Map<string, number>;
+ parents: Map<string, string>;
}
export interface CompilationResults {
@@ -64,10 +65,11 @@ export enum PerlSymbolKind {
Class = "a",
ImportedSub = "t",
Inherited = "i",
- Field = "f",
- PathedField = "d",
- LocalSub = "s",
- LocalMethod = "o",
+ Field = "f", // Instance fields
+ PathedField = "d", // Instance fields
+ LocalSub = "s",
+ LocalMethod = "o", // Assumed to be instance methods
+ Method = "x", // Assumed to be instance methods
LocalVar = "v",
Constant = "n",
Label = "l",
diff --git a/server/src/utils.ts b/server/src/utils.ts
index 963c8e5..fd26b3f 100644
--- a/server/src/utils.ts
+++ b/server/src/utils.ts
@@ -86,23 +86,42 @@ export function getSymbol(position: Position, txtDoc: TextDocument) {
return symbol;
}
-
+function findRecent (found: PerlElem[], line: number){
+ let best = found[0];
+ for (var i = 0; i < found.length; i++){
+ if(found[i].line > best.line && found[i].line <= line){
+ best = found[i];
+ }
+ };
+ return best;
+}
export function lookupSymbol(perlDoc: PerlDocument, symbol: string, line: number): PerlElem[] {
let found = perlDoc.elems.get(symbol);
if(found?.length){
// Simple lookup worked. If we have multiple (e.g. 2 lexical variables), find the nearest earlier declaration.
- let best = found[0];
- for (var i = 0; i < found.length; i++){
- if(found[i].line > best.line && found[i].line <= line){
- best = found[i];
- }
- };
+ const best = findRecent(found, line);
return [best];
}
let qSymbol = symbol;
+
+ let superClass = /^(\$\w+)\-\>SUPER\b/.exec(symbol);
+ if(superClass){
+ // If looking up the superclass of $self->SUPER, we need to find the package in which $self is defined, and then find the parent
+ let child = perlDoc.elems.get(superClass[1]);
+ if(child?.length){
+ const recentChild = findRecent(child, line);
+ if(recentChild.package){
+ const parentVar = perlDoc.parents.get(recentChild.package);
+ if(parentVar){
+ qSymbol = qSymbol.replace(/^\$\w+\-\>SUPER/, parentVar);
+ }
+ }
+ }
+ }
+
let knownObject = /^(\$\w+)\->(?:\w+)$/.exec(symbol);
if(knownObject){
const targetVar = perlDoc.canonicalElems.get(knownObject[1]);
diff --git a/testWorkspace/MyLib/ObjectPad.pm b/testWorkspace/MyLib/ObjectPad.pm
index dbdbf5c..6feebb2 100644
--- a/testWorkspace/MyLib/ObjectPad.pm
+++ b/testWorkspace/MyLib/ObjectPad.pm
@@ -1,7 +1,7 @@
use v5.26;
use Object::Pad;
-package ObjectPad;
-class ObjectPad;
+package MyLib::ObjectPad;
+class MyLib::ObjectPad;
has $x :param = 0;
has $y :param = 0;
diff --git a/testWorkspace/mainTest.pl b/testWorkspace/mainTest.pl
index 969bda1..9cfb575 100644
--- a/testWorkspace/mainTest.pl
+++ b/testWorkspace/mainTest.pl
@@ -128,7 +128,7 @@ my $hiddenPackObj = MyLib::SubPackage->new();
my $dbh2 = MyLib::DBI->connect();
-my $padObj = ObjectPad->new(x => 5, y => 10);
+my $padObj = MyLib::ObjectPad->new(x => 5, y => 10);
$padObj->describe();
my $caObj = MyLib::ClassAccessor->new();