♻️ Rework utils and color
This commit is contained in:
parent
6457bfa63d
commit
0f01b73c22
12 changed files with 1367 additions and 269 deletions
2
package-lock.json
generated
2
package-lock.json
generated
|
@ -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==",
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
];
|
||||
},
|
||||
|
|
|
@ -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";
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
233
src/utils.ts
233
src/utils.ts
|
@ -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<string[]> {
|
||||
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<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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<vscode.CompletionItem[]> {
|
||||
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);
|
||||
});
|
||||
}
|
1082
src/utils/color.ts
Normal file
1082
src/utils/color.ts
Normal file
File diff suppressed because it is too large
Load diff
102
src/utils/cursor.ts
Normal file
102
src/utils/cursor.ts
Normal file
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
40
src/utils/json.ts
Normal file
40
src/utils/json.ts
Normal file
|
@ -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);
|
||||
});
|
||||
}
|
99
src/utils/mapcomplete.ts
Normal file
99
src/utils/mapcomplete.ts
Normal file
|
@ -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<string[]> {
|
||||
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<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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<vscode.CompletionItem[]> {
|
||||
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;
|
||||
}
|
0
types/named-web-colors.d.ts
vendored
Normal file
0
types/named-web-colors.d.ts
vendored
Normal file
Loading…
Reference in a new issue