diff options
-rw-r--r-- | client/src/test/completion.test.ts | 2 | ||||
-rw-r--r-- | client/src/test/diagnostics.test.ts | 2 | ||||
-rw-r--r-- | client/src/test/helper.ts | 6 | ||||
-rw-r--r-- | client/testFixture/completion.txt | 0 | ||||
-rw-r--r-- | client/testFixture/diagnostics.txt | 1 | ||||
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | perlnavigator-0.2.5.vsix (renamed from perlnavigator-0.2.4.vsix) | bin | 1372308 -> 1373963 bytes | |||
-rw-r--r-- | server/src/completion.ts | 6 | ||||
-rw-r--r-- | server/src/hover.ts | 6 | ||||
-rw-r--r-- | server/src/parseDocument.ts | 6 | ||||
-rw-r--r-- | server/src/perl/Inquisitor.pm | 29 | ||||
-rw-r--r-- | server/src/perl/lib_bs22/pltags.pm | 6 | ||||
-rw-r--r-- | server/src/types.ts | 10 | ||||
-rw-r--r-- | server/src/utils.ts | 33 | ||||
-rw-r--r-- | testWorkspace/MyLib/ObjectPad.pm | 4 | ||||
-rw-r--r-- | testWorkspace/mainTest.pl | 2 |
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 Binary files differindex ba1f2d6..871a9c2 100644 --- a/perlnavigator-0.2.4.vsix +++ b/perlnavigator-0.2.5.vsix 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(); |