Skip to content

Commit

Permalink
Refactored reverse GSC file references + markdown description generator
Browse files Browse the repository at this point in the history
  • Loading branch information
eyza-cod2 committed Nov 4, 2024
1 parent 815a59f commit 0524f63
Show file tree
Hide file tree
Showing 22 changed files with 650 additions and 277 deletions.
12 changes: 6 additions & 6 deletions src/GscDiagnosticsCollection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ export class GscDiagnosticsCollection {

const referenceData = GscFiles.getReferencedFileForFile(gscFile, path);

if (!gscFile.config.gameConfig.includeFileItself && referenceData.gscFile?.uri.toString() === gscFile.uri.toString()) {
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);
}

Expand All @@ -390,7 +390,7 @@ export class GscDiagnosticsCollection {
}

// Check for duplicated function definitions
if (gscFile.config.gameConfig.duplicateFunctionDefinitions === false && referenceData.gscFile) {
if (gscFile.config.gameConfig.duplicateFunctionDefinitions === false && referenceData) {
// Get all function definitions from included file
const funcDefsInIncludedFile = GscFunctions.getLocalFunctionDefinitions(referenceData.gscFile);

Expand All @@ -402,7 +402,7 @@ export class GscDiagnosticsCollection {
for (let j = 0; j < index; j++) {
const otherPath = allIncludedPaths[j];
const otherReferenceData = GscFiles.getReferencedFileForFile(gscFile, otherPath);
if (!otherReferenceData.gscFile) {
if (!otherReferenceData) {
continue;
}
const otherFuncDefs = GscFunctions.getLocalFunctionDefinitions(otherReferenceData.gscFile);
Expand All @@ -420,10 +420,10 @@ export class GscDiagnosticsCollection {
}
}

if (referenceData.gscFile === undefined) {
if (referenceData === undefined) {

// This file path is ignored by configuration
if (gscFile.config.ignoredFilePaths.some(ignoredPath => path.toLowerCase().startsWith(ignoredPath.toLowerCase()))) {
if (GscFiles.isFileIgnoredBySettings(gscFile, path)) {
return;
}
const d = new vscode.Diagnostic(
Expand Down Expand Up @@ -519,7 +519,7 @@ export class GscDiagnosticsCollection {
case GscFunctionState.NotFoundFile:

// This file path is ignored by configuration
if (gscFile.config.ignoredFilePaths.some(ignoredPath => funcInfo.path.toLowerCase().startsWith(ignoredPath.toLowerCase()))) {
if (GscFiles.isFileIgnoredBySettings(gscFile, funcInfo.path)) {
return;
}
var r = (funcInfo.pathGroup) ? funcInfo.pathGroup.getRange() : group.getRange();
Expand Down
2 changes: 0 additions & 2 deletions src/GscFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,7 @@ export class GscFile {
} else {
this.config = {
referenceableGameRootFolders: [],
referenceableGameRootFoldersAll: [],
referenceableWorkspaceFolders: [],
referenceableWorkspaceFoldersAll: [],
rootFolder: undefined,
currentGame: GscGame.UniversalGame,
ignoredFunctionNames: [],
Expand Down
15 changes: 4 additions & 11 deletions src/GscFileCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,10 @@ 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[];
referenceableGameRootFolders: GscGameRootFolder[];
/** All possible workspace folders where GSC files can be found and referenced, including reverse references */
referenceableWorkspaceFoldersAll: vscode.WorkspaceFolder[];
referenceableWorkspaceFolders: vscode.WorkspaceFolder[];

/** Game root folder */
rootFolder: GscGameRootFolder | undefined;
Expand Down Expand Up @@ -235,10 +230,8 @@ export class GscWorkspaceFileData {
const currentGame = GscConfig.getSelectedGame(workspaceFolder.uri);

return {
referenceableGameRootFolders: GscFiles.loadReferenceableGameRootFolders(workspaceFolder, false),
referenceableGameRootFoldersAll: GscFiles.loadReferenceableGameRootFolders(workspaceFolder, true),
referenceableWorkspaceFolders: GscFiles.loadReferenceableWorkspaceFolders(workspaceFolder, false),
referenceableWorkspaceFoldersAll: GscFiles.loadReferenceableWorkspaceFolders(workspaceFolder, true),
referenceableGameRootFolders: GscFiles.loadReferenceableGameRootFolders(workspaceFolder),
referenceableWorkspaceFolders: GscFiles.loadReferenceableWorkspaceFolders(workspaceFolder),
rootFolder: GscConfig.getGameRootFolder(workspaceFolder.uri),
currentGame: currentGame,
ignoredFunctionNames: GscConfig.getIgnoredFunctionNames(workspaceFolder.uri),
Expand Down
108 changes: 66 additions & 42 deletions src/GscFiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,10 +320,9 @@ export class GscFiles {
* It includes current workspace and included workspace folders via settings.
* The workspace folders are sorted by their indexes (first being the last workspace folder in editor).
* @param workspaceFolder Workspace folder from which the reference is made.
* @param includeReversedReferences If true, also workspace folders that include the current workspace folder are included.
* @returns Array of workspace folders where the file can be found.
*/
public static loadReferenceableWorkspaceFolders(workspaceFolder: vscode.WorkspaceFolder, includeReversedReferences: boolean): vscode.WorkspaceFolder[] {
public static loadReferenceableWorkspaceFolders(workspaceFolder: vscode.WorkspaceFolder): vscode.WorkspaceFolder[] {

/*
Define all possible locations where the file can be found
Expand All @@ -348,13 +347,11 @@ export class GscFiles {
includedWorkspaceFolders.push(workspaceFolder);

// Find workspace folders that includes this workspace folder
if (includeReversedReferences) {
for (const workspaceFolder2 of vscode.workspace.workspaceFolders ?? []) {
const cfgIncludedWorkspaceFolderNames = GscConfig.getIncludedWorkspaceFolders(workspaceFolder2.uri);
// This workspace folder includes the referenced workspace folder
if (cfgIncludedWorkspaceFolderNames.includes(workspaceFolder.name)) {
includedWorkspaceFolders.push(workspaceFolder2);
}
for (const workspaceFolder2 of vscode.workspace.workspaceFolders ?? []) {
const cfgIncludedWorkspaceFolderNames = GscConfig.getIncludedWorkspaceFolders(workspaceFolder2.uri);
// This workspace folder includes the referenced workspace folder
if (cfgIncludedWorkspaceFolderNames.includes(workspaceFolder.name)) {
includedWorkspaceFolders.push(workspaceFolder2);
}
}

Expand Down Expand Up @@ -409,13 +406,12 @@ export class GscFiles {
* Get all possible game root folders where GSC files can be found and referenced.
* It includes game root folder from current workspace and game root folders from included workspace folders via settings.
* @param workspaceFolder Workspace folder from which the game root folders are referenced.
* @param includeReversedReferences If true, also workspace folders that include the current workspace folder are included.
* @returns Array of game root folders where the file can be found. The first item is the game root folder from the workspace where the file is located. Other items are game root folders from included workspace folders sorted by their indexes.
*/
public static loadReferenceableGameRootFolders(workspaceFolder: vscode.WorkspaceFolder, includeReversedReferences: boolean): GscGameRootFolder[] {
public static loadReferenceableGameRootFolders(workspaceFolder: vscode.WorkspaceFolder): GscGameRootFolder[] {

// Define all possible locations where the file can be found (globally included workspace folders)
const includedWorkspaceFolders = GscFiles.loadReferenceableWorkspaceFolders(workspaceFolder, includeReversedReferences);
const includedWorkspaceFolders = GscFiles.loadReferenceableWorkspaceFolders(workspaceFolder);

// Convert workspace folders to URIs taking game root folder into account
const uris = includedWorkspaceFolders.map(f => GscConfig.getGameRootFolder(f.uri)!);
Expand All @@ -426,48 +422,65 @@ export class GscFiles {

/**
* Get referenced file in specified file. It tries to find the file in all possible locations where it can be found (workspace folder and included workspace folders).
* File must be part of workspace to be found.
* @param gscFile GSC file where the reference is located.
* @param referencedFilePath Path in GSC file format (e.g. scripts\scriptName)
* @param includeReversedReferences If true, it also checks if the file is replaced by another file in another workspace folder.
* @returns The reference status and the parsed file data if found
*/
public static getReferencedFileForFile(gscFile: GscFile, referencedFilePath: string, includeReversedReferences: boolean = false): GscFileAndReferenceState {
public static getReferencedFileForFile(gscFile: GscFile, referencedFilePath: string): GscFileAndReferenceState | undefined {

const gameRootFolders = includeReversedReferences ? gscFile.config.referenceableGameRootFoldersAll : gscFile.config.referenceableGameRootFolders;
const file = this.getReferencedFilesForFile(gscFile, referencedFilePath, true);

if (file.length > 0) {
return file[0];
}

return undefined;
}


for (const referenceableGameRoot of gameRootFolders) {
/**
* Get the referenced file in specified file from all included workspaces.
* The first item in the array is the file that should be used. Other items are files that are replaced by the first file.
* File must be part of workspace to be found.
* @param gscFile GSC file where the reference is located.
* @param referencedFilePath Path in GSC file format (e.g. scripts\scriptName)
* @returns Array of reference status and the parsed file data if found
*/
public static getReferencedFilesForFile(gscFile: GscFile, referencedFilePath: string, exitOnFirst: boolean = false): GscFileAndReferenceState[] {
const referencedFiles: GscFileAndReferenceState[] = [];

if (!gscFile.workspaceFolder) {
return referencedFiles;
}

for (const referenceableGameRoot of gscFile.config.referenceableGameRootFolders) {
const gscFilePathUri = vscode.Uri.joinPath(referenceableGameRoot.uri, referencedFilePath.replace(/\\/g, '/') + ".gsc");
const gsc = GscFiles.getCachedFile(gscFilePathUri, referenceableGameRoot.workspaceFolder.uri);

if (gsc === undefined) {
continue; // not found, try next workspace folder
}

// File found, check if it is replaced by another file in another workspace folder
if (includeReversedReferences === false) {
const fileWithReverseReferences = this.getReferencedFileForFile(gscFile, referencedFilePath, true);

if (gsc !== fileWithReverseReferences.gscFile && fileWithReverseReferences.referenceState !== GscFileReferenceState.NotFound) {
// File is replaced by another file in another workspace folder
if (fileWithReverseReferences.referenceState === GscFileReferenceState.IncludedWorkspaceFolder) {
fileWithReverseReferences.referenceState = GscFileReferenceState.IncludedWorkspaceFolderReversed;
} else if (fileWithReverseReferences.referenceState === GscFileReferenceState.LocalFile) {
// This should not happen
throw new Error("File is replaced by another file in another workspace folder, but it is not included workspace folder");
}
return fileWithReverseReferences;
}
if (gsc === undefined || !gsc.workspaceFolder) {
continue; // not found or not part of workspace, try next workspace folder
}

var state = GscFileReferenceState.LocalFile;
var workspace = gsc.workspaceFolder;
if (referenceableGameRoot.workspaceFolder !== gscFile.workspaceFolder) {
state = GscFileReferenceState.IncludedWorkspaceFolder;
if (gsc.workspaceFolder.index > gscFile.workspaceFolder.index) {
state = GscFileReferenceState.IncludedWorkspaceFolderOverwritten;
} else {
state = GscFileReferenceState.IncludedWorkspaceFolder;
}
workspace = referenceableGameRoot.workspaceFolder;
}

referencedFiles.push({gscFile: gsc, referenceState: state, referenceWorkspace: workspace});

return {gscFile: gsc, referenceState: state};
if (exitOnFirst) {
break;
}
}

return {gscFile: undefined, referenceState: GscFileReferenceState.NotFound};
return referencedFiles;
}


Expand Down Expand Up @@ -495,9 +508,20 @@ export class GscFiles {
*/
public static isFileReplacedByAnotherFile(gscFile: GscFile): boolean {

const referencedFiles = this.getReferencedFileForFile(gscFile, gscFile.gamePath, true);
const referencedFiles = this.getReferencedFileForFile(gscFile, gscFile.gamePath);

if (referencedFiles && referencedFiles.gscFile !== gscFile) {
return true;
}
return false;
}

if (referencedFiles.gscFile !== gscFile) {

/**
* Check if the GSC file is ignored by settings.
*/
public static isFileIgnoredBySettings(gscFile: GscFile, gamePath: string): boolean {
if (gscFile.config.ignoredFilePaths.some(ignoredPath => gamePath.toLowerCase().startsWith(ignoredPath.toLowerCase()))) {
return true;
}
return false;
Expand Down Expand Up @@ -1098,13 +1122,13 @@ export class GscFiles {


export enum GscFileReferenceState {
NotFound,
LocalFile,
IncludedWorkspaceFolder,
IncludedWorkspaceFolderReversed
IncludedWorkspaceFolderOverwritten
}

export type GscFileAndReferenceState = {
gscFile: GscFile | undefined,
gscFile: GscFile,
referenceState: GscFileReferenceState
referenceWorkspace: vscode.WorkspaceFolder
};
67 changes: 12 additions & 55 deletions src/GscFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { GroupType, GscGroup, GscToken, GscVariableDefinitionType } from './GscF
import { GscFiles, GscFileReferenceState } from './GscFiles';
import { GscFile } from './GscFile';
import { CodFunctions } from './CodFunctions';
import { GscMarkdownGenerator } from './GscMarkdownGenerator';


export class GscFunction {
Expand All @@ -25,53 +26,8 @@ export class GscFunction {
public rangeScope: vscode.Range,
) {}

public static generateMarkdownDescription(
func: GscFunction | {name: string, parameters: {name: string, commentBefore?: string}[]},
isLocalFunction: boolean,
uri: string | undefined,
reason: string = "")
: vscode.MarkdownString
{
const md = new vscode.MarkdownString();
md.isTrusted = true;
var text = "";

// Declaration
text += func.name + "(";
text += func.parameters.map(p => p.name).join(", ");
text += ")";
md.appendCodeblock(text);

// Description
//md.appendMarkdown("" + func.desc + "\n\n");

// Parameters
func.parameters.forEach(p => {
text = "@param ```" + p.name + "```";
if (p.commentBefore !== undefined && p.commentBefore !== "") {
text += " — " + p.commentBefore;
}
text += " \n";
md.appendMarkdown(text);
});

if (!isLocalFunction && uri !== undefined) {
const uri2 = vscode.Uri.parse(uri);
const relativePath = vscode.workspace.asRelativePath(uri2);
md.appendMarkdown("\nFile: `" + relativePath + "`");
} else if (isLocalFunction) {
md.appendMarkdown("\n`Local function`");
}

if (reason !== "") {
md.appendMarkdown("\n\n" + reason);
}

return md;
}

public generateMarkdownDescription(isLocalFunction: boolean, uri: string | undefined, reason: string): vscode.MarkdownString {
return GscFunction.generateMarkdownDescription(this, isLocalFunction, uri, reason);
return GscMarkdownGenerator.generateFunctionDescription(this, isLocalFunction, uri, reason);
}
};

Expand Down Expand Up @@ -145,7 +101,7 @@ export class GscFunctions {
// File not found
if (definitions === undefined) {
// This file path is ignored by configuration
if (funcInfo && gscFile.config.ignoredFilePaths.some(ignoredPath => funcInfo.path.toLowerCase().startsWith(ignoredPath.toLowerCase()))) {
if (funcInfo && GscFiles.isFileIgnoredBySettings(gscFile, funcInfo.path)) {
return ret(GscFunctionState.NotFoundFileButIgnored, []);
}
return ret(GscFunctionState.NotFoundFile, []);
Expand Down Expand Up @@ -217,20 +173,20 @@ export class GscFunctions {
if (funcInfo && funcInfo.path.length > 0)
{
const referenceData = GscFiles.getReferencedFileForFile(gscFile, funcInfo.path);
const referencedGscFile = referenceData.gscFile;
if (referencedGscFile === undefined) {
if (referenceData === undefined) {
return undefined;
}
const referencedGscFile = referenceData.gscFile;

let reason = "";
switch (referenceData.referenceState) {
/*switch (referenceData.referenceState) {
case GscFileReferenceState.IncludedWorkspaceFolder:
reason = "Included via workspace folder settings";
break;
case GscFileReferenceState.IncludedWorkspaceFolderReversed:
case GscFileReferenceState.IncludedWorkspaceFolderOverwritten:
reason = "Included via other workspace folder settings. \nFile in current workspace is being overwritten.";
break;
}
}*/

for (const f of referencedGscFile.data.functions) {
if (!funcInfo || f.nameId === funcNameId) {
Expand Down Expand Up @@ -264,10 +220,11 @@ export class GscFunctions {
// Loop through all included files
for (const includedPath of gscFile.data.includes) {

const referencedFile = GscFiles.getReferencedFileForFile(gscFile, includedPath).gscFile;
if (referencedFile === undefined) {
const referenceData = GscFiles.getReferencedFileForFile(gscFile, includedPath);
if (referenceData === undefined) {
continue; // File not found
}
const referencedFile = referenceData.gscFile;
if (referencedFile.uri.toString() === gscFile.uri.toString()) {
continue; // This includes itself, functions are already added
}
Expand Down Expand Up @@ -310,7 +267,7 @@ export class GscFunctions {
funcReferences.push(...funcDefs.map(f => {return {func: f.func.groupFunctionName, uri: f.uri};}));

// Find all function references in all files
const allFiles = gscFile.workspaceFolder ? GscFiles.getReferenceableCachedFiles(gscFile.workspaceFolder.uri, false) : [gscFile];
const allFiles = gscFile.workspaceFolder ? GscFiles.getReferenceableCachedFiles(gscFile.workspaceFolder.uri, true) : [gscFile];

for (const gscFile2 of allFiles) {
gscFile2.data.root.walk((group) => {
Expand Down
Loading

0 comments on commit 0524f63

Please sign in to comment.