Skip to content

Commit

Permalink
Improved GSC file references when two or more workspace folders are r…
Browse files Browse the repository at this point in the history
…eferenced for each other. When there are 2 workspaces where in each there is one file with the same "game" path (e.q. maps\mp\gametypes\script.gsc), only the last file is used (according to the workspace folder order in explorer). It applies for function and variable definitions in hover provider, auto-completion etc.
  • Loading branch information
eyza-cod2 committed Oct 25, 2024
1 parent ac0f845 commit 1834293
Show file tree
Hide file tree
Showing 27 changed files with 613 additions and 161 deletions.
9 changes: 4 additions & 5 deletions src/GscCompletionItemProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export class GscCompletionItemProvider implements vscode.CompletionItemProvider

const currentGame = gscFile.config.currentGame;

const items = await GscCompletionItemProvider.getCompletionItems(gscFile, position, currentGame, undefined, document.uri);
const items = await GscCompletionItemProvider.getCompletionItems(gscFile, position, currentGame, undefined);

return items;
} catch (error) {
Expand All @@ -57,8 +57,7 @@ export class GscCompletionItemProvider implements vscode.CompletionItemProvider
gscFile: GscFile,
position: vscode.Position,
currentGame: GscGame,
config?: CompletionConfig,
uri?: vscode.Uri
config?: CompletionConfig
): Promise<vscode.CompletionItem[]>
{
const completionItems: vscode.CompletionItem[] = [];
Expand Down Expand Up @@ -115,7 +114,7 @@ export class GscCompletionItemProvider implements vscode.CompletionItemProvider

// Add items for variables like level.aaa, game["bbb"] and local1.aaa[0][1]
if (!config || config.variableItems) {
this.createVariableItems(completionItems, functionGroup.localVariableDefinitions, variableBeforeCursor, inWord, inStructureVariable, inArrayBrackets, uri);
this.createVariableItems(completionItems, functionGroup.localVariableDefinitions, variableBeforeCursor, inWord, inStructureVariable, inArrayBrackets, gscFile.uri);
}
// Add items for predefined keywords (like true, false, undefined, if, else, waittillframeend, ...)
if (!config || config.keywordItems) {
Expand Down Expand Up @@ -182,7 +181,7 @@ export class GscCompletionItemProvider implements vscode.CompletionItemProvider
const workspaceFolder = vscode.workspace.getWorkspaceFolder(uri);

if (workspaceFolder !== undefined) {
const gscFiles = GscFiles.getReferenceableCachedFiles(workspaceFolder);
const gscFiles = GscFiles.getReferenceableCachedFiles(workspaceFolder.uri, false);

// Level variables
if (variableBeforeCursor.startsWith("level")) {
Expand Down
3 changes: 0 additions & 3 deletions src/GscDiagnosticsCollection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,6 @@ export class GscDiagnosticsCollection {
}
}

// TODO check where this file is referenced to update particular files
// It will be crusual for #include files

this.diagnosticCollection?.set(uri, gscFile.diagnostics);

// ------------------------------------------------------------------------------------------------------------------------------------------
Expand Down
33 changes: 14 additions & 19 deletions src/GscFile.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,13 @@
import * as vscode from 'vscode';
import { GscData } from "./GscFileParser";
import { ConfigErrorDiagnostics, GscConfig, GscGame, GscGameConfig, GscGameRootFolder } from './GscConfig';
import { GscWorkspaceFileData } from './GscFileCache';
import { ConfigErrorDiagnostics, GscConfig, GscGame } from './GscConfig';
import { GscFilesConfig, GscWorkspaceFileData } from './GscFileCache';
import { GscFiles } from './GscFiles';

/**
* This type holds workspace configuration for current GSC file to easily access configuration without actually reading it from config file.
* When configuration is changed, it is updated in all GscFile instances.
*/
export type GscFileConfig = {
/** All possible game root folders where GSC files can be found and referenced. */
referenceableGameRootFolders: GscGameRootFolder[];
/** Ignored function names */
ignoredFunctionNames: string[];
/** Ignored file paths */
ignoredFilePaths: string[];
/** Currently selected game */
currentGame: GscGame;
/** Mode of diagnostics collection */
errorDiagnostics: ConfigErrorDiagnostics;
/** Syntax configuration of the selected game */
gameConfig: GscGameConfig;
};



export class GscFile {
Expand All @@ -32,8 +18,11 @@ export class GscFile {
/** URI of the file */
uri: vscode.Uri;

/** Path of this file from game perspective. For example: maps\mp\gametypes\script.gsc */
gamePath: string;

/** Configuration related to this file */
config: GscFileConfig;
config: GscFilesConfig;

/** Diagnostics generated for this file. @see GscFileDiagnostics.ts */
diagnostics: vscode.Diagnostic[] = [];
Expand All @@ -58,17 +47,23 @@ export class GscFile {
this.uri = uri;

if (workspaceFolder !== undefined) {
this.config = GscWorkspaceFileData.getConfig(workspaceFolder);
this.config = GscWorkspaceFileData.loadConfig(workspaceFolder);
} else {
this.config = {
referenceableGameRootFolders: [],
referenceableGameRootFoldersAll: [],
referenceableWorkspaceFolders: [],
referenceableWorkspaceFoldersAll: [],
rootFolder: undefined,
currentGame: GscGame.UniversalGame,
ignoredFunctionNames: [],
ignoredFilePaths: [],
errorDiagnostics: ConfigErrorDiagnostics.Enable,
gameConfig: GscConfig.gamesConfigs.get(GscGame.UniversalGame)!
};
}

this.gamePath = GscFiles.getGamePathFromGscFile(this);
}

updateData(data: GscData, version: number) {
Expand Down
82 changes: 59 additions & 23 deletions src/GscFileCache.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,50 @@
import * as vscode from 'vscode';
import { GscFile, GscFileConfig } from './GscFile';
import { GscFile } from './GscFile';
import { GscFiles } from './GscFiles';
import { GscConfig } from './GscConfig';
import { ConfigErrorDiagnostics, GscConfig, GscGame, GscGameConfig, GscGameRootFolder } from './GscConfig';
import { Events } from './Events';
import { LoggerOutput } from './LoggerOutput';



// Configuration of workspace that applies to all files within the workspace
export type GscFilesConfig = {
/** All possible game root folders where GSC files can be found and referenced. */
referenceableGameRootFolders: GscGameRootFolder[];
/** All possible game root folders where GSC files can be found and referenced, including reverse references */
referenceableGameRootFoldersAll: GscGameRootFolder[];

/** All possible workspace folders where GSC files can be found and referenced */
referenceableWorkspaceFolders: vscode.WorkspaceFolder[];
/** All possible workspace folders where GSC files can be found and referenced, including reverse references */
referenceableWorkspaceFoldersAll: vscode.WorkspaceFolder[];

/** Game root folder */
rootFolder: GscGameRootFolder | undefined;
/** Ignored function names */
ignoredFunctionNames: string[];
/** Ignored file paths */
ignoredFilePaths: string[];
/** Currently selected game */
currentGame: GscGame;
/** Mode of diagnostics collection */
errorDiagnostics: ConfigErrorDiagnostics;
/** Syntax configuration of the selected game */
gameConfig: GscGameConfig;
};


export class GscCachedFilesPerWorkspace {

private cachedFilesPerWorkspace: Map<string, GscWorkspaceFileData> = new Map();

createNewWorkspaceFileData(workspaceFolder: vscode.WorkspaceFolder): GscWorkspaceFileData {
createNewWorkspace(workspaceFolder: vscode.WorkspaceFolder): GscWorkspaceFileData {
const data = new GscWorkspaceFileData(workspaceFolder);
this.cachedFilesPerWorkspace.set(workspaceFolder.uri.toString(), data);
return data;
}

getWorkspaceFileData(workspaceUri: vscode.Uri): GscWorkspaceFileData | undefined {
getWorkspace(workspaceUri: vscode.Uri): GscWorkspaceFileData | undefined {
return this.cachedFilesPerWorkspace.get(workspaceUri.toString());
}

Expand All @@ -28,15 +54,15 @@ export class GscCachedFilesPerWorkspace {
if (workspaceFolder === undefined) {
return;
}
let dataOfWorkspace = this.getWorkspaceFileData(workspaceFolder.uri);
let dataOfWorkspace = this.getWorkspace(workspaceFolder.uri);
if (dataOfWorkspace === undefined) {
return;
}
dataOfWorkspace.removeParsedFile(fileUri);
}

removeWorkspaceFiles(workspaceUri: vscode.Uri) {
const workspaceData = this.getWorkspaceFileData(workspaceUri);
removeWorkspace(workspaceUri: vscode.Uri) {
const workspaceData = this.getWorkspace(workspaceUri);
if (workspaceData === undefined) {
return false;
}
Expand All @@ -60,9 +86,13 @@ export class GscCachedFilesPerWorkspace {
export class GscWorkspaceFileData {
private parsedFiles: Map<string, GscFile> = new Map();

public config: GscFilesConfig;

constructor(
public workspaceFolder: vscode.WorkspaceFolder
) {}
) {
this.config = GscWorkspaceFileData.loadConfig(this.workspaceFolder);
}

addParsedFile(gscFile: GscFile) {
if (!this.parsedFiles.has(gscFile.id)) {
Expand Down Expand Up @@ -107,29 +137,35 @@ export class GscWorkspaceFileData {
}

updateConfiguration() {
const data = GscWorkspaceFileData.getConfig(this.workspaceFolder);
this.config = GscWorkspaceFileData.loadConfig(this.workspaceFolder);

// Loop all GscFile and update their configuration
for (const file of this.parsedFiles.values()) {
file.config.referenceableGameRootFolders = data.referenceableGameRootFolders;
file.config.currentGame = data.currentGame;
file.config.ignoredFunctionNames = data.ignoredFunctionNames;
file.config.ignoredFilePaths = data.ignoredFilePaths;
file.config.errorDiagnostics = data.errorDiagnostics;
file.config.gameConfig = data.gameConfig;
file.config = this.config;

file.gamePath = GscFiles.getGamePathFromGscFile(file);
}
}

static getConfig(workspaceFolder: vscode.WorkspaceFolder): GscFileConfig {

// Get config for workspace folder
const referenceableGameRootFolders = GscFiles.getReferenceableGameRootFolders(workspaceFolder);

/**
* Loads current settings for the workspace folder and returns them as GscFilesConfig object.
*/
public static loadConfig(workspaceFolder: vscode.WorkspaceFolder): GscFilesConfig {
const currentGame = GscConfig.getSelectedGame(workspaceFolder.uri);
const ignoredFunctionNames = GscConfig.getIgnoredFunctionNames(workspaceFolder.uri);
const ignoredFilePaths = GscConfig.getIgnoredFilePaths(workspaceFolder.uri);
const errorDiagnostics = GscConfig.getErrorDiagnostics(workspaceFolder.uri);
const gameConfig = GscConfig.gamesConfigs.get(currentGame)!;

return {referenceableGameRootFolders, currentGame, ignoredFunctionNames, ignoredFilePaths, errorDiagnostics, gameConfig};
return {
referenceableGameRootFolders: GscFiles.loadReferenceableGameRootFolders(workspaceFolder, false),
referenceableGameRootFoldersAll: GscFiles.loadReferenceableGameRootFolders(workspaceFolder, true),
referenceableWorkspaceFolders: GscFiles.loadReferenceableWorkspaceFolders(workspaceFolder, false),
referenceableWorkspaceFoldersAll: GscFiles.loadReferenceableWorkspaceFolders(workspaceFolder, true),
rootFolder: GscConfig.getGameRootFolder(workspaceFolder.uri),
currentGame: currentGame,
ignoredFunctionNames: GscConfig.getIgnoredFunctionNames(workspaceFolder.uri),
ignoredFilePaths: GscConfig.getIgnoredFilePaths(workspaceFolder.uri),
errorDiagnostics: GscConfig.getErrorDiagnostics(workspaceFolder.uri),
gameConfig: GscConfig.gamesConfigs.get(currentGame)!
};
}
}
2 changes: 2 additions & 0 deletions src/GscFileParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2258,6 +2258,8 @@ export class GscFileParser {
}
const funcName = innerGroup.items[0].items[0].getSingleToken()!.name;
func = new GscFunction(
innerGroup,
innerGroup.items[0].items[0],
funcName,
funcName.toLowerCase(),
paramTokens,
Expand Down
Loading

0 comments on commit 1834293

Please sign in to comment.