Skip to content

Commit

Permalink
find other references from selected reference
Browse files Browse the repository at this point in the history
use extra parser for subroutine calls (don't include yet in symbols)
  • Loading branch information
pbaksa committed Oct 15, 2022
1 parent 317be45 commit 4721353
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 52 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ __hotspot__ or __hotspot2__ - length edit hotspot triplet
* IntelliSense lists parameters with their description, type, flags and default value. This list can be filtered by parameter type too.
* These can all be toggled on/off together with Ctrl-Shift-Space
* Follow macro calls with Ctrl-click
* Go to definitions/references, find all references of subroutines in same gdl-hsf file (Ctrl-click or F12, Shift-F12, Shift-Alt-F12)
* Go to definitions/references, find all references of subroutines in same libpart (Ctrl-click or F12, Shift-F12, Shift-Alt-F12)
* Show Call Hierarchy (Shift-Alt-H), Peek Call Hierarchy
* Incoming or outgoing macro calls are shown, respecting the execution context of the edited scipt. Eg. all scripts are searched outgoing from a master script, but only master and 2d scripts are searched outgoing from a 2d script.
* HSF library parts inside the workspace are searched. If the workspace changed in a process outside VSCode, use the "Re-scan library parts in workspace folders" command to refresh the known library parts.
Expand Down Expand Up @@ -171,6 +171,7 @@ Some valid syntaxes won't be highlighted, and some invalid syntaxes will be high
## 1.26.2
* Intellisense reads constants from edited file too, besides saved master script version
* Find references/definition handles subroutines defined in master script
## 1.26.1
* `Show Call Hierarchy`, `Peek Call Hierarchy` context menu items list incoming or outgoing macro calls inside the workspace
Expand Down
66 changes: 42 additions & 24 deletions out/extension.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion out/extension.js.map

Large diffs are not rendered by default.

26 changes: 26 additions & 0 deletions out/jumpparser.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions out/jumpparser.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

70 changes: 44 additions & 26 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { CallTree } from './calltree';
import { Constants } from './constparser';

import path = require('path');
import { Jumps } from './jumpparser';

export async function activate(context: vscode.ExtensionContext) {
//console.log("extension.activate");
Expand Down Expand Up @@ -410,7 +411,7 @@ export class GDLExtension
private onDocumentOpened(document: vscode.TextDocument) {
//console.log("GDLExtension.onDocumentOpened", document.uri.toString());

// handle only top editor - other can be SCM virtual document
// handle only top editor - other can be SCM virtual document / other document opened by extension
if (vscode.window.activeTextEditor?.document.uri === document.uri) {
this.updateHsfLibpart();
this.reparseDoc(document, 0);
Expand Down Expand Up @@ -1027,16 +1028,16 @@ export class GDLExtension
}
} else {
// look for subroutine calls only if not a macro call
const lineBefore = document.lineAt(position.line).text.substring(0, originRange.start.character);
const jumps = new Jumps(document.lineAt(position.line).text);

if (lineBefore.match(/(then|goto|gosub)\s*["'`´“”’‘]?$/i)) {
if (jumps.jumps.find(j => j.range.contains(position.with(0)))) {

let masterFunctionSymbols = undefined;

// defining same subroutines in two scripts and calling from master is possible,
// but gives build warnings and not a good practice
// therefore we don't check definitions in other scripts from master script, just the other way around
if (this.infoFromHSF &&
if (//this.infoFromHSF &&
HSFScriptType(document.uri) !== Parser.ScriptType.D) {

const masterfile = path.normalize(
Expand Down Expand Up @@ -1103,36 +1104,53 @@ export class GDLExtension
async provideReferences(document: vscode.TextDocument, position: vscode.Position,
_context: vscode.ReferenceContext, cancel: vscode.CancellationToken) : Promise<vscode.Location[]> {

const references : vscode.Location[] = [];
let references : vscode.Location[] = [];

await this.immediateParse(document, cancel);

const origin = this.mapOwnFuncionSymbols(Parser.ScriptType.ROOT).filter(s => {
// should we provide references of a definition?
let label = this.mapOwnFuncionSymbols(Parser.ScriptType.ROOT).filter(s => {
return s.selectionRange.contains(position);
})[0]?.name; // there shouldn't be more results

const scriptType = HSFScriptType(document.uri)!;
const searchScripts = (this.infoFromHSF)
? Parser.getRelatedScripts(scriptType).filter(script => script !== scriptType) // exclude current script
: [];

const libpart = (await this.wsSymbols.values(cancel)).find(e => e.root_uri.fsPath === this.hsflibpart!.rootFolder.fsPath);
let searchUris = searchScripts.map(async (script) => libpart?.scriptUri(script));
searchUris.push(Promise.resolve(document.uri)); // current script read from document

for await (const scriptUri of searchUris) {
if (scriptUri) {
const searchDocument = await vscode.workspace.openTextDocument(scriptUri);

for (const match of searchDocument.getText().matchAll(/(then|goto|gosub)\s*/gmi)) {
const start = searchDocument.positionAt(match.index!);
const end = start.translate(undefined, match[0].length);
const end_full = end.translate(undefined, origin.length);
const rest = searchDocument.getText(new vscode.Range(end, end_full));
let searchScripts : Parser.ScriptType[] | undefined;

if (rest === origin) {
references.push(new vscode.Location(searchDocument.uri, new vscode.Range(start, end_full)));
}
// should we provide all other references like this?
if (label === undefined) {
const jumps = new Jumps(document.getText());
const selection = jumps.jumps.find(j => j.range.contains(position));
if (selection !== undefined) {
label = selection.target;
// only master-defined subroutines are called from other scripts
// search all scripts only if definition is in master script
const definition = await this.provideDefinition(document, position, cancel);
if (definition.find(d => d.targetUri.fsPath.endsWith("1d.gdl"))) {
searchScripts = Parser.Scripts;
} else {
searchScripts = [scriptType];
}
}
} else {
searchScripts = Parser.getRelatedScripts(scriptType);
}

//searchScripts = this.infoFromHSF ? searchScripts : [scriptType];

if (label !== undefined) {
const libpart = (await this.wsSymbols.values(cancel)).find(e => e.root_uri.fsPath === this.hsflibpart!.rootFolder.fsPath);

let searchUris = searchScripts!.map(async (script) => (script === scriptType)
? document.uri // shortcut for current document
: libpart?.scriptUri(script));

for await (const scriptUri of searchUris) {
if (scriptUri) {
const searchDocument = await vscode.workspace.openTextDocument(scriptUri);

const jumps = new Jumps(searchDocument.getText());
references = references.concat(jumps.jumps.filter(j => j.target === label)
.map(j => new vscode.Location(searchDocument.uri, j.range)));
}
}
}
Expand Down
32 changes: 32 additions & 0 deletions src/jumpparser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import * as vscode from 'vscode';

export type Jump = {
command: string,
target: string,
range: vscode.Range
}

export class Jumps {
private static regex = /((then|goto|gosub)\s+)(([0-9]+)|((["'`´“”’‘])([^"'`´“”’‘]+)\6))/i;
// keep synced with Parser.GDLFunction.regex, but backreference index differs!
public readonly jumps: Array<Jump>;

constructor(code : string) {
this.jumps = code.split("\n")
.flatMap(Jumps.matchLine);
}

private static matchLine(line: string, linenumber: number) : Jump[] {
const match = line.match(Jumps.regex);
if (match) {
return [{
command: match[1].toLowerCase(),
target: match[3], //match[4] ?? match[7], // strings unquoted
range: new vscode.Range(linenumber, match.index!,
linenumber, match.index! + match[0].length)
}];
} else {
return [];
}
}
}

0 comments on commit 4721353

Please sign in to comment.