️ Save cache state, reuse if possible

This commit is contained in:
Robin van der Linde 2025-01-15 20:30:37 +01:00
parent b8b9586b1e
commit 80a536d848
Signed by: Robin-van-der-Linde
GPG key ID: 53956B3252478F0D

View file

@ -18,7 +18,11 @@ export class CacheWorker {
/**
* List of cache items
*/
private cache: CacheItem[] = [];
private cache: CacheData = {
timestamp: 0,
items: [],
files: {},
};
/**
* Creates a new cache
@ -30,6 +34,7 @@ export class CacheWorker {
context: vscode.ExtensionContext
): Promise<CacheWorker> {
const cache = new CacheWorker(context);
await cache.loadCacheFromFile();
await cache.scanWorkspace();
return cache;
}
@ -51,6 +56,7 @@ export class CacheWorker {
* TODO: Find a more elegant way to do this
*/
private save() {
this.cache.timestamp = Date.now();
const jsonString = JSON.stringify(this.cache);
// Save it in the cache.json file in the .cache folder in the workspace
const workspaceFolder = vscode.workspace.workspaceFolders?.[0].uri.fsPath;
@ -62,6 +68,22 @@ export class CacheWorker {
}
}
/**
* Load the cache from the .cache/cache.json file
*/
private async loadCacheFromFile() {
try {
const workspaceFolder = vscode.workspace.workspaceFolders?.[0].uri.fsPath;
const cacheUri = vscode.Uri.file(`${workspaceFolder}/.cache/cache.json`);
const cache = await vscode.workspace.fs.readFile(cacheUri);
const cacheString = new TextDecoder().decode(cache);
this.cache = JSON.parse(cacheString);
} catch (error) {
console.error("Failed to load cache from file:", error);
this.cache = { timestamp: 0, items: [], files: {} };
}
}
/**
* Create a file system watcher
*/
@ -90,10 +112,36 @@ export class CacheWorker {
* Scans the workspace for all themes and layers
*/
private async scanWorkspace() {
const files = await vscode.workspace.findFiles("**/assets/**/*.json");
let changedFiles = 0;
let unchangedFiles = 0;
const files = await vscode.workspace.findFiles("assets/*/*/*.json");
for (const file of files) {
this.saveFileToCache(file);
if (
file.fsPath.endsWith("license_info.json") ||
file.fsPath.endsWith(".proto.json") ||
file.fsPath.endsWith("layers/favourite/favourite.json")
) {
continue;
}
const stats = await vscode.workspace.fs.stat(file);
if (stats.mtime > (this.cache.files[file.fsPath] || 0)) {
console.log(
"File has changed",
file.fsPath,
"last modified",
stats.mtime,
"cache",
this.cache.files[file.fsPath]
);
this.saveFileToCache(file);
changedFiles++;
} else {
unchangedFiles++;
}
}
console.log(
`Scanned workspace: ${changedFiles} changed files, ${unchangedFiles} unchanged files`
);
}
/**
@ -141,7 +189,7 @@ export class CacheWorker {
* @param uri File URI
*/
private deleteFileFromCache(uri: vscode.Uri) {
this.cache = this.cache.filter(
this.cache.items = this.cache.items.filter(
(item) => item.filePath.fsPath !== uri.fsPath
);
@ -184,7 +232,7 @@ export class CacheWorker {
`**/assets/layers/${layer}/${layer}.json`
);
this.cache.push({
this.cache.items.push({
id: layer,
filePath: uri,
jsonPath: ["layers"],
@ -221,7 +269,7 @@ export class CacheWorker {
`**/assets/layers/${layer.builtin}/${layer.builtin}.json`
);
this.cache.push({
this.cache.items.push({
id: layer.builtin,
filePath: uri,
jsonPath: ["layers"],
@ -258,7 +306,7 @@ export class CacheWorker {
`**/assets/layers/${builtinLayer}/${builtinLayer}.json`
);
this.cache.push({
this.cache.items.push({
id: builtinLayer,
filePath: uri,
jsonPath: ["layers"],
@ -288,6 +336,12 @@ export class CacheWorker {
}
}
// We also need to update the timestamp for this file
// First, we need to get the last modified date of the file
const stats = await vscode.workspace.fs.stat(uri);
this.cache.files[uri.fsPath] = stats.mtime;
// Save the cache
this.save();
this.printCache();
}
@ -311,6 +365,15 @@ export class CacheWorker {
const from = `layers.${uriFileName.split(".")[0]}`;
await this.saveLayerTextToCache(text, uri, from);
// We also need to update the timestamp for this file
// First, we need to get the last modified date of the file
const stats = await vscode.workspace.fs.stat(uri);
this.cache.files[uri.fsPath] = stats.mtime;
// Save the cache
this.save();
this.printCache();
}
/**
@ -375,9 +438,6 @@ export class CacheWorker {
} else {
console.log("No filters found in", filePath);
}
this.save();
this.printCache();
}
/**
@ -444,7 +504,7 @@ export class CacheWorker {
);
const toRange = getStartEnd(toText, ["tagRenderings", trIndex]);
this.cache.push({
this.cache.items.push({
id: tagRendering,
filePath: fromUri,
jsonPath: ["tagRenderings"],
@ -507,7 +567,7 @@ export class CacheWorker {
);
const toRange = getStartEnd(toText, ["tagRenderings", trIndex]);
this.cache.push({
this.cache.items.push({
id: tagRendering.builtin,
filePath: fromUri,
jsonPath: ["tagRenderings"],
@ -565,7 +625,7 @@ export class CacheWorker {
);
const toRange = getStartEnd(toText, ["tagRenderings", trIndex]);
this.cache.push({
this.cache.items.push({
id: builtinTagRendering,
filePath: fromUri,
jsonPath: ["tagRenderings"],
@ -589,7 +649,7 @@ export class CacheWorker {
} else if (!referencesOnly) {
// We've now had all possible references, so now we must have an acutal tagRendering
console.log(`TagRendering found in ${from}`);
this.cache.push({
this.cache.items.push({
id: `${json.id}.${tagRendering.id}`,
filePath: fromUri,
jsonPath: ["tagRenderings"],
@ -663,7 +723,7 @@ export class CacheWorker {
}
const toRange = getStartEnd(toText, ["filter", toFilterIndex]);
this.cache.push({
this.cache.items.push({
id: filter,
filePath: fromUri,
jsonPath: ["filters"],
@ -685,7 +745,7 @@ export class CacheWorker {
} else if (typeof filter === "object" && !referencesOnly) {
// This is a filter, which can be reused
console.log(`Filter found in ${from}`);
this.cache.push({
this.cache.items.push({
id: `${json.id}.${filter.id}`,
filePath: fromUri,
jsonPath: ["filters"],
@ -708,7 +768,11 @@ export class CacheWorker {
* Cache for interacting with the cache
*/
export class Cache {
private cache: CacheItem[] = [];
private cache: CacheData = {
timestamp: 0,
items: [],
files: {},
};
public static async create() {
const cache = new Cache();
@ -742,7 +806,7 @@ export class Cache {
public getTagRenderings(): vscode.CompletionItem[] {
console.log("Getting tag renderings from cache");
const tagRenderings: vscode.CompletionItem[] = [];
for (const item of this.cache) {
for (const item of this.cache.items) {
if (item.type === "tagRendering") {
if (item.id.startsWith("questions.")) {
const completionItem = new vscode.CompletionItem(
@ -768,7 +832,7 @@ export class Cache {
public getFilters(): vscode.CompletionItem[] {
console.log("Getting filters from cache");
const filters: vscode.CompletionItem[] = [];
for (const item of this.cache) {
for (const item of this.cache.items) {
if (item.type === "filter") {
if (item.id.startsWith("filters.")) {
const completionItem = new vscode.CompletionItem(
@ -795,7 +859,7 @@ export class Cache {
* @returns List of references
*/
public getReferences(to: string): CacheItem[] {
return this.cache.filter((item) => {
return this.cache.items.filter((item) => {
if (item.type === "reference") {
return item.reference?.to.id === to;
}
@ -804,6 +868,28 @@ export class Cache {
}
}
/**
* Layout of a cache data file
*/
interface CacheData {
/**
* Timestamp when the cache was last updated
*/
timestamp: number;
/**
* List of cache items
*/
items: CacheItem[];
/**
* List of files, together with their last modified timestamp
*
* The timestamp is defined as the number of milliseconds since January 1, 1970, 00:00:00 UTC
*/
files: Record<string, number>;
}
/**
* A cached item
* Can be a tagRendering or filter from a(n) (inline) layer