From 4d1ea863dfbc0cc9a049a79b3464720e6f31c3a9 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Wed, 8 Jan 2025 00:46:51 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=A7=20First=20kind=20of=20working=20ve?= =?UTF-8?q?rsion=20for=20tagRenderingImplementationProvider?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/extension.ts | 4 +- src/layers.ts | 146 +++++++++++++++++++++++++++++++++++++++++++++ src/utils/cache.ts | 18 +++++- 3 files changed, 165 insertions(+), 3 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index da2b645..cee3eca 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -6,6 +6,7 @@ import { filterDefinitionProvider, tagRenderingCompletionProvider, tagRenderingDefinitionProvider, + tagRenderingImplementationProvider, } from "./layers"; import { pathDefinitionProvider } from "./license_info"; import { CacheWorker } from "./utils/cache"; @@ -19,7 +20,8 @@ export async function activate(context: vscode.ExtensionContext) { tagRenderingCompletionProvider, tagRenderingDefinitionProvider, filterCompletionProvider, - filterDefinitionProvider + filterDefinitionProvider, + tagRenderingImplementationProvider ); // Activate all license info related features diff --git a/src/layers.ts b/src/layers.ts index 137c4b8..bde1e41 100644 --- a/src/layers.ts +++ b/src/layers.ts @@ -13,6 +13,7 @@ import { getCursorPath, getRawCursorPath, getStartEnd } from "./utils/cursor"; import { getFilters, getTagRenderings } from "./utils/mapcomplete"; import { getValueFromPath } from "./utils/json"; import { JSONPath } from "jsonc-parser"; +import { Cache } from "./utils/cache"; /** * Tag rendering completion provider @@ -172,6 +173,151 @@ export const tagRenderingDefinitionProvider = } ); +export const tagRenderingImplementationProvider = + vscode.languages.registerImplementationProvider( + { + language: "json", + scheme: "file", + pattern: "**/assets/*/*/*.json", + }, + { + async provideImplementation( + document: vscode.TextDocument, + position: vscode.Position, + _token: vscode.CancellationToken + ) { + console.log("tagRenderingImplementationProvider"); + const text = document.getText(); + const jsonPath = getCursorPath(text, position); + const rawJsonPath = getRawCursorPath(text, position); + + const regex = /^tagRenderings(\+)?\.\d+\.id$/; + + if (regex.exec(jsonPath)) { + const tagRenderingId = getValueFromPath(text, rawJsonPath); + const layerName = document.fileName.split("/").pop()?.split(".")[0]; + const to = `layers.${layerName}.tagRenderings.${tagRenderingId}`; + + try { + const cache = await Cache.create(); + const references = cache.getReferences(to); + + if (references.length === 0) { + return null; + } else { + // TODO: This is way too much to be executing at this time, most of this should be cached + // TODO: Also, this seems to fail for the first time, but work for every subsequent time + console.log(`Found ${references.length} references to ${to}`); + + const links: vscode.DefinitionLink[] = []; + for (const reference of references) { + const originType = reference.reference?.from.split(".")[0]; + const originName = reference.reference?.from.split(".")[1]; + + // We need to open the file where the reference is located + const originFile = await vscode.workspace.findFiles( + `assets/${originType}/${originName}/${originName}.json` + ); + if (originFile.length === 0) { + continue; + } + + const originText = await vscode.workspace.fs.readFile( + originFile[0] + ); + const originTextString = new TextDecoder().decode(originText); + const origin = JSON.parse(originTextString); + + let tagRenderings: unknown[] = []; + let tagRenderingsPath: JSONPath = []; + + // Now we'll need to find the tagRenderings object, and its path + if (originType === "themes") { + const parts = reference.reference?.from.split("."); + if (!parts) { + continue; + } else { + console.log("Parts", parts); + // Now we need to find the correct inline layer + const layerIndex = origin.layers.findIndex( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (layer: any) => layer.id === parts[3] + ); + + const path: JSONPath = [ + parts[2], + layerIndex, + ...reference.jsonPath, + ]; + + console.log("Trying to get tagRenderings from theme", path); + + const tagRenderingsFromOrigin = getValueFromPath( + originTextString, + path + ); + if (!tagRenderingsFromOrigin) { + console.error( + "Could not find tagRenderings in theme", + originName + ); + continue; + } else { + // Yaay, we found the tagRenderings + console.log("Found tagRenderings in theme", originName); + tagRenderings = tagRenderingsFromOrigin as unknown[]; + tagRenderingsPath = path; + } + } + } else if (originType === "layers") { + tagRenderings = origin.tagRenderings; + tagRenderingsPath = ["tagRenderings"]; + } + + // The index is actually a really complicated, because a reference could be a string or an object with a builtin property, which can be a string or a list of strings + // Also if the reference is from an inline layer + const tagRenderingIndex = tagRenderings.findIndex( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (tr: any) => { + if (typeof tr === "string") { + return tr === reference.id; + } else if (typeof tr.builtin === "string") { + return tr.builtin === reference.id; + } + // } else if (tr.builtin) { + // return tr.builtin.includes(reference.id); + // } + } + ); + const path: JSONPath = [ + ...tagRenderingsPath, + tagRenderingIndex, + ]; + const startEnd = getStartEnd(originTextString, path); + + console.log( + `Pushing link from ${document.fileName} to ${originFile[0].path} at ${startEnd.start.line}.${startEnd.start.character} to ${startEnd.end.line}.${startEnd.end.character}` + ); + + links.push({ + originSelectionRange: getStartEnd(text, rawJsonPath), + targetRange: startEnd, + targetUri: originFile[0], + }); + } + console.log(`Found ${links.length} implementations`); + return links; + } + } catch (error) { + console.error("Error get implementation", error); + } + } + + return null; + }, + } + ); + /** * Filter completion provider * diff --git a/src/utils/cache.ts b/src/utils/cache.ts index 02405d7..bfaa5ab 100644 --- a/src/utils/cache.ts +++ b/src/utils/cache.ts @@ -280,7 +280,7 @@ export class CacheWorker { const json = JSON.parse(text); // Check if this layer doesn't have a special source, or uses a geoJson source - if (json.source === "special" || json.source.geoJson) { + if (json.source === "special" || json.source?.geoJson) { console.log("Layer has a special source, only saving references"); referencesOnly = true; } @@ -453,7 +453,6 @@ export class Cache { const cache = await vscode.workspace.fs.readFile(cacheUri); const cacheString = new TextDecoder().decode(cache); this.cache = JSON.parse(cacheString); - console.log(`Cache loaded, ${this.cache.length} items`); } else { console.error("No workspace folder found"); throw new Error("No workspace folder found"); @@ -513,6 +512,21 @@ export class Cache { } return filters; } + + /** + * Get all references to a specific item + * + * @param to Item to get references for (e.g. layers.bicycle_rental) + * @returns List of references + */ + public getReferences(to: string): CacheItem[] { + return this.cache.filter((item) => { + if (item.type === "reference") { + return item.reference?.to === to; + } + return false; + }); + } } /**