✨ Add some layer related providers
This commit is contained in:
parent
10822c6686
commit
61e61a4e2c
6 changed files with 262 additions and 62 deletions
|
@ -7,3 +7,5 @@ Not everything is supported yet, but currently the following features are suppor
|
|||
- Autocompletion for the layer names
|
||||
- Definition support for the layer names
|
||||
- Definintion support for icons
|
||||
- Autocompletion for tagRenderings in questions.json
|
||||
- Definition support for tagRenderings in questions.json
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
import * as vscode from "vscode";
|
||||
import { layerCompletionProvider, layerDefinitionProvider } from "./theme";
|
||||
import { iconDefinitionProvider } from "./generic";
|
||||
import { tagRenderingCompletionProvider } from "./layers";
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
// Activate all theme related features
|
||||
context.subscriptions.push(layerCompletionProvider, layerDefinitionProvider);
|
||||
|
||||
// Activate all layer related features
|
||||
context.subscriptions.push(tagRenderingCompletionProvider);
|
||||
|
||||
// Activate all generic features
|
||||
context.subscriptions.push(iconDefinitionProvider);
|
||||
}
|
||||
|
|
|
@ -35,8 +35,7 @@ export const iconDefinitionProvider =
|
|||
|
||||
const regexes = [/icon$/, /icon.render/, /icon.mappings.\d+.then$/];
|
||||
|
||||
for (const regex of regexes) {
|
||||
if (regex.exec(jsonPath)) {
|
||||
if (regexes.some((regex) => regex.exec(jsonPath))) {
|
||||
const iconPath = getValueFromPath(text, rawJsonPath);
|
||||
console.log("Found reference to icon", iconPath);
|
||||
|
||||
|
@ -63,17 +62,6 @@ export const iconDefinitionProvider =
|
|||
}
|
||||
|
||||
const startEnd = getStartEnd(text, rawJsonPath);
|
||||
console.log("fullIconPath", fullIconPath);
|
||||
console.log(
|
||||
"startEndLines",
|
||||
startEnd.start.line,
|
||||
startEnd.end.line
|
||||
);
|
||||
console.log(
|
||||
"startEndChars",
|
||||
startEnd.start.character,
|
||||
startEnd.end.character
|
||||
);
|
||||
|
||||
const link: vscode.DefinitionLink = {
|
||||
targetUri: vscode.Uri.file(fullIconPath),
|
||||
|
@ -83,7 +71,6 @@ export const iconDefinitionProvider =
|
|||
|
||||
return [link];
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
},
|
||||
|
|
156
src/layers.ts
Normal file
156
src/layers.ts
Normal file
|
@ -0,0 +1,156 @@
|
|||
/**
|
||||
* This file contains all functions that should be used when editing layers
|
||||
* Layer files are located in the /assets/layers/{LAYER_NAME}.json file
|
||||
* This is also loaded when editing the theme files, as layers can be inline in the theme files
|
||||
*
|
||||
* This consists of the following functions:
|
||||
* - tagRenderingCompletionProvider: Provides a list of existing tag renderings for autocompletion
|
||||
* - tagRenderingDefinitionProvider: Provides a definition for tag renderings, allowing users to jump to the tag rendering definition
|
||||
*/
|
||||
|
||||
import * as vscode from "vscode";
|
||||
import {
|
||||
getCursorPath,
|
||||
getRawCursorPath,
|
||||
getStartEnd,
|
||||
getTagRenderings,
|
||||
getValueFromPath,
|
||||
} from "./utils";
|
||||
import { JSONPath } from "jsonc-parser";
|
||||
|
||||
export const tagRenderingCompletionProvider =
|
||||
vscode.languages.registerCompletionItemProvider(
|
||||
{
|
||||
language: "json",
|
||||
scheme: "file",
|
||||
pattern: "**/assets/layers/*/*.json",
|
||||
},
|
||||
{
|
||||
async provideCompletionItems(
|
||||
document: vscode.TextDocument,
|
||||
position: vscode.Position
|
||||
) {
|
||||
// Stop running if the file is called license_info.json
|
||||
if (document.fileName.includes("license_info")) {
|
||||
return [];
|
||||
}
|
||||
|
||||
console.log("tagRenderingCompletionProvider");
|
||||
const text = document.getText();
|
||||
const jsonPath = getCursorPath(text, position);
|
||||
|
||||
const regex = /^(layers.\d+.)?tagRenderings\.\d+(.builtin)?$/;
|
||||
if (regex.exec(jsonPath)) {
|
||||
const tagRenderings = await getTagRenderings();
|
||||
console.log(`Got ${tagRenderings.length} tagRenderings`);
|
||||
|
||||
// Now we need to return the completion items
|
||||
return tagRenderings;
|
||||
}
|
||||
|
||||
return [];
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Tag rendering definition provider
|
||||
*/
|
||||
export const tagRenderingDefinitionProvider =
|
||||
vscode.languages.registerDefinitionProvider(
|
||||
{
|
||||
language: "json",
|
||||
scheme: "file",
|
||||
pattern: "**/assets/layers/*/*.json",
|
||||
},
|
||||
{
|
||||
async provideDefinition(
|
||||
document: vscode.TextDocument,
|
||||
position: vscode.Position
|
||||
) {
|
||||
console.log("tagRenderingDefinitionProvider");
|
||||
const text = document.getText();
|
||||
const jsonPath = getCursorPath(text, position);
|
||||
const rawJsonPath = getRawCursorPath(text, position);
|
||||
|
||||
const regex = /^(layers.\d.)?tagRenderings.\d*(.builtin)?$/;
|
||||
|
||||
if (regex.exec(jsonPath)) {
|
||||
const tagRendering = getValueFromPath(text, rawJsonPath);
|
||||
|
||||
if (typeof tagRendering === "string") {
|
||||
console.log("Found reference to tagRendering", tagRendering);
|
||||
if (tagRendering.indexOf(".") === -1) {
|
||||
console.log("This is a built-in tag rendering");
|
||||
// This is a built-in tag rendering
|
||||
// Read the built-in tag renderings file
|
||||
const layerFile = await vscode.workspace.findFiles(
|
||||
"assets/layers/questions/questions.json"
|
||||
);
|
||||
if (layerFile.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const layerText = await vscode.workspace.fs.readFile(
|
||||
layerFile[0]
|
||||
);
|
||||
const layerTextString = new TextDecoder().decode(layerText);
|
||||
const layer = JSON.parse(layerTextString);
|
||||
|
||||
const tagRenderingIndex = layer.tagRenderings.findIndex(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(tr: any) => tr.id === tagRendering
|
||||
);
|
||||
|
||||
const path: JSONPath = ["tagRenderings", tagRenderingIndex];
|
||||
const startEnd = getStartEnd(layerTextString, path);
|
||||
|
||||
const link: vscode.DefinitionLink = {
|
||||
targetUri: layerFile[0],
|
||||
targetRange: startEnd,
|
||||
originSelectionRange: getStartEnd(text, rawJsonPath),
|
||||
};
|
||||
|
||||
return [link];
|
||||
} else {
|
||||
// This is a reference to a tag rendering in another layer
|
||||
// We need to find the layer and the tag rendering
|
||||
const layerName = tagRendering.split(".")[0];
|
||||
const tagRenderingName = tagRendering.split(".")[1];
|
||||
|
||||
const layerFile = await vscode.workspace.findFiles(
|
||||
`assets/layers/${layerName}/${layerName}.json`
|
||||
);
|
||||
if (layerFile.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const layerText = await vscode.workspace.fs.readFile(
|
||||
layerFile[0]
|
||||
);
|
||||
const layerTextString = new TextDecoder().decode(layerText);
|
||||
const layer = JSON.parse(layerTextString);
|
||||
|
||||
const tagRenderingIndex = layer.tagRenderings.findIndex(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(tr: any) => tr.id === tagRenderingName
|
||||
);
|
||||
|
||||
const path: JSONPath = ["tagRenderings", tagRenderingIndex];
|
||||
const startEnd = getStartEnd(layerTextString, path);
|
||||
|
||||
const link: vscode.DefinitionLink = {
|
||||
targetUri: layerFile[0],
|
||||
targetRange: startEnd,
|
||||
originSelectionRange: getStartEnd(text, rawJsonPath),
|
||||
};
|
||||
|
||||
return [link];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
}
|
||||
);
|
54
src/theme.ts
54
src/theme.ts
|
@ -1,5 +1,6 @@
|
|||
/**
|
||||
* This file contains all functions that should be used when editing theme files
|
||||
* This file contains all functions that should mainly be used when editing theme files
|
||||
* All of these are related to looking for layer references in the theme files, but some layers also reference other layers
|
||||
* Theme files are located in the /assets/themes/{THEME_NAME}/{THEME_NAME}.json file
|
||||
*
|
||||
* This consists of the following functions:
|
||||
|
@ -9,7 +10,12 @@
|
|||
|
||||
import * as vscode from "vscode";
|
||||
import * as path from "path";
|
||||
import { getAvailableLayers, getCursorPath } from "./utils";
|
||||
import {
|
||||
getAvailableLayers,
|
||||
getCursorPath,
|
||||
getRawCursorPath,
|
||||
getValueFromPath,
|
||||
} from "./utils";
|
||||
|
||||
/**
|
||||
* Layer completion provider
|
||||
|
@ -25,22 +31,27 @@ export const layerCompletionProvider =
|
|||
{
|
||||
language: "json",
|
||||
scheme: "file",
|
||||
pattern: "**/assets/themes/*/*.json",
|
||||
pattern: "**/assets/*/*/*.json",
|
||||
},
|
||||
{
|
||||
async provideCompletionItems(
|
||||
document: vscode.TextDocument,
|
||||
position: vscode.Position
|
||||
) {
|
||||
console.log("layerCompletionProvider");
|
||||
// Now we'll need to try and get the current path for the cursor
|
||||
const text = document.getText();
|
||||
// Stop running if the file is called license_info.json
|
||||
if (document.fileName.includes("license_info")) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Now we need to get the current path
|
||||
console.log("layerCompletionProvider");
|
||||
const text = document.getText();
|
||||
const jsonPath = getCursorPath(text, position);
|
||||
|
||||
const regex = /^layers\.\d+(.builtin)*$/;
|
||||
if (regex.exec(jsonPath)) {
|
||||
const regexes = [
|
||||
/^layers\.\d+(.builtin)?$/,
|
||||
/^(layers.\d+.)?presets.\d+.snapToLayer(.\d)*$/,
|
||||
];
|
||||
if (regexes.some((regex) => regex.exec(jsonPath))) {
|
||||
// We need to get the available layers
|
||||
const layers = await getAvailableLayers();
|
||||
console.log(`Got ${layers.length} layers`);
|
||||
|
@ -70,33 +81,38 @@ export const layerCompletionProvider =
|
|||
* JSON paths:
|
||||
* - layers.{index} (If this is a string)
|
||||
* - layers.{index}.builtin
|
||||
* - (layers.{index}.)presets.{index}.snapToLayer(.{index})
|
||||
*/
|
||||
export const layerDefinitionProvider =
|
||||
vscode.languages.registerDefinitionProvider(
|
||||
{
|
||||
language: "json",
|
||||
scheme: "file",
|
||||
pattern: "**/assets/themes/*/*.json",
|
||||
pattern: "**/assets/*/*/*.json",
|
||||
},
|
||||
{
|
||||
provideDefinition(
|
||||
document: vscode.TextDocument,
|
||||
position: vscode.Position
|
||||
) {
|
||||
console.log("layerDefinitionProvider");
|
||||
|
||||
// We don't want to provide definitions for the license_info.json file
|
||||
if (document.fileName.endsWith("license_info.json")) {
|
||||
return null;
|
||||
// Stop running if the file is called license_info.json
|
||||
if (document.fileName.includes("license_info")) {
|
||||
return [];
|
||||
}
|
||||
|
||||
console.log("layerDefinitionProvider");
|
||||
|
||||
const text = document.getText();
|
||||
const jsonPath = getCursorPath(text, position);
|
||||
const json = JSON.parse(text);
|
||||
const rawJsonPath = getRawCursorPath(text, position);
|
||||
|
||||
const regex = /^layers\.\d+(.builtin)*$/;
|
||||
if (regex.exec(jsonPath)) {
|
||||
const layer = json.layers[parseInt(jsonPath.split(".")[1])];
|
||||
const regexes = [
|
||||
/^layers\.\d+(.builtin)?$/,
|
||||
/^(layers.\d+.)?presets.\d+.snapToLayer(.\d)*$/,
|
||||
];
|
||||
if (regexes.some((regex) => regex.exec(jsonPath))) {
|
||||
console.log("Found reference to layer");
|
||||
const layer = getValueFromPath(text, rawJsonPath);
|
||||
// If an item is a string, it's a reference to a layer, also if it's an object and has a builtin property
|
||||
if (typeof layer === "string") {
|
||||
// We have a reference to a layer
|
||||
|
|
35
src/utils.ts
35
src/utils.ts
|
@ -152,3 +152,38 @@ export function getValueFromPath(json: string, path: JSONPath): any {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function to get the tagRenderings from the questions layer
|
||||
* //TODO: This should get ALL tagRenderings, not just from the questions layer
|
||||
*
|
||||
* @returns List of CompletionItems for tagRenderings
|
||||
*/
|
||||
export async function getTagRenderings(): Promise<vscode.CompletionItem[]> {
|
||||
const tagRenderings: vscode.CompletionItem[] = [];
|
||||
|
||||
// Open the questions layer file
|
||||
const questionsFile = await vscode.workspace.findFiles(
|
||||
"assets/layers/questions/questions.json",
|
||||
"**/node_modules/**"
|
||||
);
|
||||
|
||||
if (questionsFile.length === 0) {
|
||||
console.error("questions.json not found");
|
||||
return [];
|
||||
}
|
||||
|
||||
const content = await vscode.workspace.fs.readFile(questionsFile[0]);
|
||||
const questions = JSON.parse(new TextDecoder().decode(content));
|
||||
|
||||
for (const tagRendering of questions.tagRenderings) {
|
||||
tagRenderings.push(
|
||||
new vscode.CompletionItem(
|
||||
tagRendering.id,
|
||||
vscode.CompletionItemKind.Value
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return tagRenderings;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue