From ac0f8458ed7a0c3c5712ebf8156aa939a64e7a12 Mon Sep 17 00:00:00 2001 From: eyza Date: Sun, 20 Oct 2024 19:50:14 +0200 Subject: [PATCH] Updated function precedence for universal game to prioritize local functions, then included functions, then built-in functions; if a function is defined multiple times, the first one found is used and others are ignored without error - unlike CoD2, where duplicate function names in local or included files cause an error. Tests were reorganized, GscComplex renamed to GscAll with GscAll.UniversalGame and GscAll.CoD2 --- eslint.config.mjs | 3 +- src/GscCompletionItemProvider.ts | 6 +- src/GscConfig.ts | 7 + src/GscDefinitionProvider.ts | 8 +- src/GscDiagnosticsCollection.ts | 92 ++++--- src/GscFileParser.ts | 1 + src/GscFiles.ts | 2 +- src/GscFunctions.ts | 116 +++++---- src/GscHoverProvider.ts | 44 ++-- .../.vscode/settings.json | 0 .../casting.gsc | 0 .../functionOverwriting.gsc | 0 .../functionParameters.gsc | 0 .../functionParametersLocal.gsc | 0 .../functionUndefined.gsc | 0 .../functionUndefinedFuncs.gsc | 0 .../invalidFile.gsc | 0 .../scripts/DuplicateInclude.gsc | 23 ++ .../scripts/ItselfInclude.gsc | 0 .../scripts/file.gsc | 0 .../scripts/file2.gsc | 0 .../workspace/GscAll.CoD2MP/scripts/file3.gsc | 3 + .../workspace/GscAll.CoD2MP/scripts/file4.gsc | 3 + .../workspace/GscAll.CoD2MP/scripts/file5.gsc | 3 + .../workspace/GscAll.CoD2MP/scripts/file6.gsc | 3 + .../.vscode/settings.json | 0 .../casting.gsc | 0 .../functionOverwriting.gsc | 0 .../functionParameters.gsc | 0 .../functionParametersLocal.gsc | 0 .../functionUndefined.gsc | 0 .../functionUndefinedFuncs.gsc | 0 .../invalidFile.gsc | 0 .../scripts/DuplicateInclude.gsc | 23 ++ .../scripts/ItselfInclude.gsc | 0 .../GscAll.UniversalGame/scripts/file.gsc | 3 + .../GscAll.UniversalGame/scripts/file2.gsc | 3 + .../GscAll.UniversalGame/scripts/file3.gsc | 3 + .../GscAll.UniversalGame/scripts/file4.gsc | 3 + .../GscAll.UniversalGame/scripts/file5.gsc | 3 + .../GscAll.UniversalGame/scripts/file6.gsc | 3 + .../{GscComplex.test.ts => GscAll.test.ts} | 55 ++-- .../workspace/GscAll/DuplicatedFunction.gsc | 7 + .../FunctionReferences.gsc | 0 .../FunctionReferencesFile.gsc | 0 .../SingleLine.gsc | 0 src/test/workspace/GscAll_CoD2MP.test.ts | 147 +++++++++++ .../workspace/GscAll_UniversalGame.test.ts | 123 +++++++++ .../GscDiagnosticsCollection.test.ts | 236 ------------------ .../scripts/DuplicateFunctionViaInclude.gsc | 11 - .../scripts/DuplicateInclude.gsc | 6 - .../vscode-cod-gsc-tests.code-workspace | 16 +- 52 files changed, 573 insertions(+), 383 deletions(-) rename src/test/workspace/{GscDiagnosticsCollection.CoD2MP => GscAll.CoD2MP}/.vscode/settings.json (100%) rename src/test/workspace/{GscDiagnosticsCollection.CoD2MP => GscAll.CoD2MP}/casting.gsc (100%) rename src/test/workspace/{GscDiagnosticsCollection.CoD2MP => GscAll.CoD2MP}/functionOverwriting.gsc (100%) rename src/test/workspace/{GscDiagnosticsCollection.CoD2MP => GscAll.CoD2MP}/functionParameters.gsc (100%) rename src/test/workspace/{GscDiagnosticsCollection.CoD2MP => GscAll.CoD2MP}/functionParametersLocal.gsc (100%) rename src/test/workspace/{GscDiagnosticsCollection.CoD2MP => GscAll.CoD2MP}/functionUndefined.gsc (100%) rename src/test/workspace/{GscDiagnosticsCollection.CoD2MP => GscAll.CoD2MP}/functionUndefinedFuncs.gsc (100%) rename src/test/workspace/{GscDiagnosticsCollection.CoD2MP => GscAll.CoD2MP}/invalidFile.gsc (100%) create mode 100644 src/test/workspace/GscAll.CoD2MP/scripts/DuplicateInclude.gsc rename src/test/workspace/{GscDiagnosticsCollection.CoD2MP => GscAll.CoD2MP}/scripts/ItselfInclude.gsc (100%) rename src/test/workspace/{GscDiagnosticsCollection => GscAll.CoD2MP}/scripts/file.gsc (100%) rename src/test/workspace/{GscDiagnosticsCollection => GscAll.CoD2MP}/scripts/file2.gsc (100%) create mode 100644 src/test/workspace/GscAll.CoD2MP/scripts/file3.gsc create mode 100644 src/test/workspace/GscAll.CoD2MP/scripts/file4.gsc create mode 100644 src/test/workspace/GscAll.CoD2MP/scripts/file5.gsc create mode 100644 src/test/workspace/GscAll.CoD2MP/scripts/file6.gsc rename src/test/workspace/{GscDiagnosticsCollection.UniversalGame => GscAll.UniversalGame}/.vscode/settings.json (100%) rename src/test/workspace/{GscDiagnosticsCollection.UniversalGame => GscAll.UniversalGame}/casting.gsc (100%) rename src/test/workspace/{GscDiagnosticsCollection.UniversalGame => GscAll.UniversalGame}/functionOverwriting.gsc (100%) rename src/test/workspace/{GscDiagnosticsCollection.UniversalGame => GscAll.UniversalGame}/functionParameters.gsc (100%) rename src/test/workspace/{GscDiagnosticsCollection.UniversalGame => GscAll.UniversalGame}/functionParametersLocal.gsc (100%) rename src/test/workspace/{GscDiagnosticsCollection.UniversalGame => GscAll.UniversalGame}/functionUndefined.gsc (100%) rename src/test/workspace/{GscDiagnosticsCollection.UniversalGame => GscAll.UniversalGame}/functionUndefinedFuncs.gsc (100%) rename src/test/workspace/{GscDiagnosticsCollection.UniversalGame => GscAll.UniversalGame}/invalidFile.gsc (100%) create mode 100644 src/test/workspace/GscAll.UniversalGame/scripts/DuplicateInclude.gsc rename src/test/workspace/{GscDiagnosticsCollection.UniversalGame => GscAll.UniversalGame}/scripts/ItselfInclude.gsc (100%) create mode 100644 src/test/workspace/GscAll.UniversalGame/scripts/file.gsc create mode 100644 src/test/workspace/GscAll.UniversalGame/scripts/file2.gsc create mode 100644 src/test/workspace/GscAll.UniversalGame/scripts/file3.gsc create mode 100644 src/test/workspace/GscAll.UniversalGame/scripts/file4.gsc create mode 100644 src/test/workspace/GscAll.UniversalGame/scripts/file5.gsc create mode 100644 src/test/workspace/GscAll.UniversalGame/scripts/file6.gsc rename src/test/workspace/{GscComplex.test.ts => GscAll.test.ts} (59%) create mode 100644 src/test/workspace/GscAll/DuplicatedFunction.gsc rename src/test/workspace/{GscComplex => GscAll}/FunctionReferences.gsc (100%) rename src/test/workspace/{GscComplex => GscAll}/FunctionReferencesFolder/FunctionReferencesFile.gsc (100%) rename src/test/workspace/{GscDiagnosticsCollection => GscAll}/SingleLine.gsc (100%) create mode 100644 src/test/workspace/GscAll_CoD2MP.test.ts create mode 100644 src/test/workspace/GscAll_UniversalGame.test.ts delete mode 100644 src/test/workspace/GscDiagnosticsCollection.test.ts delete mode 100644 src/test/workspace/GscDiagnosticsCollection/scripts/DuplicateFunctionViaInclude.gsc delete mode 100644 src/test/workspace/GscDiagnosticsCollection/scripts/DuplicateInclude.gsc diff --git a/eslint.config.mjs b/eslint.config.mjs index 095c695..76c0bcb 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -25,7 +25,8 @@ export default [ format: ["camelCase", "PascalCase"], }, ], - "@typescript-eslint/no-floating-promises": "error", // For TypeScript files + "@typescript-eslint/no-floating-promises": "error", // Error on not awaited promises + "@typescript-eslint/await-thenable": "warn", // Warn on awaited non-async function calls curly: "warn", eqeqeq: "warn", "no-throw-literal": "warn", diff --git a/src/GscCompletionItemProvider.ts b/src/GscCompletionItemProvider.ts index c0f041e..3f223ac 100644 --- a/src/GscCompletionItemProvider.ts +++ b/src/GscCompletionItemProvider.ts @@ -80,7 +80,7 @@ export class GscCompletionItemProvider implements vscode.CompletionItemProvider // Get current function data const functionGroup = gscData.functions.find(f => { - const range = f.scopeRange; + const range = f.rangeScope; if (((position.line === range.start.line && position.character > range.start.character) || position.line > range.start.line) && ((position.line === range.end.line && position.character < range.end.character) || position.line < range.end.line)) { @@ -360,11 +360,11 @@ export class GscCompletionItemProvider implements vscode.CompletionItemProvider // Uri is undefined in tests if (gscFile !== undefined) { // Local functions and included functions - const res = await GscFunctions.getFunctionReferenceState(undefined, gscFile); + const res = GscFunctions.getFunctionReferenceState(undefined, gscFile); res.definitions.forEach(f => { const item = new vscode.CompletionItem({label: f.func.name, description: "", detail: ""}, vscode.CompletionItemKind.Function); - item.documentation = f.func.generateMarkdownDescription(f.uri === gscFile.uri.toString(), f.uri, f.reason); + item.documentation = f.func.generateMarkdownDescription(f.uri.toString() === gscFile.uri.toString(), f.uri.toString(), f.reason); completionItems.push(item); }); } diff --git a/src/GscConfig.ts b/src/GscConfig.ts index 3063ba1..fb4f896 100644 --- a/src/GscConfig.ts +++ b/src/GscConfig.ts @@ -40,6 +40,9 @@ export type GscGameConfig = { developerBlocks: boolean; /** Allow /# #/ inside another /# #/ */ developerBlocksRecursive: boolean; + /** Allow duplicate function definitions */ + duplicateFunctionDefinitions: boolean; + foreach: boolean; doWhile: boolean; @@ -60,6 +63,7 @@ export class GscConfig { globalVariables: true, developerBlocks: true, developerBlocksRecursive: true, + duplicateFunctionDefinitions: true, foreach: true, doWhile: true, arrayInitializer: true, @@ -72,6 +76,7 @@ export class GscConfig { globalVariables: false, developerBlocks: false, developerBlocksRecursive: false, + duplicateFunctionDefinitions: false, foreach: false, doWhile: true, arrayInitializer: false, @@ -84,6 +89,7 @@ export class GscConfig { globalVariables: false, developerBlocks: true, developerBlocksRecursive: false, + duplicateFunctionDefinitions: false, foreach: false, doWhile: false, arrayInitializer: false, @@ -96,6 +102,7 @@ export class GscConfig { globalVariables: false, developerBlocks: true, developerBlocksRecursive: false, + duplicateFunctionDefinitions: false, foreach: false, doWhile: false, arrayInitializer: false, diff --git a/src/GscDefinitionProvider.ts b/src/GscDefinitionProvider.ts index 12c603f..4f86715 100644 --- a/src/GscDefinitionProvider.ts +++ b/src/GscDefinitionProvider.ts @@ -56,11 +56,9 @@ export class GscDefinitionProvider implements vscode.DefinitionProvider { if (groupAtCursor.type === GroupType.FunctionName) { const funcInfo = groupAtCursor.getFunctionReferenceInfo(); if (funcInfo !== undefined) { - const funcDefs = await GscFunctions.getAvailableFunctionsForFile(gscFile, funcInfo.name, funcInfo.path); - if (funcDefs !== undefined) { - funcDefs.forEach(f => { - locations.push(new vscode.Location(vscode.Uri.parse(f.uri), new vscode.Position(f.func.range.start.line, f.func.range.start.character))); - }); + const funcDefs = GscFunctions.getFunctionDefinitions(gscFile, funcInfo); + if (funcDefs !== undefined && funcDefs.length > 0) { + locations.push(new vscode.Location(funcDefs[0].uri, funcDefs[0].func.rangeFunctionName)); } } } diff --git a/src/GscDiagnosticsCollection.ts b/src/GscDiagnosticsCollection.ts index 5881a08..99e2915 100644 --- a/src/GscDiagnosticsCollection.ts +++ b/src/GscDiagnosticsCollection.ts @@ -3,8 +3,8 @@ import { GscFiles } from './GscFiles'; import { GscFile } from './GscFile'; import { GroupType, GscData, GscGroup, GscToken, TokenType } from './GscFileParser'; import { CodFunctions } from './CodFunctions'; -import { ConfigErrorDiagnostics, GscConfig, GscGame, GscGameRootFolder } from './GscConfig'; -import { GscFunctions, GscFunctionState } from './GscFunctions'; +import { ConfigErrorDiagnostics, GscConfig, GscGame } from './GscConfig'; +import { GscFunctionDefinition, GscFunctions, GscFunctionState } from './GscFunctions'; import { assert } from 'console'; import { LoggerOutput } from './LoggerOutput'; import { Issues } from './Issues'; @@ -87,7 +87,7 @@ export class GscDiagnosticsCollection { this.statusBarItem.tooltip = data.uri.toString(); } - count += await this.updateDiagnosticsForFile(data); + count += this.updateDiagnosticsForFile(data); // Check if it's time to pause for UI update const now = Date.now(); @@ -123,7 +123,7 @@ export class GscDiagnosticsCollection { * @param gscFile The GSC file to generate diagnostics for. * @returns The number of diagnostics created. */ - public static async updateDiagnosticsForFile(gscFile: GscFile): Promise { + public static updateDiagnosticsForFile(gscFile: GscFile): number { try { LoggerOutput.log("[GscDiagnosticsCollection] Creating diagnostics for file", vscode.workspace.asRelativePath(gscFile.uri)); @@ -151,18 +151,20 @@ export class GscDiagnosticsCollection { walkGroupItems(gscFile.data.root, gscFile.data.root.items); - // Create diagnostic for function names - for (const d of groupFunctionNames) { - const diag = await GscDiagnosticsCollection.createDiagnosticsForFunctionName(d.group, gscFile); + // Create diagnostic for included files + const includedPaths = groupIncludedPaths.map(g => g.group.getTokensAsString()); + for (let i = 0; i < groupIncludedPaths.length; i++) { + const d = groupIncludedPaths[i]; + const path = d.group.getTokensAsString(); + const diag = GscDiagnosticsCollection.createDiagnosticsForIncludedPaths(gscFile, d.group, path, includedPaths, i); if (diag) { gscFile.diagnostics.push(diag); } } - // Create diagnostic for included files - const includedPaths = groupIncludedPaths.map(g => g.group.getTokensAsString()); - for (const d of groupIncludedPaths) { - const diag = GscDiagnosticsCollection.createDiagnosticsForIncludedPaths(d.group, gscFile, includedPaths); + // Create diagnostic for function names + for (const d of groupFunctionNames) { + const diag = GscDiagnosticsCollection.createDiagnosticsForFunctionName(d.group, gscFile); if (diag) { gscFile.diagnostics.push(diag); } @@ -370,12 +372,10 @@ export class GscDiagnosticsCollection { } - private static createDiagnosticsForIncludedPaths(group: GscGroup, gscFile: GscFile, includedPaths: string[]): vscode.Diagnostic | undefined { + private static createDiagnosticsForIncludedPaths(gscFile: GscFile, group: GscGroup, path: string, allIncludedPaths: string[], index: number): vscode.Diagnostic | undefined { assert(group.type === GroupType.Path); - const tokensAsPath = group.getTokensAsString(); - - const referenceData = GscFiles.getReferencedFileForFile(gscFile, tokensAsPath); + const referenceData = GscFiles.getReferencedFileForFile(gscFile, path); if (!gscFile.config.gameConfig.includeFileItself && referenceData.gscFile?.uri.toString() === gscFile.uri.toString()) { return new vscode.Diagnostic(group.getRange(), "File is including itself", vscode.DiagnosticSeverity.Error); @@ -383,8 +383,8 @@ export class GscDiagnosticsCollection { // Find how many times this file is included let count = 0; - for (const includedPath of includedPaths) { - if (includedPath === tokensAsPath) { + for (const includedPath of allIncludedPaths) { + if (includedPath === path) { count++; } if (count >= 2) { @@ -392,31 +392,68 @@ export class GscDiagnosticsCollection { } } + // Check for duplicated function definitions + if (gscFile.config.gameConfig.duplicateFunctionDefinitions === false && referenceData.gscFile) { + // Get all function definitions from included file + const funcDefsInIncludedFile = GscFunctions.getLocalFunctionDefinitions(referenceData.gscFile); + + const alreadyDefinedFunctions: GscFunctionDefinition[] = []; + + const funcDefsFromLocalFile = GscFunctions.getLocalFunctionDefinitions(gscFile); + alreadyDefinedFunctions.push(...funcDefsFromLocalFile.map(f => ({func: f, uri: gscFile.uri, reason: "Local file"}))); + + for (let j = 0; j < index; j++) { + const otherPath = allIncludedPaths[j]; + const otherReferenceData = GscFiles.getReferencedFileForFile(gscFile, otherPath); + if (!otherReferenceData.gscFile) { + continue; + } + const otherFuncDefs = GscFunctions.getLocalFunctionDefinitions(otherReferenceData.gscFile); + + alreadyDefinedFunctions.push(...otherFuncDefs.map(f => ({func: f, uri: otherReferenceData.gscFile!.uri, reason: "Included file"}))); + } + + for (const funcDef of funcDefsInIncludedFile) { + for (const alreadyDefinedFunction of alreadyDefinedFunctions) { + if (funcDef.nameId === alreadyDefinedFunction.func.nameId) { + const s = alreadyDefinedFunction.reason === "Local file" ? "this file" : "included file '" + vscode.workspace.asRelativePath(alreadyDefinedFunction.uri) + "'"; + return new vscode.Diagnostic(group.getRange(), `Function '${funcDef.name}' is already defined in ${s}!`, vscode.DiagnosticSeverity.Error); + } + } + } + } + if (referenceData.gscFile === undefined) { // This file path is ignored by configuration - if (gscFile.config.ignoredFilePaths.some(ignoredPath => tokensAsPath.toLowerCase().startsWith(ignoredPath.toLowerCase()))) { + if (gscFile.config.ignoredFilePaths.some(ignoredPath => path.toLowerCase().startsWith(ignoredPath.toLowerCase()))) { return; } const d = new vscode.Diagnostic( group.getRange(), - `File '${tokensAsPath}.gsc' was not found in workspace folder ${gscFile.config.referenceableGameRootFolders.map(f => "'" + f.relativePath + "'").join(", ")}`, + `File '${path}.gsc' was not found in workspace folder ${gscFile.config.referenceableGameRootFolders.map(f => "'" + f.relativePath + "'").join(", ")}`, vscode.DiagnosticSeverity.Error); - d.code = "unknown_file_path_" + tokensAsPath; + d.code = "unknown_file_path_" + path; return d; } } - private static async createDiagnosticsForFunctionName( + private static createDiagnosticsForFunctionName( group: GscGroup, gscFile: GscFile) { // Function declaration if (group.parent?.type === GroupType.FunctionDeclaration) { - // TODO check for duplicate - + const funcName = group.getFirstToken().name; + + // Check for duplicate function name definition + const defs = GscFunctions.getLocalFunctionDefinitions(gscFile, funcName); + if (defs.length > 1) { + return new vscode.Diagnostic(group.getRange(), `Duplicate function definition of '${funcName}'!`, vscode.DiagnosticSeverity.Error); + } + // This function is overwriting the build-in function if (CodFunctions.isPredefinedFunction(funcName, gscFile.config.currentGame)) { @@ -430,13 +467,13 @@ export class GscDiagnosticsCollection { const funcInfo = group.getFunctionReferenceInfo(); if (funcInfo !== undefined) { - const res = await GscFunctions.getFunctionReferenceState({name: funcInfo.name, path: funcInfo.path}, gscFile); + const res = GscFunctions.getFunctionReferenceState({name: funcInfo.name, path: funcInfo.path}, gscFile); switch (res.state as GscFunctionState) { case GscFunctionState.NameIgnored: return; - // Function was found in exactly one place + // Function was found case GscFunctionState.Found: const funcDef = res.definitions[0].func; if (funcInfo.params.length > funcDef.parameters.length) { @@ -451,11 +488,6 @@ export class GscDiagnosticsCollection { break; - // Function is defined on too many places - case GscFunctionState.FoundOnMultiplePlaces: - return new vscode.Diagnostic(group.getRange(), `Function '${funcInfo.name}' is defined in ${res.definitions.length} places!`, vscode.DiagnosticSeverity.Error); - - // This function is predefined function case GscFunctionState.FoundInPredefined: // Find in predefined functions diff --git a/src/GscFileParser.ts b/src/GscFileParser.ts index 7e82e77..d14854a 100644 --- a/src/GscFileParser.ts +++ b/src/GscFileParser.ts @@ -2263,6 +2263,7 @@ export class GscFileParser { paramTokens, [], innerGroup.getRange(), + innerGroup.items[0].items[0].getRange(), innerGroup.items[1].getRange() ); diff --git a/src/GscFiles.ts b/src/GscFiles.ts index da7408d..9eff240 100644 --- a/src/GscFiles.ts +++ b/src/GscFiles.ts @@ -469,7 +469,7 @@ export class GscFiles { LoggerOutput.log("Debouncing done (" + debounceTime + "ms elapsed) - diagnostics update for file (" + debugText + ")", vscode.workspace.asRelativePath(uri!)); this.debounceTimersDiagnostics.delete(uriString); - void GscDiagnosticsCollection.updateDiagnosticsForFile(gscFile); + GscDiagnosticsCollection.updateDiagnosticsForFile(gscFile); }, debounceTime)); } else { this.debounceTimersDiagnostics.set("", setTimeout(() => { diff --git a/src/GscFunctions.ts b/src/GscFunctions.ts index 1c72ff4..700abf8 100644 --- a/src/GscFunctions.ts +++ b/src/GscFunctions.ts @@ -17,7 +17,8 @@ export class GscFunction { /** Local variable declarations like "a = 1;". Its always statement with item[0] as Reference */ public localVariableDefinitions: GscVariableDefinition[], public range: vscode.Range, - public scopeRange: vscode.Range, + public rangeFunctionName: vscode.Range, + public rangeScope: vscode.Range, ) {} public static generateMarkdownDescription( @@ -72,7 +73,7 @@ export class GscFunction { export type GscFunctionDefinition = { func: GscFunction, - uri: string, + uri: vscode.Uri, reason: string }; @@ -86,10 +87,10 @@ export type GscVariableDefinition = { + export enum GscFunctionState { NameIgnored, Found, - FoundOnMultiplePlaces, FoundInPredefined, NotFoundFile, NotFoundFileButIgnored, @@ -119,25 +120,20 @@ export class GscFunctions { * Get the state of the function reference and its definitions. * It will tell if referenced function is found, not found, found on multiple places, ignored, etc. */ - static async getFunctionReferenceState( + static getFunctionReferenceState( funcInfo: {name:string, path: string} | undefined, gscFile: GscFile) - : Promise + : GscFunctionStateAndDefinitions { function ret(state: GscFunctionState, definitions: GscFunctionDefinition[]) { return {state: state, definitions: definitions}; } - - // This function name is ignored by configuration - if (funcInfo && gscFile.config.ignoredFunctionNames.some(name => name.toLowerCase() === funcInfo.name.toLowerCase())) { - return ret(GscFunctionState.NameIgnored, []); - } // Get file URI and position where the file is defined - const definitions = await GscFunctions.getAvailableFunctionsForFile(gscFile, funcInfo?.name, funcInfo?.path); + const definitions = GscFunctions.getFunctionDefinitions(gscFile, funcInfo); // File not found - if (definitions === undefined) { + if (definitions === undefined) { // This file path is ignored by configuration if (funcInfo && gscFile.config.ignoredFilePaths.some(ignoredPath => funcInfo.path.toLowerCase().startsWith(ignoredPath.toLowerCase()))) { return ret(GscFunctionState.NotFoundFileButIgnored, []); @@ -146,16 +142,10 @@ export class GscFunctions { } // Function was found in exactly one place - else if (definitions.length === 1) { + else if (definitions.length >= 1) { return ret(GscFunctionState.Found, definitions); } - // Function is defined on too many places - else if (definitions.length > 1) { - return ret(GscFunctionState.FoundOnMultiplePlaces, definitions); - } - - // This function is predefined function else if (funcInfo && funcInfo.path === "" && CodFunctions.isPredefinedFunction(funcInfo.name, gscFile.config.currentGame)) { return ret(GscFunctionState.FoundInPredefined, definitions); @@ -164,8 +154,12 @@ export class GscFunctions { // Function name was not found else { + // This function name is ignored by configuration + if (funcInfo && gscFile.config.ignoredFunctionNames.some(name => name.toLowerCase() === funcInfo.name.toLowerCase())) { + return ret(GscFunctionState.NameIgnored, []); // TODO rename to NotFoundNameButIgnored? + // External function call - if (funcInfo && funcInfo.path.length > 0) { + } else if (funcInfo && funcInfo.path.length > 0) { return ret(GscFunctionState.NotFoundFunctionExternal, definitions); // Local function call @@ -176,38 +170,58 @@ export class GscFunctions { } + /** + * Get function defined in specified GSC file by its name. Only current file is used, included files are ignored. + * If the function name is not specified, all functions are returned. + */ + public static getLocalFunctionDefinitions(gscFile: GscFile, funcName?: string) : GscFunction[] + { + if (funcName === undefined) { + return gscFile.data.functions; + } + const funcNameId = funcName.toLowerCase(); + + const functions = gscFile.data.functions.filter(f => f.nameId === funcNameId); + + return functions; + } + /** - * Get array of functions that are available for specified document URI. If funcName is specified, only functions with that name are returned. - * If the file is not found, undefined is returned. If file is found but function is not found, it will return an empty array. + * Get function definitions available for specified GSC file by its name and file path. + * It uses local or external file and files included by '#include' directive. + * If the function name and file path is not specified, all functions are returned. + * If the file path is specified but the file is not found, undefined is returned. If the file is found, but function name is not, it will return an empty array. */ - public static async getAvailableFunctionsForFile(gscFile: GscFile, funcName: string | undefined = undefined, funcFilePath: string | undefined = undefined) - : Promise + public static getFunctionDefinitions(gscFile: GscFile, funcInfo: {name: string, path: string | ""} | undefined) : GscFunctionDefinition[] | undefined { - const funcDefs: GscFunctionDefinition [] = []; - const funcNameId = funcName ? funcName.toLowerCase() : undefined; + let funcDefinitions: GscFunctionDefinition[] = []; + const funcNameId = funcInfo ? funcInfo.name.toLowerCase() : undefined; if (!gscFile.workspaceFolder) { return undefined; // This file is not part of workspace } // Its external function call - if (funcFilePath && funcFilePath.length > 0) + if (funcInfo && funcInfo.path.length > 0) { - const referenceData = GscFiles.getReferencedFileForFile(gscFile, funcFilePath); + const referenceData = GscFiles.getReferencedFileForFile(gscFile, funcInfo.path); const referencedGscFile = referenceData.gscFile; - if (referencedGscFile === undefined) { return undefined; } - referencedGscFile.data.functions.forEach(f => { - if (!funcNameId || f.nameId === funcNameId) { - const reason = referenceData.referenceState === GscFileReferenceState.IncludedWorkspaceFolder ? "Included via workspace folder settings" : ""; - funcDefs.push({func: f, uri: referencedGscFile.uri.toString(), reason: reason}); + for (const f of referencedGscFile.data.functions) { + if (!funcInfo || f.nameId === funcNameId) { + const funcDefinition: GscFunctionDefinition = { + func: f, + uri: referencedGscFile.uri, + reason: referenceData.referenceState === GscFileReferenceState.IncludedWorkspaceFolder ? "Included via workspace folder settings" : "" + }; + funcDefinitions.push(funcDefinition); } - }); + } } // Its local function or included function @@ -216,34 +230,44 @@ export class GscFunctions { // TODO look into additional global include list // Find function in this file - gscFile.data.functions.forEach(f => { - if (!funcNameId || f.nameId === funcNameId) { - funcDefs.push({func: f, uri: gscFile.uri.toString(), reason: /*"Local function"*/""}); + for (const f of gscFile.data.functions) { + if (!funcInfo || f.nameId === funcNameId) { + const funcDefinition: GscFunctionDefinition = { + func: f, + uri: gscFile.uri, + reason: /*"Local function"*/"" + }; + funcDefinitions.push(funcDefinition); } - }); + } // Loop through all included files for (const includedPath of gscFile.data.includes) { const referencedFile = GscFiles.getReferencedFileForFile(gscFile, includedPath).gscFile; - if (referencedFile === undefined) { continue; // File not found } - if (referencedFile.uri.toString() === gscFile.uri.toString()) { - continue; + continue; // This includes itself, functions are already added } - referencedFile.data.functions.forEach(f => { - if (!funcNameId || f.nameId === funcNameId) { - funcDefs.push({func: f, uri: referencedFile.uri.toString(), reason: "Included via '#include'"}); + for (const f of referencedFile.data.functions) { + if (!funcInfo || f.nameId === funcNameId) { + const funcDefinition: GscFunctionDefinition = { + func: f, + uri: referencedFile.uri, + reason: "Included via '#include'" + }; + funcDefinitions.push(funcDefinition); } - }); + } + } } - return funcDefs; + return funcDefinitions; } + } \ No newline at end of file diff --git a/src/GscHoverProvider.ts b/src/GscHoverProvider.ts index 06cf8cf..f065fe9 100644 --- a/src/GscHoverProvider.ts +++ b/src/GscHoverProvider.ts @@ -55,7 +55,7 @@ export class GscHoverProvider implements vscode.HoverProvider { const isUniversalGame = GscConfig.isUniversalGame(gscFile.config.currentGame); const errorDiagnosticsDisabled = gscFile.config.errorDiagnostics === ConfigErrorDiagnostics.Disable; - const res = await GscFunctions.getFunctionReferenceState({name: funcInfo.name, path: funcInfo.path}, gscFile); + const res = GscFunctions.getFunctionReferenceState({name: funcInfo.name, path: funcInfo.path}, gscFile); switch (res.state as GscFunctionState) { case GscFunctionState.NameIgnored: @@ -63,23 +63,35 @@ export class GscHoverProvider implements vscode.HoverProvider { break; case GscFunctionState.Found: - res.definitions.forEach(async d => { - - markdown.appendMarkdown(d.func.generateMarkdownDescription(d.uri === uri.toString(), d.uri, d.reason).value); - - /*public static markdownAppendFunctionData(md: vscode.MarkdownString, fileUri: string, functionData: GscFunction) { - const parametersText = functionData.parameters.map(p => p.name).join(", "); - - md.appendCodeblock(`${functionData.name}(${parametersText})`); - - md.appendMarkdown("File: ```" + vscode.workspace.asRelativePath(vscode.Uri.parse(fileUri)) + "```"); - }*/ - }); - break; + for (let i = 0; i < res.definitions.length; i++) { + const d = res.definitions[i]; + if (!gscFile.config.gameConfig.duplicateFunctionDefinitions && i > 0) { + markdown.appendMarkdown('\n\r'); + markdown.appendMarkdown('-------------------------------------------------------------------------- \n\r'); + } - case GscFunctionState.FoundOnMultiplePlaces: - // There would be error by diagnostics + markdown.appendMarkdown(d.func.generateMarkdownDescription(d.uri.toString() === uri.toString(), d.uri.toString(), d.reason).value); + + if (gscFile.config.gameConfig.duplicateFunctionDefinitions) { + if (res.definitions.length > 1) { + const files = res.definitions + .filter((f, i) => (i > 0) && (f.uri.toString() !== uri.toString())); // ignore first definition and current file (if duplicate func) + + if (files.length > 0) { + markdown.appendMarkdown('\n\r'); + markdown.appendMarkdown('-------------------------------------------------------------------------- \n\r'); + if (files.length === 1) { + markdown.appendMarkdown(`Function '${funcInfo.name}' is also defined in this file: \n\r`); + } else { + markdown.appendMarkdown(`Function '${funcInfo.name}' is also defined in these files: \n\r`); + } + markdown.appendMarkdown(files.map(f => `\n- ${vscode.workspace.asRelativePath(f.uri)}`).join("")); + } + } + break; // Show only first definition for universal game + } + } break; diff --git a/src/test/workspace/GscDiagnosticsCollection.CoD2MP/.vscode/settings.json b/src/test/workspace/GscAll.CoD2MP/.vscode/settings.json similarity index 100% rename from src/test/workspace/GscDiagnosticsCollection.CoD2MP/.vscode/settings.json rename to src/test/workspace/GscAll.CoD2MP/.vscode/settings.json diff --git a/src/test/workspace/GscDiagnosticsCollection.CoD2MP/casting.gsc b/src/test/workspace/GscAll.CoD2MP/casting.gsc similarity index 100% rename from src/test/workspace/GscDiagnosticsCollection.CoD2MP/casting.gsc rename to src/test/workspace/GscAll.CoD2MP/casting.gsc diff --git a/src/test/workspace/GscDiagnosticsCollection.CoD2MP/functionOverwriting.gsc b/src/test/workspace/GscAll.CoD2MP/functionOverwriting.gsc similarity index 100% rename from src/test/workspace/GscDiagnosticsCollection.CoD2MP/functionOverwriting.gsc rename to src/test/workspace/GscAll.CoD2MP/functionOverwriting.gsc diff --git a/src/test/workspace/GscDiagnosticsCollection.CoD2MP/functionParameters.gsc b/src/test/workspace/GscAll.CoD2MP/functionParameters.gsc similarity index 100% rename from src/test/workspace/GscDiagnosticsCollection.CoD2MP/functionParameters.gsc rename to src/test/workspace/GscAll.CoD2MP/functionParameters.gsc diff --git a/src/test/workspace/GscDiagnosticsCollection.CoD2MP/functionParametersLocal.gsc b/src/test/workspace/GscAll.CoD2MP/functionParametersLocal.gsc similarity index 100% rename from src/test/workspace/GscDiagnosticsCollection.CoD2MP/functionParametersLocal.gsc rename to src/test/workspace/GscAll.CoD2MP/functionParametersLocal.gsc diff --git a/src/test/workspace/GscDiagnosticsCollection.CoD2MP/functionUndefined.gsc b/src/test/workspace/GscAll.CoD2MP/functionUndefined.gsc similarity index 100% rename from src/test/workspace/GscDiagnosticsCollection.CoD2MP/functionUndefined.gsc rename to src/test/workspace/GscAll.CoD2MP/functionUndefined.gsc diff --git a/src/test/workspace/GscDiagnosticsCollection.CoD2MP/functionUndefinedFuncs.gsc b/src/test/workspace/GscAll.CoD2MP/functionUndefinedFuncs.gsc similarity index 100% rename from src/test/workspace/GscDiagnosticsCollection.CoD2MP/functionUndefinedFuncs.gsc rename to src/test/workspace/GscAll.CoD2MP/functionUndefinedFuncs.gsc diff --git a/src/test/workspace/GscDiagnosticsCollection.CoD2MP/invalidFile.gsc b/src/test/workspace/GscAll.CoD2MP/invalidFile.gsc similarity index 100% rename from src/test/workspace/GscDiagnosticsCollection.CoD2MP/invalidFile.gsc rename to src/test/workspace/GscAll.CoD2MP/invalidFile.gsc diff --git a/src/test/workspace/GscAll.CoD2MP/scripts/DuplicateInclude.gsc b/src/test/workspace/GscAll.CoD2MP/scripts/DuplicateInclude.gsc new file mode 100644 index 0000000..4ec2957 --- /dev/null +++ b/src/test/workspace/GscAll.CoD2MP/scripts/DuplicateInclude.gsc @@ -0,0 +1,23 @@ +#include scripts\file; // Duplicate #include file path +#include scripts\file; // Duplicate #include file path +#include scripts\file2; // Function 'func2' is already defined in this file! +#include scripts\file3; +#include scripts\file4; // Function 'func3' is already defined in included file 'GscAll.CoD2MP/scripts/file3.gsc'! +#include scripts\file5; // Function 'func5' is already defined in this file! +#include scripts\file6; // Function 'func5' is already defined in this file! + +main() { + func1(); + func2(); +} + +func2() { + +} + +func5() { + +} + +duplicate() {} +duplicate() {} \ No newline at end of file diff --git a/src/test/workspace/GscDiagnosticsCollection.CoD2MP/scripts/ItselfInclude.gsc b/src/test/workspace/GscAll.CoD2MP/scripts/ItselfInclude.gsc similarity index 100% rename from src/test/workspace/GscDiagnosticsCollection.CoD2MP/scripts/ItselfInclude.gsc rename to src/test/workspace/GscAll.CoD2MP/scripts/ItselfInclude.gsc diff --git a/src/test/workspace/GscDiagnosticsCollection/scripts/file.gsc b/src/test/workspace/GscAll.CoD2MP/scripts/file.gsc similarity index 100% rename from src/test/workspace/GscDiagnosticsCollection/scripts/file.gsc rename to src/test/workspace/GscAll.CoD2MP/scripts/file.gsc diff --git a/src/test/workspace/GscDiagnosticsCollection/scripts/file2.gsc b/src/test/workspace/GscAll.CoD2MP/scripts/file2.gsc similarity index 100% rename from src/test/workspace/GscDiagnosticsCollection/scripts/file2.gsc rename to src/test/workspace/GscAll.CoD2MP/scripts/file2.gsc diff --git a/src/test/workspace/GscAll.CoD2MP/scripts/file3.gsc b/src/test/workspace/GscAll.CoD2MP/scripts/file3.gsc new file mode 100644 index 0000000..f14fea9 --- /dev/null +++ b/src/test/workspace/GscAll.CoD2MP/scripts/file3.gsc @@ -0,0 +1,3 @@ +func3() { + +} \ No newline at end of file diff --git a/src/test/workspace/GscAll.CoD2MP/scripts/file4.gsc b/src/test/workspace/GscAll.CoD2MP/scripts/file4.gsc new file mode 100644 index 0000000..f14fea9 --- /dev/null +++ b/src/test/workspace/GscAll.CoD2MP/scripts/file4.gsc @@ -0,0 +1,3 @@ +func3() { + +} \ No newline at end of file diff --git a/src/test/workspace/GscAll.CoD2MP/scripts/file5.gsc b/src/test/workspace/GscAll.CoD2MP/scripts/file5.gsc new file mode 100644 index 0000000..397d368 --- /dev/null +++ b/src/test/workspace/GscAll.CoD2MP/scripts/file5.gsc @@ -0,0 +1,3 @@ +func5() { + +} \ No newline at end of file diff --git a/src/test/workspace/GscAll.CoD2MP/scripts/file6.gsc b/src/test/workspace/GscAll.CoD2MP/scripts/file6.gsc new file mode 100644 index 0000000..397d368 --- /dev/null +++ b/src/test/workspace/GscAll.CoD2MP/scripts/file6.gsc @@ -0,0 +1,3 @@ +func5() { + +} \ No newline at end of file diff --git a/src/test/workspace/GscDiagnosticsCollection.UniversalGame/.vscode/settings.json b/src/test/workspace/GscAll.UniversalGame/.vscode/settings.json similarity index 100% rename from src/test/workspace/GscDiagnosticsCollection.UniversalGame/.vscode/settings.json rename to src/test/workspace/GscAll.UniversalGame/.vscode/settings.json diff --git a/src/test/workspace/GscDiagnosticsCollection.UniversalGame/casting.gsc b/src/test/workspace/GscAll.UniversalGame/casting.gsc similarity index 100% rename from src/test/workspace/GscDiagnosticsCollection.UniversalGame/casting.gsc rename to src/test/workspace/GscAll.UniversalGame/casting.gsc diff --git a/src/test/workspace/GscDiagnosticsCollection.UniversalGame/functionOverwriting.gsc b/src/test/workspace/GscAll.UniversalGame/functionOverwriting.gsc similarity index 100% rename from src/test/workspace/GscDiagnosticsCollection.UniversalGame/functionOverwriting.gsc rename to src/test/workspace/GscAll.UniversalGame/functionOverwriting.gsc diff --git a/src/test/workspace/GscDiagnosticsCollection.UniversalGame/functionParameters.gsc b/src/test/workspace/GscAll.UniversalGame/functionParameters.gsc similarity index 100% rename from src/test/workspace/GscDiagnosticsCollection.UniversalGame/functionParameters.gsc rename to src/test/workspace/GscAll.UniversalGame/functionParameters.gsc diff --git a/src/test/workspace/GscDiagnosticsCollection.UniversalGame/functionParametersLocal.gsc b/src/test/workspace/GscAll.UniversalGame/functionParametersLocal.gsc similarity index 100% rename from src/test/workspace/GscDiagnosticsCollection.UniversalGame/functionParametersLocal.gsc rename to src/test/workspace/GscAll.UniversalGame/functionParametersLocal.gsc diff --git a/src/test/workspace/GscDiagnosticsCollection.UniversalGame/functionUndefined.gsc b/src/test/workspace/GscAll.UniversalGame/functionUndefined.gsc similarity index 100% rename from src/test/workspace/GscDiagnosticsCollection.UniversalGame/functionUndefined.gsc rename to src/test/workspace/GscAll.UniversalGame/functionUndefined.gsc diff --git a/src/test/workspace/GscDiagnosticsCollection.UniversalGame/functionUndefinedFuncs.gsc b/src/test/workspace/GscAll.UniversalGame/functionUndefinedFuncs.gsc similarity index 100% rename from src/test/workspace/GscDiagnosticsCollection.UniversalGame/functionUndefinedFuncs.gsc rename to src/test/workspace/GscAll.UniversalGame/functionUndefinedFuncs.gsc diff --git a/src/test/workspace/GscDiagnosticsCollection.UniversalGame/invalidFile.gsc b/src/test/workspace/GscAll.UniversalGame/invalidFile.gsc similarity index 100% rename from src/test/workspace/GscDiagnosticsCollection.UniversalGame/invalidFile.gsc rename to src/test/workspace/GscAll.UniversalGame/invalidFile.gsc diff --git a/src/test/workspace/GscAll.UniversalGame/scripts/DuplicateInclude.gsc b/src/test/workspace/GscAll.UniversalGame/scripts/DuplicateInclude.gsc new file mode 100644 index 0000000..e0c8ad4 --- /dev/null +++ b/src/test/workspace/GscAll.UniversalGame/scripts/DuplicateInclude.gsc @@ -0,0 +1,23 @@ +#include scripts\file; // Duplicate #include file path +#include scripts\file; // Duplicate #include file path +#include scripts\file2; +#include scripts\file3; +#include scripts\file4; +#include scripts\file5; +#include scripts\file6; + +main() { + func1(); + func2(); +} + +func2() { + +} + +func5() { + +} + +duplicate() {} +duplicate() {} \ No newline at end of file diff --git a/src/test/workspace/GscDiagnosticsCollection.UniversalGame/scripts/ItselfInclude.gsc b/src/test/workspace/GscAll.UniversalGame/scripts/ItselfInclude.gsc similarity index 100% rename from src/test/workspace/GscDiagnosticsCollection.UniversalGame/scripts/ItselfInclude.gsc rename to src/test/workspace/GscAll.UniversalGame/scripts/ItselfInclude.gsc diff --git a/src/test/workspace/GscAll.UniversalGame/scripts/file.gsc b/src/test/workspace/GscAll.UniversalGame/scripts/file.gsc new file mode 100644 index 0000000..fa84d7d --- /dev/null +++ b/src/test/workspace/GscAll.UniversalGame/scripts/file.gsc @@ -0,0 +1,3 @@ +func1() { + +} \ No newline at end of file diff --git a/src/test/workspace/GscAll.UniversalGame/scripts/file2.gsc b/src/test/workspace/GscAll.UniversalGame/scripts/file2.gsc new file mode 100644 index 0000000..3970216 --- /dev/null +++ b/src/test/workspace/GscAll.UniversalGame/scripts/file2.gsc @@ -0,0 +1,3 @@ +func2() { + +} \ No newline at end of file diff --git a/src/test/workspace/GscAll.UniversalGame/scripts/file3.gsc b/src/test/workspace/GscAll.UniversalGame/scripts/file3.gsc new file mode 100644 index 0000000..f14fea9 --- /dev/null +++ b/src/test/workspace/GscAll.UniversalGame/scripts/file3.gsc @@ -0,0 +1,3 @@ +func3() { + +} \ No newline at end of file diff --git a/src/test/workspace/GscAll.UniversalGame/scripts/file4.gsc b/src/test/workspace/GscAll.UniversalGame/scripts/file4.gsc new file mode 100644 index 0000000..f14fea9 --- /dev/null +++ b/src/test/workspace/GscAll.UniversalGame/scripts/file4.gsc @@ -0,0 +1,3 @@ +func3() { + +} \ No newline at end of file diff --git a/src/test/workspace/GscAll.UniversalGame/scripts/file5.gsc b/src/test/workspace/GscAll.UniversalGame/scripts/file5.gsc new file mode 100644 index 0000000..397d368 --- /dev/null +++ b/src/test/workspace/GscAll.UniversalGame/scripts/file5.gsc @@ -0,0 +1,3 @@ +func5() { + +} \ No newline at end of file diff --git a/src/test/workspace/GscAll.UniversalGame/scripts/file6.gsc b/src/test/workspace/GscAll.UniversalGame/scripts/file6.gsc new file mode 100644 index 0000000..397d368 --- /dev/null +++ b/src/test/workspace/GscAll.UniversalGame/scripts/file6.gsc @@ -0,0 +1,3 @@ +func5() { + +} \ No newline at end of file diff --git a/src/test/workspace/GscComplex.test.ts b/src/test/workspace/GscAll.test.ts similarity index 59% rename from src/test/workspace/GscComplex.test.ts rename to src/test/workspace/GscAll.test.ts index 94dca04..d12356e 100644 --- a/src/test/workspace/GscComplex.test.ts +++ b/src/test/workspace/GscAll.test.ts @@ -1,9 +1,9 @@ import * as vscode from 'vscode'; import assert from 'assert'; -import { GscHoverProvider } from '../../GscHoverProvider'; -import { GscDefinitionProvider } from '../../GscDefinitionProvider'; import * as tests from '../Tests.test'; +import { GscHoverProvider } from '../../GscHoverProvider'; import { GscFunction } from '../../GscFunctions'; +import { GscDefinitionProvider } from '../../GscDefinitionProvider'; /* @@ -11,16 +11,17 @@ These tests depends on pre-created files in ./src/test/workspace These files are copied into temp folder (configured in .vscode-test.mjs) */ -suite('GscComplex', () => { +suite('GscAll', () => { setup(async () => { await tests.activateExtension(); }); + // Check case insensitivity of function calls (file paths, function names) - test('GscComplex.FunctionReferences -> error + hover + definition', async () => { + test('GscAll.FunctionReferences -> error + hover + definition', async () => { try { - const gsc = await tests.loadGscFile(['GscComplex', 'FunctionReferences.gsc']); + const gsc = await tests.loadGscFile(['GscAll', 'FunctionReferences.gsc']); // There should be no error - everything is case insensitive assert.ok(gsc.diagnostics.length === 0); @@ -28,43 +29,43 @@ suite('GscComplex', () => { // Correct path // FunctionReferencesFolder\FunctionReferencesFile::funcName(); const hover1 = await GscHoverProvider.getHover(gsc, new vscode.Position(5, 58)); - tests.checkHover(hover1, GscFunction.generateMarkdownDescription({name: "funcName", parameters: []}, false, tests.filePathToUri("GscComplex/FunctionReferencesFolder/FunctionReferencesFile.gsc").toString()).value); - //checkHover(hover1, "\n```\nfuncName()\n```\nFile: ```GscComplex/FunctionReferencesFolder/FunctionReferencesFile.gsc```"); + tests.checkHover(hover1, GscFunction.generateMarkdownDescription({name: "funcName", parameters: []}, false, tests.filePathToUri("GscAll/FunctionReferencesFolder/FunctionReferencesFile.gsc").toString()).value); + //checkHover(hover1, "\n```\nfuncName()\n```\nFile: ```GscAll/FunctionReferencesFolder/FunctionReferencesFile.gsc```"); const locations1 = await GscDefinitionProvider.getFunctionDefinitionLocations(gsc, new vscode.Position(5, 58)); - tests.checkDefinition(locations1, "GscComplex/FunctionReferencesFolder/FunctionReferencesFile.gsc"); + tests.checkDefinition(locations1, "GscAll/FunctionReferencesFolder/FunctionReferencesFile.gsc"); // Lowercase path // functionreferencesfolder\FunctionReferencesFile::funcName(); const hover2 = await GscHoverProvider.getHover(gsc, new vscode.Position(8, 58)); - tests.checkHover(hover2, GscFunction.generateMarkdownDescription({name: "funcName", parameters: []}, false, tests.filePathToUri("GscComplex/FunctionReferencesFolder/FunctionReferencesFile.gsc").toString()).value); + tests.checkHover(hover2, GscFunction.generateMarkdownDescription({name: "funcName", parameters: []}, false, tests.filePathToUri("GscAll/FunctionReferencesFolder/FunctionReferencesFile.gsc").toString()).value); const locations2 = await GscDefinitionProvider.getFunctionDefinitionLocations(gsc, new vscode.Position(8, 58)); - tests.checkDefinition(locations2, "GscComplex/FunctionReferencesFolder/FunctionReferencesFile.gsc"); + tests.checkDefinition(locations2, "GscAll/FunctionReferencesFolder/FunctionReferencesFile.gsc"); // Lowercase path + file // functionreferencesfolder\functionreferencesfile::funcName(); const hover3 = await GscHoverProvider.getHover(gsc, new vscode.Position(11, 58)); - tests.checkHover(hover3, GscFunction.generateMarkdownDescription({name: "funcName", parameters: []}, false, tests.filePathToUri("GscComplex/FunctionReferencesFolder/FunctionReferencesFile.gsc").toString()).value); + tests.checkHover(hover3, GscFunction.generateMarkdownDescription({name: "funcName", parameters: []}, false, tests.filePathToUri("GscAll/FunctionReferencesFolder/FunctionReferencesFile.gsc").toString()).value); const locations3 = await GscDefinitionProvider.getFunctionDefinitionLocations(gsc, new vscode.Position(11, 58)); - tests.checkDefinition(locations3, "GscComplex/FunctionReferencesFolder/FunctionReferencesFile.gsc"); + tests.checkDefinition(locations3, "GscAll/FunctionReferencesFolder/FunctionReferencesFile.gsc"); // Lowercase path + file + func name // functionreferencesfolder\functionreferencesfile::funcname(); const hover4 = await GscHoverProvider.getHover(gsc, new vscode.Position(14, 58)); - tests.checkHover(hover4, GscFunction.generateMarkdownDescription({name: "funcName", parameters: []}, false, tests.filePathToUri("GscComplex/FunctionReferencesFolder/FunctionReferencesFile.gsc").toString()).value); + tests.checkHover(hover4, GscFunction.generateMarkdownDescription({name: "funcName", parameters: []}, false, tests.filePathToUri("GscAll/FunctionReferencesFolder/FunctionReferencesFile.gsc").toString()).value); const locations4 = await GscDefinitionProvider.getFunctionDefinitionLocations(gsc, new vscode.Position(14, 58)); - tests.checkDefinition(locations4, "GscComplex/FunctionReferencesFolder/FunctionReferencesFile.gsc"); + tests.checkDefinition(locations4, "GscAll/FunctionReferencesFolder/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("GscComplex/FunctionReferencesFolder/FunctionReferencesFile.gsc").toString(), "Included via '#include'").value); + tests.checkHover(hover5, GscFunction.generateMarkdownDescription({name: "includedFuncName", parameters: []}, false, tests.filePathToUri("GscAll/FunctionReferencesFolder/FunctionReferencesFile.gsc").toString(), "Included via '#include'").value); const locations5 = await GscDefinitionProvider.getFunctionDefinitionLocations(gsc, new vscode.Position(17, 8)); - tests.checkDefinition(locations5, "GscComplex/FunctionReferencesFolder/FunctionReferencesFile.gsc"); + tests.checkDefinition(locations5, "GscAll/FunctionReferencesFolder/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)); - tests.checkDefinition(locations6, "GscComplex/FunctionReferences.gsc"); + tests.checkDefinition(locations6, "GscAll/FunctionReferences.gsc"); } catch (error) { tests.printDebugInfoForError(error); @@ -72,4 +73,22 @@ suite('GscComplex', () => { }); -}); + test('GscAll.SingleLine', async () => { + const gsc = await tests.loadGscFile(['GscAll', 'SingleLine.gsc']); + + tests.checkDiagnostic(gsc.diagnostics, 0, 'Structure field access should be on single line.', vscode.DiagnosticSeverity.Information); + tests.checkDiagnostic(gsc.diagnostics, 1, 'Function calls with object should be on single line.', vscode.DiagnosticSeverity.Information); + tests.checkDiagnostic(gsc.diagnostics, 2, 'Function calls with object should be on single line.', vscode.DiagnosticSeverity.Information); + assert.ok(gsc.diagnostics.length === 3); + }); + + + test('GscAll.DuplicatedFunction', async () => { + const gsc = await tests.loadGscFile(['GscAll', 'DuplicatedFunction.gsc']); + + tests.checkDiagnostic(gsc.diagnostics, 0, "Duplicate function definition of 'main'!", vscode.DiagnosticSeverity.Error); + tests.checkDiagnostic(gsc.diagnostics, 1, "Duplicate function definition of 'main'!", vscode.DiagnosticSeverity.Error); + assert.ok(gsc.diagnostics.length === 2); + }); + +}); \ No newline at end of file diff --git a/src/test/workspace/GscAll/DuplicatedFunction.gsc b/src/test/workspace/GscAll/DuplicatedFunction.gsc new file mode 100644 index 0000000..65cf0ea --- /dev/null +++ b/src/test/workspace/GscAll/DuplicatedFunction.gsc @@ -0,0 +1,7 @@ +main() { + +} + +main() { + +} \ No newline at end of file diff --git a/src/test/workspace/GscComplex/FunctionReferences.gsc b/src/test/workspace/GscAll/FunctionReferences.gsc similarity index 100% rename from src/test/workspace/GscComplex/FunctionReferences.gsc rename to src/test/workspace/GscAll/FunctionReferences.gsc diff --git a/src/test/workspace/GscComplex/FunctionReferencesFolder/FunctionReferencesFile.gsc b/src/test/workspace/GscAll/FunctionReferencesFolder/FunctionReferencesFile.gsc similarity index 100% rename from src/test/workspace/GscComplex/FunctionReferencesFolder/FunctionReferencesFile.gsc rename to src/test/workspace/GscAll/FunctionReferencesFolder/FunctionReferencesFile.gsc diff --git a/src/test/workspace/GscDiagnosticsCollection/SingleLine.gsc b/src/test/workspace/GscAll/SingleLine.gsc similarity index 100% rename from src/test/workspace/GscDiagnosticsCollection/SingleLine.gsc rename to src/test/workspace/GscAll/SingleLine.gsc diff --git a/src/test/workspace/GscAll_CoD2MP.test.ts b/src/test/workspace/GscAll_CoD2MP.test.ts new file mode 100644 index 0000000..a6e2242 --- /dev/null +++ b/src/test/workspace/GscAll_CoD2MP.test.ts @@ -0,0 +1,147 @@ +import * as vscode from 'vscode'; +import assert from 'assert'; +import * as tests from '../Tests.test'; +import { GscHoverProvider } from '../../GscHoverProvider'; +import { GscFunction } from '../../GscFunctions'; +import { GscDefinitionProvider } from '../../GscDefinitionProvider'; + + +/* +These tests depends on pre-created files in ./src/test/workspace +These files are copied into temp folder (configured in .vscode-test.mjs) +*/ + +suite('GscAll.CoD2MP', () => { + + setup(async () => { + await tests.activateExtension(); + }); + + test('GscAll.CoD2MP.casting', async () => { + const gsc = await tests.loadGscFile(['GscAll.CoD2MP', 'casting.gsc']); + + tests.checkDiagnostic(gsc.diagnostics, 0, "Casting to data type is not supported for CoD2 MP", vscode.DiagnosticSeverity.Error); + assert.ok(gsc.diagnostics.length === 1); + }); + + + test('GscAll.CoD2MP.functionOverwriting', async () => { + const gsc = await tests.loadGscFile(['GscAll.CoD2MP', 'functionOverwriting.gsc']); + + tests.checkDiagnostic(gsc.diagnostics, 0, "Function 'spawn' is overwriting build-in function", vscode.DiagnosticSeverity.Information); + tests.checkDiagnostic(gsc.diagnostics, 1, "Function 'hide' is overwriting build-in function", vscode.DiagnosticSeverity.Information); + assert.ok(gsc.diagnostics.length === 2); + }); + + + test('GscAll.CoD2MP.functionParameters', async () => { + const gsc = await tests.loadGscFile(['GscAll.CoD2MP', 'functionParameters.gsc']); + + tests.checkDiagnostic(gsc.diagnostics, 0, "Function 'spawn' expect min 2 parameters, got 0", vscode.DiagnosticSeverity.Error); + tests.checkDiagnostic(gsc.diagnostics, 1, "Function 'spawn' expect min 2 parameters, got 0", vscode.DiagnosticSeverity.Error); + tests.checkDiagnostic(gsc.diagnostics, 2, "Function 'spawn' expect min 2 parameters, got 0", vscode.DiagnosticSeverity.Error); + tests.checkDiagnostic(gsc.diagnostics, 3, "Function 'spawn' expect min 2 parameters, got 0", vscode.DiagnosticSeverity.Error); + tests.checkDiagnostic(gsc.diagnostics, 4, "Function 'aCos' can not be called on object (does not support callon object)", vscode.DiagnosticSeverity.Error); + tests.checkDiagnostic(gsc.diagnostics, 5, "Function 'getAmmoCount' must be called on object (callon object is missing)", vscode.DiagnosticSeverity.Error); + tests.checkDiagnostic(gsc.diagnostics, 6, "Function 'spawn' expect min 2 parameters, got 0", vscode.DiagnosticSeverity.Error); + tests.checkDiagnostic(gsc.diagnostics, 7, "Function 'spawn' expect min 2 parameters, got 1", vscode.DiagnosticSeverity.Error); + tests.checkDiagnostic(gsc.diagnostics, 8, "Function 'spawn' expect max 5 parameters, got 6", vscode.DiagnosticSeverity.Error); + tests.checkDiagnostic(gsc.diagnostics, 9, "Function 'spawn' expect max 5 parameters, got 7", vscode.DiagnosticSeverity.Error); + tests.checkDiagnostic(gsc.diagnostics, 10, "Function 'spawn' expect max 5 parameters, got 8", vscode.DiagnosticSeverity.Error); + assert.ok(gsc.diagnostics.length === 11); + }); + + + test('GscAll.CoD2MP.functionParametersLocal', async () => { + const gsc = await tests.loadGscFile(['GscAll.CoD2MP', 'functionParametersLocal.gsc']); + + tests.checkDiagnostic(gsc.diagnostics, 0, "Function 'p0' does not expect any parameters, got 1", vscode.DiagnosticSeverity.Error); + tests.checkDiagnostic(gsc.diagnostics, 1, "Function 'p0' does not expect any parameters, got 2", vscode.DiagnosticSeverity.Error); + tests.checkDiagnostic(gsc.diagnostics, 2, "Function 'p1' expect 1 parameter, got 2", vscode.DiagnosticSeverity.Error); + tests.checkDiagnostic(gsc.diagnostics, 3, "Function 'p2' expect 2 parameters, got 3", vscode.DiagnosticSeverity.Error); + tests.checkDiagnostic(gsc.diagnostics, 4, "Function 'p2' expect 2 parameters, got 5", vscode.DiagnosticSeverity.Error); + assert.ok(gsc.diagnostics.length === 5); + }); + + + test('GscAll.CoD2MP.functionUndefined', async () => { + const gsc = await tests.loadGscFile(['GscAll.CoD2MP', 'functionUndefined.gsc']); + + tests.checkDiagnostic(gsc.diagnostics, 0, "Function 'unknownLocalFunction' is not defined!", vscode.DiagnosticSeverity.Error); + tests.checkDiagnostic(gsc.diagnostics, 1, "Function 'func2' is not defined in 'functionUndefinedFuncs.gsc'!", vscode.DiagnosticSeverity.Error); + assert.ok(gsc.diagnostics.length === 2); + }); + + + test('GscAll.CoD2MP.invalidFile', async () => { + const gsc = await tests.loadGscFile(['GscAll.CoD2MP', 'invalidFile.gsc']); + + tests.checkDiagnostic(gsc.diagnostics, 0, "File 'unknown_path\\script.gsc' was not found in workspace folder 'GscAll.CoD2MP'", vscode.DiagnosticSeverity.Error); + assert.ok(gsc.diagnostics.length === 1); + }); + + + + test('GscAll.CoD2MP.ItselfInclude', async () => { + try { + const gsc = await tests.loadGscFile(['GscAll.CoD2MP', 'scripts', 'ItselfInclude.gsc']); + + tests.checkDiagnostic(gsc.diagnostics, 0, "File is including itself", vscode.DiagnosticSeverity.Error); + assert.strictEqual(gsc.diagnostics.length, 1); + + // Correct path + // FunctionReferencesFolder\FunctionReferencesFile::funcName(); + 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)); + tests.checkDefinition(locations1, "GscAll.CoD2MP/scripts/ItselfInclude.gsc"); + + + } catch (error) { + tests.printDebugInfoForError(error); + } + }); + + + + + + + + test('GscAll.CoD2MP.DuplicateInclude', async () => { + try { + const gsc = await tests.loadGscFile(['GscAll.CoD2MP', 'scripts', 'DuplicateInclude.gsc']); + + tests.checkDiagnostic(gsc.diagnostics, 0, "Duplicate #include file path", vscode.DiagnosticSeverity.Error); + tests.checkDiagnostic(gsc.diagnostics, 1, "Duplicate #include file path", vscode.DiagnosticSeverity.Error); + tests.checkDiagnostic(gsc.diagnostics, 2, "Function 'func2' is already defined in this file!", vscode.DiagnosticSeverity.Error); + tests.checkDiagnostic(gsc.diagnostics, 3, "Function 'func3' is already defined in included file 'GscAll.CoD2MP/scripts/file3.gsc'!", vscode.DiagnosticSeverity.Error); + tests.checkDiagnostic(gsc.diagnostics, 4, "Function 'func5' is already defined in this file!", vscode.DiagnosticSeverity.Error); + tests.checkDiagnostic(gsc.diagnostics, 5, "Function 'func5' is already defined in this file!", vscode.DiagnosticSeverity.Error); + tests.checkDiagnostic(gsc.diagnostics, 6, "Duplicate function definition of 'duplicate'!", vscode.DiagnosticSeverity.Error); + tests.checkDiagnostic(gsc.diagnostics, 7, "Duplicate function definition of 'duplicate'!", vscode.DiagnosticSeverity.Error); + assert.strictEqual(gsc.diagnostics.length, 8); + + var hover = await GscHoverProvider.getHover(gsc, new vscode.Position(10, 7)); + var md = GscFunction.generateMarkdownDescription({name: "func2", parameters: []}, true, undefined, undefined); + md.appendMarkdown('\n\r'); + md.appendMarkdown('-------------------------------------------------------------------------- \n\r'); + md.appendMarkdown(GscFunction.generateMarkdownDescription({name: "func2", parameters: []}, false, tests.filePathToUri("GscAll.CoD2MP/scripts/file2.gsc").toString(), "Included via '#include'").value); + tests.checkHover(hover, md.value); + + var hover = await GscHoverProvider.getHover(gsc, new vscode.Position(17, 3)); + var md = GscFunction.generateMarkdownDescription({name: "func5", parameters: []}, true, undefined, undefined); + md.appendMarkdown('\n\r'); + md.appendMarkdown('-------------------------------------------------------------------------- \n\r'); + md.appendMarkdown(GscFunction.generateMarkdownDescription({name: "func5", parameters: []}, false, tests.filePathToUri("GscAll.CoD2MP/scripts/file5.gsc").toString(), "Included via '#include'").value); + md.appendMarkdown('\n\r'); + md.appendMarkdown('-------------------------------------------------------------------------- \n\r'); + md.appendMarkdown(GscFunction.generateMarkdownDescription({name: "func5", parameters: []}, false, tests.filePathToUri("GscAll.CoD2MP/scripts/file6.gsc").toString(), "Included via '#include'").value); + tests.checkHover(hover, md.value); + + } catch (error) { + tests.printDebugInfoForError(error); + } + }); +}); diff --git a/src/test/workspace/GscAll_UniversalGame.test.ts b/src/test/workspace/GscAll_UniversalGame.test.ts new file mode 100644 index 0000000..ddbe5b7 --- /dev/null +++ b/src/test/workspace/GscAll_UniversalGame.test.ts @@ -0,0 +1,123 @@ +import * as vscode from 'vscode'; +import assert from 'assert'; +import * as tests from '../Tests.test'; +import { GscHoverProvider } from '../../GscHoverProvider'; +import { GscFunction } from '../../GscFunctions'; +import { GscDefinitionProvider } from '../../GscDefinitionProvider'; + + +/* +These tests depends on pre-created files in ./src/test/workspace +These files are copied into temp folder (configured in .vscode-test.mjs) +*/ + +suite('GscAll.UniversalGame', () => { + + setup(async () => { + await tests.activateExtension(); + }); + + test('GscAll.UniversalGame.casting', async () => { + const gsc = await tests.loadGscFile(['GscAll.UniversalGame', 'casting.gsc']); + + assert.ok(gsc.diagnostics.length === 0); + }); + + test('GscAll.UniversalGame.functionOverwriting', async () => { + const gsc = await tests.loadGscFile(['GscAll.UniversalGame', 'functionOverwriting.gsc']); + + assert.ok(gsc.diagnostics.length === 0); + }); + + + test('GscAll.UniversalGame.functionParameters', async () => { + const gsc = await tests.loadGscFile(['GscAll.UniversalGame', 'functionParameters.gsc']); + + assert.ok(gsc.diagnostics.length === 0); + }); + + + test('GscAll.CoD2MP.functionParametersLocal', async () => { + const gsc = await tests.loadGscFile(['GscAll.UniversalGame', 'functionParametersLocal.gsc']); + + tests.checkDiagnostic(gsc.diagnostics, 0, "Function 'p0' does not expect any parameters, got 1", vscode.DiagnosticSeverity.Error); + tests.checkDiagnostic(gsc.diagnostics, 1, "Function 'p0' does not expect any parameters, got 2", vscode.DiagnosticSeverity.Error); + tests.checkDiagnostic(gsc.diagnostics, 2, "Function 'p1' expect 1 parameter, got 2", vscode.DiagnosticSeverity.Error); + tests.checkDiagnostic(gsc.diagnostics, 3, "Function 'p2' expect 2 parameters, got 3", vscode.DiagnosticSeverity.Error); + tests.checkDiagnostic(gsc.diagnostics, 4, "Function 'p2' expect 2 parameters, got 5", vscode.DiagnosticSeverity.Error); + assert.ok(gsc.diagnostics.length === 5); + }); + + + test('GscAll.UniversalGame.functionUndefined', async () => { + const gsc = await tests.loadGscFile(['GscAll.UniversalGame', 'functionUndefined.gsc']); + + tests.checkDiagnostic(gsc.diagnostics, 0, "Function 'func2' is not defined in 'functionUndefinedFuncs.gsc'!", vscode.DiagnosticSeverity.Error); + assert.ok(gsc.diagnostics.length === 1); + }); + + + test('GscAll.UniversalGame.invalidFile', async () => { + const gsc = await tests.loadGscFile(['GscAll.UniversalGame', 'invalidFile.gsc']); + + tests.checkDiagnostic(gsc.diagnostics, 0, "File 'unknown_path\\script.gsc' was not found in workspace folder 'GscAll.UniversalGame'", vscode.DiagnosticSeverity.Error); + assert.ok(gsc.diagnostics.length === 1); + }); + + test('GscAll.UniversalGame.ItselfInclude', async () => { + try { + const gsc = await tests.loadGscFile(['GscAll.UniversalGame', 'scripts', 'ItselfInclude.gsc']); + + assert.strictEqual(gsc.diagnostics.length, 0); + + // Correct path + // FunctionReferencesFolder\FunctionReferencesFile::funcName(); + 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)); + tests.checkDefinition(locations1, "GscAll.UniversalGame/scripts/ItselfInclude.gsc"); + + + } catch (error) { + tests.printDebugInfoForError(error); + } + }); + + + + + + + test('GscAll.UniversalGame.DuplicateInclude', async () => { + try { + const gsc = await tests.loadGscFile(['GscAll.UniversalGame', 'scripts', 'DuplicateInclude.gsc']); + + tests.checkDiagnostic(gsc.diagnostics, 0, "Duplicate #include file path", vscode.DiagnosticSeverity.Error); + tests.checkDiagnostic(gsc.diagnostics, 1, "Duplicate #include file path", vscode.DiagnosticSeverity.Error); + tests.checkDiagnostic(gsc.diagnostics, 2, "Duplicate function definition of 'duplicate'!", vscode.DiagnosticSeverity.Error); + tests.checkDiagnostic(gsc.diagnostics, 3, "Duplicate function definition of 'duplicate'!", vscode.DiagnosticSeverity.Error); + assert.strictEqual(gsc.diagnostics.length, 4); + + var hover1 = await GscHoverProvider.getHover(gsc, new vscode.Position(10, 7)); + var md = GscFunction.generateMarkdownDescription({name: "func2", parameters: []}, true, undefined, undefined); + md.appendMarkdown('\n\r'); + md.appendMarkdown('-------------------------------------------------------------------------- \n\r'); + md.appendMarkdown(`Function 'func2' is also defined in this file: \n\r`); + md.appendMarkdown(`\n- GscAll.UniversalGame/scripts/file2.gsc`); + tests.checkHover(hover1, md.value); + + var hover = await GscHoverProvider.getHover(gsc, new vscode.Position(17, 3)); + var md = GscFunction.generateMarkdownDescription({name: "func5", parameters: []}, true, undefined, undefined); + md.appendMarkdown('\n\r'); + md.appendMarkdown('-------------------------------------------------------------------------- \n\r'); + md.appendMarkdown(`Function 'func5' is also defined in these files: \n\r`); + md.appendMarkdown(`\n- GscAll.UniversalGame/scripts/file5.gsc`); + md.appendMarkdown(`\n- GscAll.UniversalGame/scripts/file6.gsc`); + tests.checkHover(hover, md.value); + + } catch (error) { + tests.printDebugInfoForError(error); + } + }); +}); diff --git a/src/test/workspace/GscDiagnosticsCollection.test.ts b/src/test/workspace/GscDiagnosticsCollection.test.ts deleted file mode 100644 index 4c02e87..0000000 --- a/src/test/workspace/GscDiagnosticsCollection.test.ts +++ /dev/null @@ -1,236 +0,0 @@ -import * as vscode from 'vscode'; -import assert from 'assert'; -import * as tests from '../Tests.test'; -import { GscHoverProvider } from '../../GscHoverProvider'; -import { GscFunction } from '../../GscFunctions'; -import { GscDefinitionProvider } from '../../GscDefinitionProvider'; - - -/* -These tests depends on pre-created files in ./src/test/workspace -These files are copied into temp folder (configured in .vscode-test.mjs) -*/ - - - -suite('GscDiagnosticsCollection', () => { - - setup(async () => { - await tests.activateExtension(); - }); - - test('GscDiagnosticsCollection.SingleLine', async () => { - const gsc = await tests.loadGscFile(['GscDiagnosticsCollection', 'SingleLine.gsc']); - - tests.checkDiagnostic(gsc.diagnostics, 0, 'Structure field access should be on single line.', vscode.DiagnosticSeverity.Information); - tests.checkDiagnostic(gsc.diagnostics, 1, 'Function calls with object should be on single line.', vscode.DiagnosticSeverity.Information); - tests.checkDiagnostic(gsc.diagnostics, 2, 'Function calls with object should be on single line.', vscode.DiagnosticSeverity.Information); - assert.ok(gsc.diagnostics.length === 3); - }); - - - - test('GscDiagnosticsCollection.DuplicateInclude', async () => { - try { - const gsc = await tests.loadGscFile(['GscDiagnosticsCollection', 'scripts', 'DuplicateInclude.gsc']); - - tests.checkDiagnostic(gsc.diagnostics, 0, "Duplicate #include file path", vscode.DiagnosticSeverity.Error); - tests.checkDiagnostic(gsc.diagnostics, 1, "Duplicate #include file path", vscode.DiagnosticSeverity.Error); - assert.strictEqual(gsc.diagnostics.length, 2); - - } catch (error) { - tests.printDebugInfoForError(error); - } - }); - - - test('GscDiagnosticsCollection.DuplicateFunctionViaInclude', async () => { - try { - const gsc = await tests.loadGscFile(['GscDiagnosticsCollection', 'scripts', 'DuplicateFunctionViaInclude.gsc']); - - tests.checkDiagnostic(gsc.diagnostics, 0, "Function 'func2' is defined in 2 places!", vscode.DiagnosticSeverity.Error); - assert.strictEqual(gsc.diagnostics.length, 1); - - } catch (error) { - tests.printDebugInfoForError(error); - } - }); - -}); - - - - - - -suite('GscDiagnosticsCollection.CoD2MP', () => { - - setup(async () => { - await tests.activateExtension(); - }); - - test('GscDiagnosticsCollection.CoD2MP.casting', async () => { - const gsc = await tests.loadGscFile(['GscDiagnosticsCollection.CoD2MP', 'casting.gsc']); - - tests.checkDiagnostic(gsc.diagnostics, 0, "Casting to data type is not supported for CoD2 MP", vscode.DiagnosticSeverity.Error); - assert.ok(gsc.diagnostics.length === 1); - }); - - - test('GscDiagnosticsCollection.CoD2MP.functionOverwriting', async () => { - const gsc = await tests.loadGscFile(['GscDiagnosticsCollection.CoD2MP', 'functionOverwriting.gsc']); - - tests.checkDiagnostic(gsc.diagnostics, 0, "Function 'spawn' is overwriting build-in function", vscode.DiagnosticSeverity.Information); - tests.checkDiagnostic(gsc.diagnostics, 1, "Function 'hide' is overwriting build-in function", vscode.DiagnosticSeverity.Information); - assert.ok(gsc.diagnostics.length === 2); - }); - - - test('GscDiagnosticsCollection.CoD2MP.functionParameters', async () => { - const gsc = await tests.loadGscFile(['GscDiagnosticsCollection.CoD2MP', 'functionParameters.gsc']); - - tests.checkDiagnostic(gsc.diagnostics, 0, "Function 'spawn' expect min 2 parameters, got 0", vscode.DiagnosticSeverity.Error); - tests.checkDiagnostic(gsc.diagnostics, 1, "Function 'spawn' expect min 2 parameters, got 0", vscode.DiagnosticSeverity.Error); - tests.checkDiagnostic(gsc.diagnostics, 2, "Function 'spawn' expect min 2 parameters, got 0", vscode.DiagnosticSeverity.Error); - tests.checkDiagnostic(gsc.diagnostics, 3, "Function 'spawn' expect min 2 parameters, got 0", vscode.DiagnosticSeverity.Error); - tests.checkDiagnostic(gsc.diagnostics, 4, "Function 'aCos' can not be called on object (does not support callon object)", vscode.DiagnosticSeverity.Error); - tests.checkDiagnostic(gsc.diagnostics, 5, "Function 'getAmmoCount' must be called on object (callon object is missing)", vscode.DiagnosticSeverity.Error); - tests.checkDiagnostic(gsc.diagnostics, 6, "Function 'spawn' expect min 2 parameters, got 0", vscode.DiagnosticSeverity.Error); - tests.checkDiagnostic(gsc.diagnostics, 7, "Function 'spawn' expect min 2 parameters, got 1", vscode.DiagnosticSeverity.Error); - tests.checkDiagnostic(gsc.diagnostics, 8, "Function 'spawn' expect max 5 parameters, got 6", vscode.DiagnosticSeverity.Error); - tests.checkDiagnostic(gsc.diagnostics, 9, "Function 'spawn' expect max 5 parameters, got 7", vscode.DiagnosticSeverity.Error); - tests.checkDiagnostic(gsc.diagnostics, 10, "Function 'spawn' expect max 5 parameters, got 8", vscode.DiagnosticSeverity.Error); - assert.ok(gsc.diagnostics.length === 11); - }); - - - test('GscDiagnosticsCollection.CoD2MP.functionParametersLocal', async () => { - const gsc = await tests.loadGscFile(['GscDiagnosticsCollection.CoD2MP', 'functionParametersLocal.gsc']); - - tests.checkDiagnostic(gsc.diagnostics, 0, "Function 'p0' does not expect any parameters, got 1", vscode.DiagnosticSeverity.Error); - tests.checkDiagnostic(gsc.diagnostics, 1, "Function 'p0' does not expect any parameters, got 2", vscode.DiagnosticSeverity.Error); - tests.checkDiagnostic(gsc.diagnostics, 2, "Function 'p1' expect 1 parameter, got 2", vscode.DiagnosticSeverity.Error); - tests.checkDiagnostic(gsc.diagnostics, 3, "Function 'p2' expect 2 parameters, got 3", vscode.DiagnosticSeverity.Error); - tests.checkDiagnostic(gsc.diagnostics, 4, "Function 'p2' expect 2 parameters, got 5", vscode.DiagnosticSeverity.Error); - assert.ok(gsc.diagnostics.length === 5); - }); - - - test('GscDiagnosticsCollection.CoD2MP.functionUndefined', async () => { - const gsc = await tests.loadGscFile(['GscDiagnosticsCollection.CoD2MP', 'functionUndefined.gsc']); - - tests.checkDiagnostic(gsc.diagnostics, 0, "Function 'unknownLocalFunction' is not defined!", vscode.DiagnosticSeverity.Error); - tests.checkDiagnostic(gsc.diagnostics, 1, "Function 'func2' is not defined in 'functionUndefinedFuncs.gsc'!", vscode.DiagnosticSeverity.Error); - assert.ok(gsc.diagnostics.length === 2); - }); - - - test('GscDiagnosticsCollection.CoD2MP.invalidFile', async () => { - const gsc = await tests.loadGscFile(['GscDiagnosticsCollection.CoD2MP', 'invalidFile.gsc']); - - tests.checkDiagnostic(gsc.diagnostics, 0, "File 'unknown_path\\script.gsc' was not found in workspace folder 'GscDiagnosticsCollection.CoD2MP'", vscode.DiagnosticSeverity.Error); - assert.ok(gsc.diagnostics.length === 1); - }); - - - - test('GscDiagnosticsCollection.CoD2MP.ItselfInclude', async () => { - try { - const gsc = await tests.loadGscFile(['GscDiagnosticsCollection.CoD2MP', 'scripts', 'ItselfInclude.gsc']); - - tests.checkDiagnostic(gsc.diagnostics, 0, "File is including itself", vscode.DiagnosticSeverity.Error); - assert.strictEqual(gsc.diagnostics.length, 1); - - // Correct path - // FunctionReferencesFolder\FunctionReferencesFile::funcName(); - const hover1 = await GscHoverProvider.getHover(gsc, new vscode.Position(3, 6)); - tests.checkHover(hover1, GscFunction.generateMarkdownDescription({name: "func1", parameters: []}, true, tests.filePathToUri("GscDiagnosticsCollection.CoD2MP/scripts/ItselfInclude.gsc").toString()).value); - - const locations1 = await GscDefinitionProvider.getFunctionDefinitionLocations(gsc, new vscode.Position(3, 6)); - tests.checkDefinition(locations1, "GscDiagnosticsCollection.CoD2MP/scripts/ItselfInclude.gsc"); - - - } catch (error) { - tests.printDebugInfoForError(error); - } - }); -}); - - - - - - -suite('GscDiagnosticsCollection.UniversalGame', () => { - - setup(async () => { - await tests.activateExtension(); - }); - - test('GscDiagnosticsCollection.UniversalGame.casting', async () => { - const gsc = await tests.loadGscFile(['GscDiagnosticsCollection.UniversalGame', 'casting.gsc']); - - assert.ok(gsc.diagnostics.length === 0); - }); - - test('GscDiagnosticsCollection.UniversalGame.functionOverwriting', async () => { - const gsc = await tests.loadGscFile(['GscDiagnosticsCollection.UniversalGame', 'functionOverwriting.gsc']); - - assert.ok(gsc.diagnostics.length === 0); - }); - - - test('GscDiagnosticsCollection.UniversalGame.functionParameters', async () => { - const gsc = await tests.loadGscFile(['GscDiagnosticsCollection.UniversalGame', 'functionParameters.gsc']); - - assert.ok(gsc.diagnostics.length === 0); - }); - - - test('GscDiagnosticsCollection.CoD2MP.functionParametersLocal', async () => { - const gsc = await tests.loadGscFile(['GscDiagnosticsCollection.UniversalGame', 'functionParametersLocal.gsc']); - - tests.checkDiagnostic(gsc.diagnostics, 0, "Function 'p0' does not expect any parameters, got 1", vscode.DiagnosticSeverity.Error); - tests.checkDiagnostic(gsc.diagnostics, 1, "Function 'p0' does not expect any parameters, got 2", vscode.DiagnosticSeverity.Error); - tests.checkDiagnostic(gsc.diagnostics, 2, "Function 'p1' expect 1 parameter, got 2", vscode.DiagnosticSeverity.Error); - tests.checkDiagnostic(gsc.diagnostics, 3, "Function 'p2' expect 2 parameters, got 3", vscode.DiagnosticSeverity.Error); - tests.checkDiagnostic(gsc.diagnostics, 4, "Function 'p2' expect 2 parameters, got 5", vscode.DiagnosticSeverity.Error); - assert.ok(gsc.diagnostics.length === 5); - }); - - - test('GscDiagnosticsCollection.UniversalGame.functionUndefined', async () => { - const gsc = await tests.loadGscFile(['GscDiagnosticsCollection.UniversalGame', 'functionUndefined.gsc']); - - tests.checkDiagnostic(gsc.diagnostics, 0, "Function 'func2' is not defined in 'functionUndefinedFuncs.gsc'!", vscode.DiagnosticSeverity.Error); - assert.ok(gsc.diagnostics.length === 1); - }); - - - test('GscDiagnosticsCollection.UniversalGame.invalidFile', async () => { - const gsc = await tests.loadGscFile(['GscDiagnosticsCollection.UniversalGame', 'invalidFile.gsc']); - - tests.checkDiagnostic(gsc.diagnostics, 0, "File 'unknown_path\\script.gsc' was not found in workspace folder 'GscDiagnosticsCollection.UniversalGame'", vscode.DiagnosticSeverity.Error); - assert.ok(gsc.diagnostics.length === 1); - }); - - test('GscDiagnosticsCollection.UniversalGame.ItselfInclude', async () => { - try { - const gsc = await tests.loadGscFile(['GscDiagnosticsCollection.UniversalGame', 'scripts', 'ItselfInclude.gsc']); - - assert.strictEqual(gsc.diagnostics.length, 0); - - // Correct path - // FunctionReferencesFolder\FunctionReferencesFile::funcName(); - const hover1 = await GscHoverProvider.getHover(gsc, new vscode.Position(3, 6)); - tests.checkHover(hover1, GscFunction.generateMarkdownDescription({name: "func1", parameters: []}, true, tests.filePathToUri("GscDiagnosticsCollection.UniversalGame/scripts/ItselfInclude.gsc").toString()).value); - - const locations1 = await GscDefinitionProvider.getFunctionDefinitionLocations(gsc, new vscode.Position(3, 6)); - tests.checkDefinition(locations1, "GscDiagnosticsCollection.UniversalGame/scripts/ItselfInclude.gsc"); - - - } catch (error) { - tests.printDebugInfoForError(error); - } - }); -}); diff --git a/src/test/workspace/GscDiagnosticsCollection/scripts/DuplicateFunctionViaInclude.gsc b/src/test/workspace/GscDiagnosticsCollection/scripts/DuplicateFunctionViaInclude.gsc deleted file mode 100644 index 38ee091..0000000 --- a/src/test/workspace/GscDiagnosticsCollection/scripts/DuplicateFunctionViaInclude.gsc +++ /dev/null @@ -1,11 +0,0 @@ -#include scripts\file; -#include scripts\file2; - -main() { - func1(); - func2(); -} - -func2() { - -} \ No newline at end of file diff --git a/src/test/workspace/GscDiagnosticsCollection/scripts/DuplicateInclude.gsc b/src/test/workspace/GscDiagnosticsCollection/scripts/DuplicateInclude.gsc deleted file mode 100644 index 6262b11..0000000 --- a/src/test/workspace/GscDiagnosticsCollection/scripts/DuplicateInclude.gsc +++ /dev/null @@ -1,6 +0,0 @@ -#include scripts\file; -#include scripts\file; - -main() { - func1(); -} \ No newline at end of file diff --git a/src/test/workspace/vscode-cod-gsc-tests.code-workspace b/src/test/workspace/vscode-cod-gsc-tests.code-workspace index d2da1a9..2b2ddce 100644 --- a/src/test/workspace/vscode-cod-gsc-tests.code-workspace +++ b/src/test/workspace/vscode-cod-gsc-tests.code-workspace @@ -1,20 +1,16 @@ { "folders": [ { - "name": "GscComplex", - "path": "GscComplex" + "name": "GscAll", + "path": "GscAll" }, { - "name": "GscDiagnosticsCollection", - "path": "GscDiagnosticsCollection" + "name": "GscAll.CoD2MP", + "path": "GscAll.CoD2MP" }, { - "name": "GscDiagnosticsCollection.CoD2MP", - "path": "GscDiagnosticsCollection.CoD2MP" - }, - { - "name": "GscDiagnosticsCollection.UniversalGame", - "path": "GscDiagnosticsCollection.UniversalGame" + "name": "GscAll.UniversalGame", + "path": "GscAll.UniversalGame" }, { "name": "GscQuickFix",