From 815a59f3bb79a15b71070ea2229a544f133d9d8e Mon Sep 17 00:00:00 2001 From: eyza Date: Wed, 30 Oct 2024 16:25:27 +0100 Subject: [PATCH] Added definition provider and hover provider for GSC paths --- src/GscDefinitionProvider.ts | 69 +++++++++++++------ src/GscHoverProvider.ts | 23 +++++-- src/test/Tests.test.ts | 2 +- src/test/workspace/GscAll_CoD2MP.test.ts | 2 +- .../workspace/GscAll_UniversalGame.test.ts | 2 +- src/test/workspace/GscFileReferences.test.ts | 31 ++++++--- 6 files changed, 92 insertions(+), 37 deletions(-) diff --git a/src/GscDefinitionProvider.ts b/src/GscDefinitionProvider.ts index 4f86715..46f0834 100644 --- a/src/GscDefinitionProvider.ts +++ b/src/GscDefinitionProvider.ts @@ -1,7 +1,7 @@ import * as vscode from 'vscode'; import { GscFiles } from './GscFiles'; import { GscFile } from './GscFile'; -import { GroupType } from './GscFileParser'; +import { GroupType, GscGroup } from './GscFileParser'; import { GscFunctions } from './GscFunctions'; import { Issues } from './Issues'; import { LoggerOutput } from './LoggerOutput'; @@ -24,7 +24,7 @@ export class GscDefinitionProvider implements vscode.DefinitionProvider { // Get parsed file const gscFile = await GscFiles.getFileData(document.uri, false, "provide definition"); - const locations = await GscDefinitionProvider.getFunctionDefinitionLocations(gscFile, position); + const locations = await GscDefinitionProvider.getDefinitionLocations(gscFile, position); return locations; } catch (error) { @@ -34,16 +34,8 @@ export class GscDefinitionProvider implements vscode.DefinitionProvider { } - /** - * This function finds definitions of function names in current file, included files and in external files - * @example - * function(1, 2); - * function_included(); - * _tests\definition_file::function_file(); - * @returns - */ - public static async getFunctionDefinitionLocations(gscFile: GscFile, position: vscode.Position): Promise { - const locations: vscode.Location[] = []; + public static async getDefinitionLocations(gscFile: GscFile, position: vscode.Position): Promise { + var locations: vscode.Location[] = []; const gscData = gscFile.data; @@ -53,14 +45,14 @@ export class GscDefinitionProvider implements vscode.DefinitionProvider { return locations; } - if (groupAtCursor.type === GroupType.FunctionName) { - const funcInfo = groupAtCursor.getFunctionReferenceInfo(); - if (funcInfo !== undefined) { - const funcDefs = GscFunctions.getFunctionDefinitions(gscFile, funcInfo); - if (funcDefs !== undefined && funcDefs.length > 0) { - locations.push(new vscode.Location(funcDefs[0].uri, funcDefs[0].func.rangeFunctionName)); - } - } + switch (groupAtCursor.type) { + case GroupType.FunctionName: + locations = await this.getFunctionDefinitionLocations(gscFile, groupAtCursor); + break; + + case GroupType.Path: + locations = await this.getPathDefinitionLocations(gscFile, groupAtCursor); + break; } //console.log(groupAtCursor.toString()); @@ -70,4 +62,41 @@ export class GscDefinitionProvider implements vscode.DefinitionProvider { return locations; } + + /** + * This function finds definitions of function names in current file, included files and in external files + * @example + * function(1, 2); + * function_included(); + * _tests\definition_file::function_file(); + * @returns + */ + private static async getFunctionDefinitionLocations(gscFile: GscFile, groupAtCursor: GscGroup): Promise { + const locations: vscode.Location[] = []; + + const funcInfo = groupAtCursor.getFunctionReferenceInfo(); + if (funcInfo !== undefined) { + const funcDefs = GscFunctions.getFunctionDefinitions(gscFile, funcInfo); + if (funcDefs !== undefined && funcDefs.length > 0) { + locations.push(new vscode.Location(funcDefs[0].uri, funcDefs[0].func.rangeFunctionName)); + } + } + + return locations; + } + + + /** This function finds referenced files by game path */ + private static async getPathDefinitionLocations(gscFile: GscFile, groupAtCursor: GscGroup): Promise { + const locations: vscode.Location[] = []; + + const path = groupAtCursor.getTokensAsString(); + const fileReference = GscFiles.getReferencedFileForFile(gscFile, path); + if (fileReference !== undefined && fileReference.gscFile !== undefined) { + locations.push(new vscode.Location(fileReference.gscFile.uri, fileReference.gscFile.data.root.getRange())); + } + + return locations; + } + } \ No newline at end of file diff --git a/src/GscHoverProvider.ts b/src/GscHoverProvider.ts index f065fe9..de4b3a0 100644 --- a/src/GscHoverProvider.ts +++ b/src/GscHoverProvider.ts @@ -39,11 +39,14 @@ export class GscHoverProvider implements vscode.HoverProvider { public static async getHover(gscFile: GscFile, position: vscode.Position): Promise { + let hoverRange: vscode.Range | undefined = undefined; let markdown = new vscode.MarkdownString(); markdown.isTrusted = true; // enable HTML tags const gscData = gscFile.data; const uri = gscFile.uri; + const isUniversalGame = GscConfig.isUniversalGame(gscFile.config.currentGame); + const errorDiagnosticsDisabled = gscFile.config.errorDiagnostics === ConfigErrorDiagnostics.Disable; // Get group before cursor var groupAtCursor = gscData.root.findGroupOnLeftAtPosition(position); @@ -52,9 +55,6 @@ export class GscHoverProvider implements vscode.HoverProvider { const funcInfo = groupAtCursor.getFunctionReferenceInfo(); if (funcInfo !== undefined) { - const isUniversalGame = GscConfig.isUniversalGame(gscFile.config.currentGame); - const errorDiagnosticsDisabled = gscFile.config.errorDiagnostics === ConfigErrorDiagnostics.Disable; - const res = GscFunctions.getFunctionReferenceState({name: funcInfo.name, path: funcInfo.path}, gscFile); switch (res.state as GscFunctionState) { @@ -155,12 +155,27 @@ export class GscHoverProvider implements vscode.HoverProvider { } + + } else if (groupAtCursor?.type === GroupType.Path) { + const path = groupAtCursor.getTokensAsString(); + const fileReference = GscFiles.getReferencedFileForFile(gscFile, path); + if (fileReference !== undefined && fileReference.gscFile !== undefined) { + markdown.appendMarkdown("File: `" + vscode.workspace.asRelativePath(fileReference.gscFile.uri, true) + "`"); + hoverRange = groupAtCursor.getRange(); + } else { + // There would be error by diagnostics, unless disabled + if (errorDiagnosticsDisabled) { + markdown.appendText(`⚠️ Path '${path}' is not valid!`); + markdown.appendText(`\n\n🛈 Error diagnostics disabled via workspace settings`); + } + } + } if (markdown.value === "") { return undefined; } else { - return new vscode.Hover(markdown); + return new vscode.Hover(markdown, hoverRange); } } diff --git a/src/test/Tests.test.ts b/src/test/Tests.test.ts index fb0aaa0..778575e 100644 --- a/src/test/Tests.test.ts +++ b/src/test/Tests.test.ts @@ -140,7 +140,7 @@ export function checkDefinition(locations: vscode.Location[], expectedFileEnd: s } export async function checkDefinitionFunc(gscFile: GscFile, pos: vscode.Position, pathUri: string) { - const locations = await GscDefinitionProvider.getFunctionDefinitionLocations(gscFile, pos); + const locations = await GscDefinitionProvider.getDefinitionLocations(gscFile, pos); checkDefinition(locations, pathUri); } diff --git a/src/test/workspace/GscAll_CoD2MP.test.ts b/src/test/workspace/GscAll_CoD2MP.test.ts index a6e2242..da1851f 100644 --- a/src/test/workspace/GscAll_CoD2MP.test.ts +++ b/src/test/workspace/GscAll_CoD2MP.test.ts @@ -94,7 +94,7 @@ suite('GscAll.CoD2MP', () => { const hover1 = await GscHoverProvider.getHover(gsc, new vscode.Position(3, 6)); tests.checkHover(hover1, GscFunction.generateMarkdownDescription({name: "func1", parameters: []}, true, tests.filePathToUri("GscAll.CoD2MP/scripts/ItselfInclude.gsc").toString()).value); - const locations1 = await GscDefinitionProvider.getFunctionDefinitionLocations(gsc, new vscode.Position(3, 6)); + const locations1 = await GscDefinitionProvider.getDefinitionLocations(gsc, new vscode.Position(3, 6)); tests.checkDefinition(locations1, "GscAll.CoD2MP/scripts/ItselfInclude.gsc"); diff --git a/src/test/workspace/GscAll_UniversalGame.test.ts b/src/test/workspace/GscAll_UniversalGame.test.ts index ddbe5b7..4e4564a 100644 --- a/src/test/workspace/GscAll_UniversalGame.test.ts +++ b/src/test/workspace/GscAll_UniversalGame.test.ts @@ -75,7 +75,7 @@ suite('GscAll.UniversalGame', () => { const hover1 = await GscHoverProvider.getHover(gsc, new vscode.Position(3, 6)); tests.checkHover(hover1, GscFunction.generateMarkdownDescription({name: "func1", parameters: []}, true, tests.filePathToUri("GscAll.UniversalGame/scripts/ItselfInclude.gsc").toString()).value); - const locations1 = await GscDefinitionProvider.getFunctionDefinitionLocations(gsc, new vscode.Position(3, 6)); + const locations1 = await GscDefinitionProvider.getDefinitionLocations(gsc, new vscode.Position(3, 6)); tests.checkDefinition(locations1, "GscAll.UniversalGame/scripts/ItselfInclude.gsc"); diff --git a/src/test/workspace/GscFileReferences.test.ts b/src/test/workspace/GscFileReferences.test.ts index c56e176..cff470f 100644 --- a/src/test/workspace/GscFileReferences.test.ts +++ b/src/test/workspace/GscFileReferences.test.ts @@ -25,45 +25,56 @@ suite('GscFileReferences', () => { // There should be no error - everything is case insensitive assert.ok(gsc.diagnostics.length === 0); + const hover1_include = await GscHoverProvider.getHover(gsc, new vscode.Position(0, 40)); + tests.checkHover(hover1_include, "File: `" + vscode.workspace.asRelativePath(tests.filePathToUri("GscFileReferences.1/LowerUpperCaseFolder/FunctionReferencesFile.gsc"), true) + "`"); + // Correct path // FunctionReferencesFolder\FunctionReferencesFile::funcName(); const hover1 = await GscHoverProvider.getHover(gsc, new vscode.Position(5, 57)); tests.checkHover(hover1, GscFunction.generateMarkdownDescription({name: "funcName", parameters: []}, false, tests.filePathToUri("GscFileReferences.1/LowerUpperCaseFolder/FunctionReferencesFile.gsc").toString()).value); - //checkHover(hover1, "\n```\nfuncName()\n```\nFile: ```GscFileReferences.1/LowerUpperCaseFolder/FunctionReferencesFile.gsc```"); - const locations1 = await GscDefinitionProvider.getFunctionDefinitionLocations(gsc, new vscode.Position(5, 57)); + const hover1_file = await GscHoverProvider.getHover(gsc, new vscode.Position(5, 40)); + tests.checkHover(hover1_file, "File: `" + vscode.workspace.asRelativePath(tests.filePathToUri("GscFileReferences.1/LowerUpperCaseFolder/FunctionReferencesFile.gsc"), true) + "`"); + const locations1 = await GscDefinitionProvider.getDefinitionLocations(gsc, new vscode.Position(5, 57)); tests.checkDefinition(locations1, "GscFileReferences.1/LowerUpperCaseFolder/FunctionReferencesFile.gsc"); // Lowercase path // functionreferencesfolder\FunctionReferencesFile::funcName(); const hover2 = await GscHoverProvider.getHover(gsc, new vscode.Position(8, 57)); tests.checkHover(hover2, GscFunction.generateMarkdownDescription({name: "funcName", parameters: []}, false, tests.filePathToUri("GscFileReferences.1/LowerUpperCaseFolder/FunctionReferencesFile.gsc").toString()).value); - const locations2 = await GscDefinitionProvider.getFunctionDefinitionLocations(gsc, new vscode.Position(8, 57)); + const hover2_file = await GscHoverProvider.getHover(gsc, new vscode.Position(8, 40)); + tests.checkHover(hover2_file, "File: `" + vscode.workspace.asRelativePath(tests.filePathToUri("GscFileReferences.1/LowerUpperCaseFolder/FunctionReferencesFile.gsc"), true) + "`"); + const locations2 = await GscDefinitionProvider.getDefinitionLocations(gsc, new vscode.Position(8, 57)); tests.checkDefinition(locations2, "GscFileReferences.1/LowerUpperCaseFolder/FunctionReferencesFile.gsc"); // Lowercase path + file // functionreferencesfolder\functionreferencesfile::funcName(); const hover3 = await GscHoverProvider.getHover(gsc, new vscode.Position(11, 57)); tests.checkHover(hover3, GscFunction.generateMarkdownDescription({name: "funcName", parameters: []}, false, tests.filePathToUri("GscFileReferences.1/LowerUpperCaseFolder/FunctionReferencesFile.gsc").toString()).value); - const locations3 = await GscDefinitionProvider.getFunctionDefinitionLocations(gsc, new vscode.Position(11, 57)); + const hover3_file = await GscHoverProvider.getHover(gsc, new vscode.Position(11, 40)); + tests.checkHover(hover3_file, "File: `" + vscode.workspace.asRelativePath(tests.filePathToUri("GscFileReferences.1/LowerUpperCaseFolder/FunctionReferencesFile.gsc"), true) + "`"); + const locations3 = await GscDefinitionProvider.getDefinitionLocations(gsc, new vscode.Position(11, 57)); tests.checkDefinition(locations3, "GscFileReferences.1/LowerUpperCaseFolder/FunctionReferencesFile.gsc"); // Lowercase path + file + func name // functionreferencesfolder\functionreferencesfile::funcname(); const hover4 = await GscHoverProvider.getHover(gsc, new vscode.Position(14, 57)); tests.checkHover(hover4, GscFunction.generateMarkdownDescription({name: "funcName", parameters: []}, false, tests.filePathToUri("GscFileReferences.1/LowerUpperCaseFolder/FunctionReferencesFile.gsc").toString()).value); - const locations4 = await GscDefinitionProvider.getFunctionDefinitionLocations(gsc, new vscode.Position(14, 57)); + const hover4_file = await GscHoverProvider.getHover(gsc, new vscode.Position(14, 40)); + tests.checkHover(hover4_file, "File: `" + vscode.workspace.asRelativePath(tests.filePathToUri("GscFileReferences.1/LowerUpperCaseFolder/FunctionReferencesFile.gsc"), true) + "`"); + const locations4 = await GscDefinitionProvider.getDefinitionLocations(gsc, new vscode.Position(14, 57)); tests.checkDefinition(locations4, "GscFileReferences.1/LowerUpperCaseFolder/FunctionReferencesFile.gsc"); + // Hover over "includedFuncName();" - its case insensitive, external file should be found const hover5 = await GscHoverProvider.getHover(gsc, new vscode.Position(17, 8)); tests.checkHover(hover5, GscFunction.generateMarkdownDescription({name: "includedFuncName", parameters: []}, false, tests.filePathToUri("GscFileReferences.1/LowerUpperCaseFolder/FunctionReferencesFile.gsc").toString(), "Included via '#include'").value); - const locations5 = await GscDefinitionProvider.getFunctionDefinitionLocations(gsc, new vscode.Position(17, 8)); + const locations5 = await GscDefinitionProvider.getDefinitionLocations(gsc, new vscode.Position(17, 8)); tests.checkDefinition(locations5, "GscFileReferences.1/LowerUpperCaseFolder/FunctionReferencesFile.gsc"); // Hover over "localFunc();" - its case insensitive, local func should be found const hover6 = await GscHoverProvider.getHover(gsc, new vscode.Position(20, 8)); tests.checkHover(hover6, GscFunction.generateMarkdownDescription({name: "LOCALFUNC", parameters: []}, true, undefined, undefined).value); - const locations6 = await GscDefinitionProvider.getFunctionDefinitionLocations(gsc, new vscode.Position(20, 8)); + const locations6 = await GscDefinitionProvider.getDefinitionLocations(gsc, new vscode.Position(20, 8)); tests.checkDefinition(locations6, "GscFileReferences.1/LowerUpperCase.gsc"); Issues.checkForNewError(); @@ -99,7 +110,7 @@ suite('GscFileReferences', () => { var position = new vscode.Position(8, 22); var hover = await GscHoverProvider.getHover(gsc, position); tests.checkHover(hover, GscFunction.generateMarkdownDescription({name: "main", parameters: []}, true, undefined, undefined).value); - var locations = await GscDefinitionProvider.getFunctionDefinitionLocations(gsc, position); + var locations = await GscDefinitionProvider.getDefinitionLocations(gsc, position); tests.checkDefinition(locations, "GscFileReferences.1/scripts/file1.gsc"); @@ -171,7 +182,7 @@ suite('GscFileReferences', () => { var position = new vscode.Position(9, 22); var hover = await GscHoverProvider.getHover(gsc, position); tests.checkHover(hover, GscFunction.generateMarkdownDescription({name: "main", parameters: []}, true, undefined, undefined).value); - var locations = await GscDefinitionProvider.getFunctionDefinitionLocations(gsc, position); + var locations = await GscDefinitionProvider.getDefinitionLocations(gsc, position); tests.checkDefinition(locations, "GscFileReferences.2/scripts/file2.gsc"); @@ -238,7 +249,7 @@ suite('GscFileReferences', () => { var position = new vscode.Position(10, 22); var hover = await GscHoverProvider.getHover(gsc, position); tests.checkHover(hover, GscFunction.generateMarkdownDescription({name: "main", parameters: []}, true, undefined, undefined).value); - var locations = await GscDefinitionProvider.getFunctionDefinitionLocations(gsc, position); + var locations = await GscDefinitionProvider.getDefinitionLocations(gsc, position); tests.checkDefinition(locations, "GscFileReferences.3/scripts/file3.gsc");