Skip to content

Commit

Permalink
WIP initial implementation of fracas workspace symbol cache
Browse files Browse the repository at this point in the history
  • Loading branch information
thisisthedave committed Jul 8, 2022
1 parent 4dfd9d9 commit 79bf6f8
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 9 deletions.
5 changes: 5 additions & 0 deletions src/editor-lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ import {
} from './config';
import { lastMatch } from './regular-expressions';

export async function findProjectFiles(pattern: string = '**/*.frc'): Promise<vscode.Uri[]> {
const files = vscode.workspace.findFiles(new vscode.RelativePattern(getProjectFolder(), pattern));
return files;
}

/**
* Search many documents to find matches for a regular expression.
* @param searchRx The regular expression used to search files
Expand Down
30 changes: 21 additions & 9 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as vscode from "vscode";
import * as path from "path";
import { DiagnosticSeverity, LanguageClient, LanguageClientOptions, TextDocumentContentChangeEvent } from "vscode-languageclient/node";
import { DiagnosticSeverity, LanguageClient, LanguageClientOptions } from "vscode-languageclient/node";
import * as com from "./commands";
import * as ue4 from "./ue4";
import {
Expand Down Expand Up @@ -113,21 +113,33 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
}
}

vscode.workspace.onDidChangeTextDocument(changeEvent => {
const document = changeEvent.document;
if (document.languageId === 'fracas') {
diagnosticCollection.clear();
// vscode.workspace.onDidChangeTextDocument(changeEvent => {
// const document = changeEvent.document;
// if (document.languageId === 'fracas') {
// diagnosticCollection.clear();
// const range = document.getWordRangeAtPosition(
// vscode.window.activeTextEditor?.selection.anchor ?? new vscode.Position(0,0),
// /[#:\w\-+*.>/]+/);
// if (range) {
// const diagnostic = new vscode.Diagnostic(range, "Why'd you fuck this up?", DiagnosticSeverity.Warning);
// diagnosticCollection.set(document.uri, [diagnostic]);
// }
// }
// });

vscode.workspace.fs.stat
vscode.workspace.onDidSaveTextDocument(async document => {
if (document && document.languageId === "fracas") {
// diagnosticCollection.clear();
const range = document.getWordRangeAtPosition(
vscode.window.activeTextEditor?.selection.anchor ?? new vscode.Position(0,0),
/[#:\w\-+*.>/]+/);

if (range) {
const diagnostic = new vscode.Diagnostic(range, "Why'd you fuck this up?", DiagnosticSeverity.Warning);
diagnosticCollection.set(document.uri, [diagnostic]);
}
}
});
vscode.workspace.onDidSaveTextDocument(async document => {
if (document && document.languageId === "fracas") {

await com.precompileFracasFile(document);
_maybeUpdateStringTables([document.uri]);
}
Expand Down
127 changes: 127 additions & 0 deletions src/fracas/symbol-cache
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import * as vscode from "vscode";
import {
findProjectFiles
} from '../editor-lib'
import {
FracasDefinition,
findAllIdentifiers,
findAllImportDefinitions
} from './syntax'

class FileState {
constructor(
public readonly stat: vscode.FileStat,
public readonly importedPaths: string[],
public readonly identifiers: string[],
public readonly providedIdentifiers: string[])
{}
}

function _fileStateKey(fsPath: string): string {
return `fs@${fsPath}`;
}

function _identifierKey(identifier: string): string {
return `id@${identifier}`;
}

function _importKey(importPath: string): string {
return `imp@${importPath}`;
}

export class FracasSymbolCache {
constructor(private storage: vscode.Memento) {}

async updateAll(): Promise<void> {
// collect fracas files currently in the project, and cache entries
const fileUris = await findProjectFiles();

// remove cache entries for files that are gone
const curPaths = new Set(fileUris.map(uri => uri.fsPath)) // the file path is the cache key
const removed = this.getFilePaths().filter(cachedPath => !curPaths.has(cachedPath));
for (const path of removed) {
this.removeFileState(path);
}

// update missing and stale cache entries
await Promise.all(fileUris.map(this._refreshFileStateForUri));
}

getFilePaths(): string[] {
return this.storage.keys().filter(key => key.startsWith('fs@'));
}

hasFileState(path: string): boolean {
return path in this.storage.keys();
}

getFileState(path: string): FileState | undefined {
return this.storage.get<FileState>(_fileStateKey(path));
}

getFileStateTimestamp(path: string): number {
const entry = this.getFileState(path);
return entry?.stat.mtime ?? 0
}

updateFileState(document: vscode.TextDocument, stat: vscode.FileStat): void {
this.removeFileState(document.fileName); // clear stale cache data

const imports = findAllImportDefinitions(document);
const importedPaths = imports.map(imp => imp.location.uri.fsPath);
const identifierDefs = findAllIdentifiers(document);
const identifiers = identifierDefs.map(id => document.getText(id.range));

// cache reverse map of file identifiers
for (const identifier of identifiers) {
this._setFilePathForIdentifier(identifier, document.fileName);
}

const newFileState = new FileState(stat, importedPaths, identifiers, [] /* TODO */);
this.storage.update(document.uri.fsPath, newFileState);
}

removeFileState(path: string): void {
const entry = this.getFileState(path);
// remove all identifiers for this file
if (entry) {
for (const identifier of entry.identifiers) {
this.storage.update(_identifierKey(identifier), undefined);
}
}

this.storage.update(path, undefined); // clear the entry.
}

private _getImportedIdentifiersRecursive(path: string, pathsSeen: Set<string>, identifiers: Set<string>): void {
if (pathsSeen.has(path)) {
return;
}
pathsSeen.add(path);
const fileState = this.getFileState(path);
if (fileState) {
for (const identifier of fileState.providedIdentifiers) {
identifiers.add(identifier);
}
for (const importedPath of fileState.importedPaths) {
this._getImportedIdentifiersRecursive(importedPath, pathsSeen, identifiers);
}
}
}

getFilePathForIdentifier(identifier: string): string | undefined {
return this.storage.get<string>(_identifierKey(identifier));
}

private _setFilePathForIdentifier(identifier: string, path: string): void {
this.storage.update(_identifierKey(identifier), path);
}

private async _refreshFileStateForUri(uri: vscode.Uri): Promise<void> {
const stat = await vscode.workspace.fs.stat(uri);
if (!this.hasFileState(uri.fsPath) || stat.mtime > this.getFileStateTimestamp(uri.fsPath)) {
const document = await vscode.workspace.openTextDocument(uri);
this.updateFileState(document, stat);
}
}
}

0 comments on commit 79bf6f8

Please sign in to comment.