From 0f01b73c222d1aac5adc04ec7291cba67ea93506 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Sat, 4 Jan 2025 14:49:18 +0100 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Rework=20utils=20and=20col?= =?UTF-8?q?or?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 2 +- package.json | 2 +- src/generic.ts | 48 +- src/layers.ts | 11 +- src/license_info.ts | 8 +- src/theme.ts | 9 +- src/utils.ts | 233 -------- src/utils/color.ts | 1082 +++++++++++++++++++++++++++++++++++ src/utils/cursor.ts | 102 ++++ src/utils/json.ts | 40 ++ src/utils/mapcomplete.ts | 99 ++++ types/named-web-colors.d.ts | 0 12 files changed, 1367 insertions(+), 269 deletions(-) delete mode 100644 src/utils.ts create mode 100644 src/utils/color.ts create mode 100644 src/utils/cursor.ts create mode 100644 src/utils/json.ts create mode 100644 src/utils/mapcomplete.ts create mode 100644 types/named-web-colors.d.ts diff --git a/package-lock.json b/package-lock.json index f3eed10..3c919ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -667,7 +667,7 @@ "node": ">=7.0.0" } }, - "node_modules/color-name": { + "node_modules/color-convert/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", diff --git a/package.json b/package.json index 6438eba..4f770db 100644 --- a/package.json +++ b/package.json @@ -51,4 +51,4 @@ "colortranslator": "^4.1.0", "jsonc-parser": "^3.3.1" } -} +} \ No newline at end of file diff --git a/src/generic.ts b/src/generic.ts index fa4fb8f..1075c97 100644 --- a/src/generic.ts +++ b/src/generic.ts @@ -3,15 +3,11 @@ */ import * as vscode from "vscode"; -import { - getCursorPath, - getRawCursorPath, - getStartEnd, - getValueFromPath, - pathToJSONPath, -} from "./utils"; +import { getCursorPath, getRawCursorPath, getStartEnd } from "./utils/cursor"; +import { getValueFromPath, pathToJSONPath } from "./utils/json"; import { ColorTranslator } from "colortranslator"; import * as path from "path"; +import { findHexColor } from "./utils/color"; /** * Icon definition provider @@ -111,22 +107,46 @@ export const colorProvider = vscode.languages.registerColorProvider( provideColorPresentations(color, _context, _token) { console.log("colorProvider.provideColorPresentations"); - const colorHex = new ColorTranslator( + let outputColor: string; + + // Convert the color to a hex string + outputColor = new ColorTranslator( `rgba(${color.red * 255}, ${color.green * 255}, ${color.blue * 255}, ${ color.alpha })` ).HEXA; - // If the color is fully opaque (AKA it ends with FF), we can remove the alpha channel - const colorHexString = colorHex.endsWith("FF") - ? colorHex.substring(0, 7) - : colorHex; + console.log(`First output color: ${outputColor}`); - // TODO: Add color names, maybe convert to short-hand hex + // If the color is fully opaque (AKA it ends with FF), we can remove the alpha channel + outputColor = outputColor.endsWith("FF") + ? outputColor.substring(0, 7) + : outputColor; + + console.log(`Output color after alpha check: ${outputColor}`); + + // See if we can find a color name + const colorName = findHexColor(outputColor); + if (colorName) { + outputColor = colorName.name; + } + + console.log(`Output color after color name check: ${outputColor}`); + + // If we have a pair like #AABBCC, we can shorten that to #ABC + if ( + outputColor[1] === outputColor[2] && + outputColor[3] === outputColor[4] && + outputColor[5] === outputColor[6] + ) { + outputColor = `#${outputColor[1]}${outputColor[3]}${outputColor[5]}`; + } + + console.log(`Color after shortening: ${outputColor}`); return [ { - label: colorHexString, + label: outputColor, }, ]; }, diff --git a/src/layers.ts b/src/layers.ts index be94909..57867f7 100644 --- a/src/layers.ts +++ b/src/layers.ts @@ -9,14 +9,9 @@ */ import * as vscode from "vscode"; -import { - getCursorPath, - getFilters, - getRawCursorPath, - getStartEnd, - getTagRenderings, - getValueFromPath, -} from "./utils"; +import { getCursorPath, getRawCursorPath, getStartEnd } from "./utils/cursor"; +import { getFilters, getTagRenderings } from "./utils/mapcomplete"; +import { getValueFromPath } from "./utils/json"; import { JSONPath } from "jsonc-parser"; /** diff --git a/src/license_info.ts b/src/license_info.ts index 870f8a7..af745ab 100644 --- a/src/license_info.ts +++ b/src/license_info.ts @@ -8,12 +8,8 @@ import * as vscode from "vscode"; import * as path from "path"; -import { - getCursorPath, - getRawCursorPath, - getStartEnd, - getValueFromPath, -} from "./utils"; +import { getCursorPath, getRawCursorPath, getStartEnd } from "./utils/cursor"; +import { getValueFromPath } from "./utils/json"; /** * Path definition provider diff --git a/src/theme.ts b/src/theme.ts index 8f8739a..c8ed021 100644 --- a/src/theme.ts +++ b/src/theme.ts @@ -10,12 +10,9 @@ import * as vscode from "vscode"; import * as path from "path"; -import { - getAvailableLayers, - getCursorPath, - getRawCursorPath, - getValueFromPath, -} from "./utils"; +import { getCursorPath, getRawCursorPath } from "./utils/cursor"; +import { getAvailableLayers } from "./utils/mapcomplete"; +import { getValueFromPath } from "./utils/json"; /** * Layer completion provider diff --git a/src/utils.ts b/src/utils.ts deleted file mode 100644 index c1324e4..0000000 --- a/src/utils.ts +++ /dev/null @@ -1,233 +0,0 @@ -import * as vscode from "vscode"; -import * as path from "path"; -import { - findNodeAtLocation, - getLocation, - JSONPath, - parseTree, -} from "jsonc-parser"; - -/** - * Utility function to get the JSON path at the cursor position, separated by dots - * - * @param jsonText Original unparsed JSON text - * @param position VScode cursor position - * @returns JSON path as a string, separated by dots - */ -export function getCursorPath( - jsonText: string, - position: vscode.Position -): string { - return getRawCursorPath(jsonText, position).join("."); -} - -/** - * Utility function to get the JSON path at the cursor position - * - * @param jsonText Original unparsed JSON text - * @param position VScode cursor position - * @returns JSON path as an array of strings - */ -export function getRawCursorPath( - jsonText: string, - position: vscode.Position -): JSONPath { - const offset = positionToOffset(jsonText, position); - const location = getLocation(jsonText, offset); - return location.path; -} - -/** - * Utility function to convert a VScode position to a numeric offset - * - * @param text Original text content - * @param position VScode cursor position - * @returns Offset - */ -function positionToOffset(text: string, position: vscode.Position): number { - const lines = text.split("\n"); - let offset = 0; - for (let i = 0; i < position.line; i++) { - offset += lines[i].length + 1; // +1 for the newline character - } - offset += position.character; - return offset; -} - -/** - * Function to get all available layers on disk - * - * In essence, we look for folders under the assets/layers directory - * and return the names of the folders - * @returns List of layer names - */ -export async function getAvailableLayers(): Promise { - const layers: string[] = []; - let files = await vscode.workspace.findFiles( - "assets/layers/**/*.json", - "**/node_modules/**" - ); - - files = files.filter((file) => { - return !file.fsPath.includes("license_info"); - }); - - for (const file of files) { - const layerName = path.basename(path.dirname(file.fsPath)); - layers.push(layerName); - } - - return layers; -} - -/** - * Function to get a range of a JSON path - * Useful for creating document links - * - * @param json file content - * @param path JSON path - * @returns Range of the path - */ -export function getStartEnd(json: string, path: JSONPath): vscode.Range { - const rootNode = parseTree(json); - if (!rootNode) { - return new vscode.Range(0, 0, 0, 0); - } else { - const node = findNodeAtLocation(rootNode, path); - if (!node) { - return new vscode.Range(0, 0, 0, 0); - } else { - return new vscode.Range( - offsetToPosition(json, node.offset + 1), - offsetToPosition(json, node.offset + node.length - 1) - ); - } - } -} - -/** - * Utility function to convert an offset to a position - * - * @param text Text content - * @param offset Offset - * @returns Position - */ -function offsetToPosition(text: string, offset: number): vscode.Position { - const lines = text.split("\n"); - let currentOffset = 0; - for (let i = 0; i < lines.length; i++) { - if (currentOffset + lines[i].length + 1 >= offset) { - return new vscode.Position(i, offset - currentOffset); - } - currentOffset += lines[i].length + 1; - } - return new vscode.Position(0, 0); -} - -/** - * Utility function to get the value of a JSON path - * - * @param json Original JSON content - * @param path JSON path - * @returns Value of the path - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function getValueFromPath(json: string, path: JSONPath): any { - console.log("getValueFromPath", path); - const rootNode = parseTree(json); - if (!rootNode) { - console.log("Root node not found"); - return undefined; - } else { - console.log("Root node found"); - const node = findNodeAtLocation(rootNode, path); - if (!node) { - return undefined; - } else { - console.log( - "Substring", - json.substring(node.offset, node.offset + node.length) - ); - return JSON.parse(json.substring(node.offset, node.offset + node.length)); - } - } -} - -/** - * 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 { - 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; -} - -/** - * Utility function to get the filters from the filters layer - * //TODO: This should get ALL filters, not just from the filters layer - * - * @returns List of CompletionItems for tagRenderings - */ -export async function getFilters(): Promise { - const filtersList: vscode.CompletionItem[] = []; - - // Open the filters layer file - const filtersFile = await vscode.workspace.findFiles( - "assets/layers/filters/filters.json", - "**/node_modules/**" - ); - - if (filtersFile.length === 0) { - console.error("filters.json not found"); - return []; - } - - const content = await vscode.workspace.fs.readFile(filtersFile[0]); - const filters = JSON.parse(new TextDecoder().decode(content)); - - for (const filter of filters.filter) { - filtersList.push( - new vscode.CompletionItem(filter.id, vscode.CompletionItemKind.Value) - ); - } - - return filtersList; -} - -/** - * Utility function to convert a string path to a JSON path - * - * @param path String path, separated by dots - * @returns JSON path - */ -export function pathToJSONPath(path: string): JSONPath { - return path.split(".").map((str) => { - return isNaN(parseInt(str)) ? str : parseInt(str); - }); -} diff --git a/src/utils/color.ts b/src/utils/color.ts new file mode 100644 index 0000000..816c442 --- /dev/null +++ b/src/utils/color.ts @@ -0,0 +1,1082 @@ +/** + * Some utilities to works with colors, mainly color names + */ + +/** + * Representation of a color, contains hex and rgb values + */ +interface Color { + /** + * CSS color name + */ + name: string; + + /** + * Hex representation of the color + */ + hex: string; + + /** + * Red value (0-255) + */ + r: number; + + /** + * Green value (0-255) + */ + g: number; + + /** + * Blue value (0-255) + */ + b: number; +} + +/** + * Find a color by its hex value + */ +export function findHexColor(hex: string): Color | undefined { + return colors.find((color) => color.hex === hex.toLowerCase()); +} + +/** + * List of all named colors + */ +const colors: Color[] = [ + { + name: "aliceblue", + hex: "#f0f8ff", + r: 240, + g: 248, + b: 255, + }, + { + name: "antiquewhite", + hex: "#faebd7", + r: 250, + g: 235, + b: 215, + }, + { + name: "aqua", + hex: "#00ffff", + r: 0, + g: 255, + b: 255, + }, + { + name: "aquamarine", + hex: "#7fffd4", + r: 127, + g: 255, + b: 212, + }, + { + name: "azure", + hex: "#f0ffff", + r: 240, + g: 255, + b: 255, + }, + { + name: "beige", + hex: "#f5f5dc", + r: 245, + g: 245, + b: 220, + }, + { + name: "bisque", + hex: "#ffe4c4", + r: 255, + g: 228, + b: 196, + }, + { + name: "black", + hex: "#000000", + r: 0, + g: 0, + b: 0, + }, + { + name: "blanchedalmond", + hex: "#ffebcd", + r: 255, + g: 235, + b: 205, + }, + { + name: "blue", + hex: "#0000ff", + r: 0, + g: 0, + b: 255, + }, + { + name: "blueviolet", + hex: "#8a2be2", + r: 138, + g: 43, + b: 226, + }, + { + name: "brown", + hex: "#a52a2a", + r: 165, + g: 42, + b: 42, + }, + { + name: "burlywood", + hex: "#deb887", + r: 222, + g: 184, + b: 135, + }, + { + name: "cadetblue", + hex: "#5f9ea0", + r: 95, + g: 158, + b: 160, + }, + { + name: "chartreuse", + hex: "#7fff00", + r: 127, + g: 255, + b: 0, + }, + { + name: "chocolate", + hex: "#d2691e", + r: 210, + g: 105, + b: 30, + }, + { + name: "coral", + hex: "#ff7f50", + r: 255, + g: 127, + b: 80, + }, + { + name: "cornflowerblue", + hex: "#6495ed", + r: 100, + g: 149, + b: 237, + }, + { + name: "cornsilk", + hex: "#fff8dc", + r: 255, + g: 248, + b: 220, + }, + { + name: "crimson", + hex: "#dc143c", + r: 220, + g: 20, + b: 60, + }, + { + name: "cyan", + hex: "#00ffff", + r: 0, + g: 255, + b: 255, + }, + { + name: "darkblue", + hex: "#00008b", + r: 0, + g: 0, + b: 139, + }, + { + name: "darkcyan", + hex: "#008b8b", + r: 0, + g: 139, + b: 139, + }, + { + name: "darkgoldenrod", + hex: "#b8860b", + r: 184, + g: 134, + b: 11, + }, + { + name: "darkgray", + hex: "#a9a9a9", + r: 169, + g: 169, + b: 169, + }, + { + name: "darkgreen", + hex: "#006400", + r: 0, + g: 100, + b: 0, + }, + { + name: "darkgrey", + hex: "#a9a9a9", + r: 169, + g: 169, + b: 169, + }, + { + name: "darkkhaki", + hex: "#bdb76b", + r: 189, + g: 183, + b: 107, + }, + { + name: "darkmagenta", + hex: "#8b008b", + r: 139, + g: 0, + b: 139, + }, + { + name: "darkolivegreen", + hex: "#556b2f", + r: 85, + g: 107, + b: 47, + }, + { + name: "darkorange", + hex: "#ff8c00", + r: 255, + g: 140, + b: 0, + }, + { + name: "darkorchid", + hex: "#9932cc", + r: 153, + g: 50, + b: 204, + }, + { + name: "darkred", + hex: "#8b0000", + r: 139, + g: 0, + b: 0, + }, + { + name: "darksalmon", + hex: "#e9967a", + r: 233, + g: 150, + b: 122, + }, + { + name: "darkseagreen", + hex: "#8fbc8f", + r: 143, + g: 188, + b: 143, + }, + { + name: "darkslateblue", + hex: "#483d8b", + r: 72, + g: 61, + b: 139, + }, + { + name: "darkslategray", + hex: "#2f4f4f", + r: 47, + g: 79, + b: 79, + }, + { + name: "darkslategrey", + hex: "#2f4f4f", + r: 47, + g: 79, + b: 79, + }, + { + name: "darkturquoise", + hex: "#00ced1", + r: 0, + g: 206, + b: 209, + }, + { + name: "darkviolet", + hex: "#9400d3", + r: 148, + g: 0, + b: 211, + }, + { + name: "deeppink", + hex: "#ff1493", + r: 255, + g: 20, + b: 147, + }, + { + name: "deepskyblue", + hex: "#00bfff", + r: 0, + g: 191, + b: 255, + }, + { + name: "dimgray", + hex: "#696969", + r: 105, + g: 105, + b: 105, + }, + { + name: "dimgrey", + hex: "#696969", + r: 105, + g: 105, + b: 105, + }, + { + name: "dodgerblue", + hex: "#1e90ff", + r: 30, + g: 144, + b: 255, + }, + { + name: "firebrick", + hex: "#b22222", + r: 178, + g: 34, + b: 34, + }, + { + name: "floralwhite", + hex: "#fffaf0", + r: 255, + g: 250, + b: 240, + }, + { + name: "forestgreen", + hex: "#228b22", + r: 34, + g: 139, + b: 34, + }, + { + name: "fuchsia", + hex: "#ff00ff", + r: 255, + g: 0, + b: 255, + }, + { + name: "gainsboro", + hex: "#dcdcdc", + r: 220, + g: 220, + b: 220, + }, + { + name: "ghostwhite", + hex: "#f8f8ff", + r: 248, + g: 248, + b: 255, + }, + { + name: "gold", + hex: "#ffd700", + r: 255, + g: 215, + b: 0, + }, + { + name: "goldenrod", + hex: "#daa520", + r: 218, + g: 165, + b: 32, + }, + { + name: "gray", + hex: "#808080", + r: 128, + g: 128, + b: 128, + }, + { + name: "green", + hex: "#008000", + r: 0, + g: 128, + b: 0, + }, + { + name: "greenyellow", + hex: "#adff2f", + r: 173, + g: 255, + b: 47, + }, + { + name: "grey", + hex: "#808080", + r: 128, + g: 128, + b: 128, + }, + { + name: "honeydew", + hex: "#f0fff0", + r: 240, + g: 255, + b: 240, + }, + { + name: "hotpink", + hex: "#ff69b4", + r: 255, + g: 105, + b: 180, + }, + { + name: "indianred", + hex: "#cd5c5c", + r: 205, + g: 92, + b: 92, + }, + { + name: "indigo", + hex: "#4b0082", + r: 75, + g: 0, + b: 130, + }, + { + name: "ivory", + hex: "#fffff0", + r: 255, + g: 255, + b: 240, + }, + { + name: "khaki", + hex: "#f0e68c", + r: 240, + g: 230, + b: 140, + }, + { + name: "lavender", + hex: "#e6e6fa", + r: 230, + g: 230, + b: 250, + }, + { + name: "lavenderblush", + hex: "#fff0f5", + r: 255, + g: 240, + b: 245, + }, + { + name: "lawngreen", + hex: "#7cfc00", + r: 124, + g: 252, + b: 0, + }, + { + name: "lemonchiffon", + hex: "#fffacd", + r: 255, + g: 250, + b: 205, + }, + { + name: "lightblue", + hex: "#add8e6", + r: 173, + g: 216, + b: 230, + }, + { + name: "lightcoral", + hex: "#f08080", + r: 240, + g: 128, + b: 128, + }, + { + name: "lightcyan", + hex: "#e0ffff", + r: 224, + g: 255, + b: 255, + }, + { + name: "lightgoldenrodyellow", + hex: "#fafad2", + r: 250, + g: 250, + b: 210, + }, + { + name: "lightgray", + hex: "#d3d3d3", + r: 211, + g: 211, + b: 211, + }, + { + name: "lightgreen", + hex: "#90ee90", + r: 144, + g: 238, + b: 144, + }, + { + name: "lightgrey", + hex: "#d3d3d3", + r: 211, + g: 211, + b: 211, + }, + { + name: "lightpink", + hex: "#ffb6c1", + r: 255, + g: 182, + b: 193, + }, + { + name: "lightsalmon", + hex: "#ffa07a", + r: 255, + g: 160, + b: 122, + }, + { + name: "lightseagreen", + hex: "#20b2aa", + r: 32, + g: 178, + b: 170, + }, + { + name: "lightskyblue", + hex: "#87cefa", + r: 135, + g: 206, + b: 250, + }, + { + name: "lightslategray", + hex: "#778899", + r: 119, + g: 136, + b: 153, + }, + { + name: "lightslategrey", + hex: "#778899", + r: 119, + g: 136, + b: 153, + }, + { + name: "lightsteelblue", + hex: "#b0c4de", + r: 176, + g: 196, + b: 222, + }, + { + name: "lightyellow", + hex: "#ffffe0", + r: 255, + g: 255, + b: 224, + }, + { + name: "lime", + hex: "#00ff00", + r: 0, + g: 255, + b: 0, + }, + { + name: "limegreen", + hex: "#32cd32", + r: 50, + g: 205, + b: 50, + }, + { + name: "linen", + hex: "#faf0e6", + r: 250, + g: 240, + b: 230, + }, + { + name: "magenta", + hex: "#ff00ff", + r: 255, + g: 0, + b: 255, + }, + { + name: "maroon", + hex: "#800000", + r: 128, + g: 0, + b: 0, + }, + { + name: "mediumaquamarine", + hex: "#66cdaa", + r: 102, + g: 205, + b: 170, + }, + { + name: "mediumblue", + hex: "#0000cd", + r: 0, + g: 0, + b: 205, + }, + { + name: "mediumorchid", + hex: "#ba55d3", + r: 186, + g: 85, + b: 211, + }, + { + name: "mediumpurple", + hex: "#9370db", + r: 147, + g: 112, + b: 219, + }, + { + name: "mediumseagreen", + hex: "#3cb371", + r: 60, + g: 179, + b: 113, + }, + { + name: "mediumslateblue", + hex: "#7b68ee", + r: 123, + g: 104, + b: 238, + }, + { + name: "mediumspringgreen", + hex: "#00fa9a", + r: 0, + g: 250, + b: 154, + }, + { + name: "mediumturquoise", + hex: "#48d1cc", + r: 72, + g: 209, + b: 204, + }, + { + name: "mediumvioletred", + hex: "#c71585", + r: 199, + g: 21, + b: 133, + }, + { + name: "midnightblue", + hex: "#191970", + r: 25, + g: 25, + b: 112, + }, + { + name: "mintcream", + hex: "#f5fffa", + r: 245, + g: 255, + b: 250, + }, + { + name: "mistyrose", + hex: "#ffe4e1", + r: 255, + g: 228, + b: 225, + }, + { + name: "moccasin", + hex: "#ffe4b5", + r: 255, + g: 228, + b: 181, + }, + { + name: "navajowhite", + hex: "#ffdead", + r: 255, + g: 222, + b: 173, + }, + { + name: "navy", + hex: "#000080", + r: 0, + g: 0, + b: 128, + }, + { + name: "oldlace", + hex: "#fdf5e6", + r: 253, + g: 245, + b: 230, + }, + { + name: "olive", + hex: "#808000", + r: 128, + g: 128, + b: 0, + }, + { + name: "olivedrab", + hex: "#6b8e23", + r: 107, + g: 142, + b: 35, + }, + { + name: "orange", + hex: "#ffa500", + r: 255, + g: 165, + b: 0, + }, + { + name: "orangered", + hex: "#ff4500", + r: 255, + g: 69, + b: 0, + }, + { + name: "orchid", + hex: "#da70d6", + r: 218, + g: 112, + b: 214, + }, + { + name: "palegoldenrod", + hex: "#eee8aa", + r: 238, + g: 232, + b: 170, + }, + { + name: "palegreen", + hex: "#98fb98", + r: 152, + g: 251, + b: 152, + }, + { + name: "paleturquoise", + hex: "#afeeee", + r: 175, + g: 238, + b: 238, + }, + { + name: "palevioletred", + hex: "#db7093", + r: 219, + g: 112, + b: 147, + }, + { + name: "papayawhip", + hex: "#ffefd5", + r: 255, + g: 239, + b: 213, + }, + { + name: "peachpuff", + hex: "#ffdab9", + r: 255, + g: 218, + b: 185, + }, + { + name: "peru", + hex: "#cd853f", + r: 205, + g: 133, + b: 63, + }, + { + name: "pink", + hex: "#ffc0cb", + r: 255, + g: 192, + b: 203, + }, + { + name: "plum", + hex: "#dda0dd", + r: 221, + g: 160, + b: 221, + }, + { + name: "powderblue", + hex: "#b0e0e6", + r: 176, + g: 224, + b: 230, + }, + { + name: "purple", + hex: "#800080", + r: 128, + g: 0, + b: 128, + }, + { + name: "rebeccapurple", + hex: "#663399", + r: 102, + g: 51, + b: 153, + }, + { + name: "red", + hex: "#ff0000", + r: 255, + g: 0, + b: 0, + }, + { + name: "rosybrown", + hex: "#bc8f8f", + r: 188, + g: 143, + b: 143, + }, + { + name: "royalblue", + hex: "#4169e1", + r: 65, + g: 105, + b: 225, + }, + { + name: "saddlebrown", + hex: "#8b4513", + r: 139, + g: 69, + b: 19, + }, + { + name: "salmon", + hex: "#fa8072", + r: 250, + g: 128, + b: 114, + }, + { + name: "sandybrown", + hex: "#f4a460", + r: 244, + g: 164, + b: 96, + }, + { + name: "seagreen", + hex: "#2e8b57", + r: 46, + g: 139, + b: 87, + }, + { + name: "seashell", + hex: "#fff5ee", + r: 255, + g: 245, + b: 238, + }, + { + name: "sienna", + hex: "#a0522d", + r: 160, + g: 82, + b: 45, + }, + { + name: "silver", + hex: "#c0c0c0", + r: 192, + g: 192, + b: 192, + }, + { + name: "skyblue", + hex: "#87ceeb", + r: 135, + g: 206, + b: 235, + }, + { + name: "slateblue", + hex: "#6a5acd", + r: 106, + g: 90, + b: 205, + }, + { + name: "slategray", + hex: "#708090", + r: 112, + g: 128, + b: 144, + }, + { + name: "slategrey", + hex: "#708090", + r: 112, + g: 128, + b: 144, + }, + { + name: "snow", + hex: "#fffafa", + r: 255, + g: 250, + b: 250, + }, + { + name: "springgreen", + hex: "#00ff7f", + r: 0, + g: 255, + b: 127, + }, + { + name: "steelblue", + hex: "#4682b4", + r: 70, + g: 130, + b: 180, + }, + { + name: "tan", + hex: "#d2b48c", + r: 210, + g: 180, + b: 140, + }, + { + name: "teal", + hex: "#008080", + r: 0, + g: 128, + b: 128, + }, + { + name: "thistle", + hex: "#d8bfd8", + r: 216, + g: 191, + b: 216, + }, + { + name: "tomato", + hex: "#ff6347", + r: 255, + g: 99, + b: 71, + }, + { + name: "turquoise", + hex: "#40e0d0", + r: 64, + g: 224, + b: 208, + }, + { + name: "violet", + hex: "#ee82ee", + r: 238, + g: 130, + b: 238, + }, + { + name: "wheat", + hex: "#f5deb3", + r: 245, + g: 222, + b: 179, + }, + { + name: "white", + hex: "#ffffff", + r: 255, + g: 255, + b: 255, + }, + { + name: "whitesmoke", + hex: "#f5f5f5", + r: 245, + g: 245, + b: 245, + }, + { + name: "yellow", + hex: "#ffff00", + r: 255, + g: 255, + b: 0, + }, + { + name: "yellowgreen", + hex: "#9acd32", + r: 154, + g: 205, + b: 50, + }, +]; diff --git a/src/utils/cursor.ts b/src/utils/cursor.ts new file mode 100644 index 0000000..dc2ca40 --- /dev/null +++ b/src/utils/cursor.ts @@ -0,0 +1,102 @@ +/** + * Utility functions related to vscode cursor position and JSON paths + */ + +import * as vscode from "vscode"; +import { + findNodeAtLocation, + getLocation, + JSONPath, + parseTree, +} from "jsonc-parser"; + +/** + * Utility function to get the JSON path at the cursor position, separated by dots + * + * @param jsonText Original unparsed JSON text + * @param position VScode cursor position + * @returns JSON path as a string, separated by dots + */ +export function getCursorPath( + jsonText: string, + position: vscode.Position +): string { + return getRawCursorPath(jsonText, position).join("."); +} + +/** + * Utility function to get the JSON path at the cursor position + * + * @param jsonText Original unparsed JSON text + * @param position VScode cursor position + * @returns JSON path as an array of strings + */ +export function getRawCursorPath( + jsonText: string, + position: vscode.Position +): JSONPath { + const offset = positionToOffset(jsonText, position); + const location = getLocation(jsonText, offset); + return location.path; +} + +/** + * Utility function to convert a VScode position to a numeric offset + * + * @param text Original text content + * @param position VScode cursor position + * @returns Offset + */ +function positionToOffset(text: string, position: vscode.Position): number { + const lines = text.split("\n"); + let offset = 0; + for (let i = 0; i < position.line; i++) { + offset += lines[i].length + 1; // +1 for the newline character + } + offset += position.character; + return offset; +} + +/** + * Utility function to convert an offset to a position + * + * @param text Text content + * @param offset Offset + * @returns Position + */ +function offsetToPosition(text: string, offset: number): vscode.Position { + const lines = text.split("\n"); + let currentOffset = 0; + for (let i = 0; i < lines.length; i++) { + if (currentOffset + lines[i].length + 1 >= offset) { + return new vscode.Position(i, offset - currentOffset); + } + currentOffset += lines[i].length + 1; + } + return new vscode.Position(0, 0); +} + +/** + * Function to get a range of a JSON path + * Useful for creating document links + * + * @param json file content + * @param path JSON path + * @returns Range of the path + */ +export function getStartEnd(json: string, path: JSONPath): vscode.Range { + const rootNode = parseTree(json); + if (!rootNode) { + return new vscode.Range(0, 0, 0, 0); + } else { + const node = findNodeAtLocation(rootNode, path); + if (!node) { + return new vscode.Range(0, 0, 0, 0); + } else { + return new vscode.Range( + offsetToPosition(json, node.offset + 1), + offsetToPosition(json, node.offset + node.length - 1) + ); + } + } +} diff --git a/src/utils/json.ts b/src/utils/json.ts new file mode 100644 index 0000000..5c4f838 --- /dev/null +++ b/src/utils/json.ts @@ -0,0 +1,40 @@ +/** + * Utility functions to work with JSON content and paths + */ + +import { JSONPath, findNodeAtLocation, parseTree } from "jsonc-parser"; + +/** + * Utility function to get the value of a JSON path + * + * @param json Original JSON content + * @param path JSON path + * @returns Value of the path + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function getValueFromPath(json: string, path: JSONPath): any { + console.log("getValueFromPath", path); + const rootNode = parseTree(json); + if (!rootNode) { + return undefined; + } else { + const node = findNodeAtLocation(rootNode, path); + if (!node) { + return undefined; + } else { + return JSON.parse(json.substring(node.offset, node.offset + node.length)); + } + } +} + +/** + * Utility function to convert a string path to a JSON path + * + * @param path String path, separated by dots + * @returns JSON path + */ +export function pathToJSONPath(path: string): JSONPath { + return path.split(".").map((str) => { + return isNaN(parseInt(str)) ? str : parseInt(str); + }); +} diff --git a/src/utils/mapcomplete.ts b/src/utils/mapcomplete.ts new file mode 100644 index 0000000..2abf840 --- /dev/null +++ b/src/utils/mapcomplete.ts @@ -0,0 +1,99 @@ +/** + * This file contains some function related to interaction with MapComplete data in the editor + */ + +import * as vscode from "vscode"; +import * as path from "path"; + +/** + * Function to get all available layers on disk + * + * In essence, we look for folders under the assets/layers directory + * and return the names of the folders + * @returns List of layer names + */ +export async function getAvailableLayers(): Promise { + const layers: string[] = []; + let files = await vscode.workspace.findFiles( + "assets/layers/**/*.json", + "**/node_modules/**" + ); + + files = files.filter((file) => { + return !file.fsPath.includes("license_info"); + }); + + for (const file of files) { + const layerName = path.basename(path.dirname(file.fsPath)); + layers.push(layerName); + } + + return layers; +} + +/** + * 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 { + 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; +} + +/** + * Utility function to get the filters from the filters layer + * //TODO: This should get ALL filters, not just from the filters layer + * + * @returns List of CompletionItems for tagRenderings + */ +export async function getFilters(): Promise { + const filtersList: vscode.CompletionItem[] = []; + + // Open the filters layer file + const filtersFile = await vscode.workspace.findFiles( + "assets/layers/filters/filters.json", + "**/node_modules/**" + ); + + if (filtersFile.length === 0) { + console.error("filters.json not found"); + return []; + } + + const content = await vscode.workspace.fs.readFile(filtersFile[0]); + const filters = JSON.parse(new TextDecoder().decode(content)); + + for (const filter of filters.filter) { + filtersList.push( + new vscode.CompletionItem(filter.id, vscode.CompletionItemKind.Value) + ); + } + + return filtersList; +} diff --git a/types/named-web-colors.d.ts b/types/named-web-colors.d.ts new file mode 100644 index 0000000..e69de29