Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 165 additions & 0 deletions language-server/0001-feat-support-sublime-text-colors.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
From 5c483dfc8bfc74e9cfdc296747816cd4a572be17 Mon Sep 17 00:00:00 2001
From: Rafal Chlodnicki <rchl2k@gmail.com>
Date: Sun, 8 Feb 2026 17:13:58 +0100
Subject: [PATCH] feat: support sublime text colors

---
src/services/jsonDocumentSymbols.ts | 4 +-
src/utils/colors.ts | 121 +++++++++++++++++++++++++++-
2 files changed, 122 insertions(+), 3 deletions(-)

diff --git a/src/services/jsonDocumentSymbols.ts b/src/services/jsonDocumentSymbols.ts
index e3b752c..1ceb772 100644
--- a/src/services/jsonDocumentSymbols.ts
+++ b/src/services/jsonDocumentSymbols.ts
@@ -5,7 +5,7 @@

import * as Parser from '../parser/jsonParser';
import * as Strings from '../utils/strings';
-import { colorFromHex } from '../utils/colors';
+import { tryParseColor } from '../utils/colors';
import * as l10n from '@vscode/l10n';

import {
@@ -247,7 +247,7 @@ export class JSONDocumentSymbols {
if (!s.inverted && s.schema && (s.schema.format === 'color' || s.schema.format === 'color-hex') && s.node && s.node.type === 'string') {
const nodeId = String(s.node.offset);
if (!visitedNode[nodeId]) {
- const color = colorFromHex(Parser.getNodeValue(s.node));
+ const color = tryParseColor(Parser.getNodeValue(s.node));
if (color) {
const range = getRange(document, s.node);
result.push({ color, range });
diff --git a/src/utils/colors.ts b/src/utils/colors.ts
index 1d71060..3c4adef 100644
--- a/src/utils/colors.ts
+++ b/src/utils/colors.ts
@@ -71,4 +71,123 @@ export function colorFrom256RGB(red: number, green: number, blue: number, alpha:
blue: blue / 255.0,
alpha
};
-}
\ No newline at end of file
+}
+
+/**
+ * Assumes H is in the range 0-360, and S, L are in the range 0-1.
+ */
+function colorFromHSL(h: number, s: number, l: number, alpha: number = 1.0): Color {
+ s = Math.max(0, Math.min(1, s)); // Saturation should be in [0, 1]
+ l = Math.max(0, Math.min(1, l)); // Lightness should be in [0, 1]
+
+ const c = (1 - Math.abs(2 * l - 1)) * s; // Chroma
+ const x = c * (1 - Math.abs((h / 60) % 2 - 1)); // Second largest component
+ const m = l - c / 2;
+
+ let r = 0, g = 0, b = 0;
+
+ if (h < 60) {
+ r = c; g = x; b = 0;
+ } else if (h < 120) {
+ r = x; g = c; b = 0;
+ } else if (h < 180) {
+ r = 0; g = c; b = x;
+ } else if (h < 240) {
+ r = 0; g = x; b = c;
+ } else if (h < 300) {
+ r = x; g = 0; b = c;
+ } else {
+ r = c; g = 0; b = x;
+ }
+
+ return {
+ red: r + m,
+ green: g + m,
+ blue: b + m,
+ alpha,
+ };
+}
+
+/**
+ * Assumes H is in the range 0-360, and W, B are in the range 0-1.
+ */
+function colorFromHWB(h: number, w: number, b: number, alpha: number = 1.0): Color {
+ w = Math.max(0, Math.min(1, w)); // Saturation should be in [0, 1]
+ b = Math.max(0, Math.min(1, b)); // Lightness should be in [0, 1]
+
+ if (w + b >= 1) {
+ let gray = w / (w + b);
+ let rgbValue = Math.round(gray * 255);
+ return {
+ red: rgbValue,
+ green: rgbValue,
+ blue: rgbValue,
+ alpha,
+ }
+ }
+
+ // Convert HWB to an intermediate HSL for calculation.
+ // The saturation and lightness calculation here is a common way to bridge models.
+ let lightness = w + (1 - b - w) / 2;
+ let saturation = (1 - b - w === 0) ? 0 : (lightness === 0 || lightness === 1) ? 0 : (1 - w - b) / (1 - Math.abs(2 * lightness - 1));
+ return colorFromHSL(h, saturation, lightness, alpha);
+}
+
+const RE_ST_COLOR_HEX = /^\s*color\(\s*(?<hex>#[a-zA-Z0-9]+)(?:\s+alpha\(\s*(?<a>[\d.]+)\s*\)\s*\))/
+const RE_ST_COLOR_RGB = /^\s*color\(\s*rgb\(\s*(?<r>\d+)\s*,\s*(?<g>\d+)\s*,\s*(?<b>\d+)\s*\)(?:\s+alpha\(\s*(?<a>[\d.]+)\s*\)\s*\))/
+const RE_ST_COLOR_HSL = /^\s*color\(\s*hsl\(\s*(?<h>\d+)\s*,\s*(?<s>\d+%)\s*,\s*(?<l>\d+%)\s*\)(?:\s+alpha\(\s*(?<a>[\d.]+)\s*\)\s*\))/
+const RE_ST_COLOR_HWB = /^\s*color\(\s*hwb\(\s*(?<h>\d+)\s*,\s*(?<w>\d+%)\s*,\s*(?<b>\d+%)\s*\)(?:\s+alpha\(\s*(?<a>[\d.]+)\s*\)\s*\))/
+const RE_ST_RGB = /^\s*rgba?\(\s*(?<r>\d+)\s*,\s*(?<g>\d+)\s*,\s*(?<b>\d+)\s*(?:,\s*(?<a>[\d.]+))?\s*\)/;
+const RE_ST_HSL = /^\s*hsla?\(\s*(?<h>\d+)\s*,\s*(?<s>\d+%)\s*,\s*(?<l>\d+%)\s*(?:,\s*(?<a>[\d.]+))?\s*\)/;
+const RE_ST_HWB = /^\s*hwb\(\s*(?<h>\d+)\s*,\s*(?<w>\d+%)\s*,\s*(?<b>\d+%)\s*(?:,\s*(?<a>[\d.]+))?\s*\)/;
+
+export function tryParseColor(text: string): Color | undefined {
+ let color = colorFromHex(text);
+ if (color) {
+ return color
+ }
+
+ let m = RE_ST_COLOR_HEX.exec(text)
+ if (m?.groups) {
+ const hex = m.groups.hex
+ let a = ''
+ if (m.groups.a) {
+ const alpha = Number.parseFloat(m.groups.a)
+ if (hex.length === 4) {
+ a = Math.round(alpha * 15.0).toString(16)
+ } else {
+ a = Math.round(alpha * 255.0).toString(16).padStart(2, '0')
+ }
+ }
+ return colorFromHex(`${hex}${a}`)
+ }
+
+ m = RE_ST_COLOR_RGB.exec(text) || RE_ST_RGB.exec(text)
+ if (m?.groups) {
+ const r = Number.parseInt(m.groups.r)
+ const g = Number.parseInt(m.groups.g)
+ const b = Number.parseInt(m.groups.b)
+ const a = m.groups.a ? Number.parseFloat(m.groups.a) : undefined
+ return colorFrom256RGB(r, g, b, a)
+ }
+
+ m = RE_ST_COLOR_HSL.exec(text) || RE_ST_HSL.exec(text)
+ if (m?.groups) {
+ const h = Number.parseInt(m.groups.h)
+ const s = Number.parseInt(m.groups.s) / 100
+ const l = Number.parseInt(m.groups.l) / 100
+ const a = m.groups.a ? Number.parseFloat(m.groups.a) : undefined
+ return colorFromHSL(h, s, l, a)
+ }
+
+ m = RE_ST_COLOR_HWB.exec(text) || RE_ST_HWB.exec(text)
+ if (m?.groups) {
+ const h = Number.parseInt(m.groups.h)
+ const w = Number.parseInt(m.groups.w) / 100
+ const b = Number.parseInt(m.groups.b) / 100
+ const a = m.groups.a ? Number.parseFloat(m.groups.a) : undefined
+ return colorFromHWB(h, w, b, a)
+ }
+
+ return undefined
+}
--
2.50.1 (Apple Git-155)

6 changes: 0 additions & 6 deletions language-server/bin/vscode-json-languageserver

This file was deleted.

74 changes: 60 additions & 14 deletions language-server/compile-language-server.sh
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
#!/usr/bin/env bash

GITHUB_REPO_URL="https://github.com/microsoft/vscode"
GITHUB_REPO_NAME=$(echo "${GITHUB_REPO_URL}" | command grep -oE '[^/]*$')
VSCODE_REPO_URL="https://github.com/microsoft/vscode"
JSON_SERVICE_REPO_URL="https://github.com/microsoft/vscode-json-languageservice"

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
REPO_DIR="${SCRIPT_DIR}"
CLONED_REPO_DIR="${REPO_DIR}/temp"
SRC_SERVER_DIR="${CLONED_REPO_DIR}/extensions/json-language-features/server/"
CLONED_VSCODE_DIR="${REPO_DIR}/temp"
SRC_SERVER_DIR="${CLONED_VSCODE_DIR}/extensions/json-language-features/server/"
CLONED_JSON_SERVICE_DIR="${REPO_DIR}/temp2"

echo "Your node version: $(node --version || echo '<missing>')"
read -rp "You need at least version 22 of Node. Exit the script if it doesn't match requirements. Otherwise press enter."
Expand All @@ -17,14 +18,13 @@ read -rp "You need at least version 22 of Node. Exit the script if it doesn't ma

pushd "${REPO_DIR}" || exit

rm -rf out package-lock.json package.json update-info.log
rm -rf out package-lock.json package.json update-info.log *.tgz "${CLONED_VSCODE_DIR}" "${CLONED_JSON_SERVICE_DIR}"

popd || exit


# ---------------- #
# clone repo #
# ---------------- #
# ------------ #
# clone vscode #
# ------------ #

pushd "${REPO_DIR}" || exit

Expand All @@ -35,8 +35,8 @@ if [ "${ref}" = "" ]; then
ref="main"
fi

echo "Cloning ${GITHUB_REPO_URL}"
git clone ${GITHUB_REPO_URL} --branch ${ref} --single-branch "${CLONED_REPO_DIR}" || echo "Repo already cloned. Continuing..."
echo "Cloning ${VSCODE_REPO_URL}"
git clone ${VSCODE_REPO_URL} --branch ${ref} --single-branch "${CLONED_VSCODE_DIR}" || echo "Repo already cloned. Continuing..."
current_sha=$( git rev-parse HEAD )
printf "ref: %s\n%s\n" "$ref" "$current_sha" > update-info.log

Expand All @@ -46,7 +46,7 @@ popd || exit
# prepare deps #
# ------------ #

pushd "${CLONED_REPO_DIR}" || exit
pushd "${CLONED_VSCODE_DIR}" || exit

echo 'Installing dependencies...'
npm i
Expand All @@ -59,6 +59,9 @@ popd || exit

pushd "${SRC_SERVER_DIR}" || exit

# Get exact version of vscode-json-languageservice
json_service_version=$(npm ls --json --depth=0 vscode-json-languageservice | jq '.dependencies["vscode-json-languageservice"].version' --raw-output) || exit

echo 'Compiling server...'
npm run compile

Expand All @@ -72,8 +75,51 @@ pushd "${SRC_SERVER_DIR}" || exit

echo 'Copying and cleaning up files...'
find ./out -name "*.map" -delete
cp -r bin out package.json README.md "${REPO_DIR}"
rm -rf "${CLONED_REPO_DIR}"
cp -r out package.json README.md "${REPO_DIR}"
rm -rf "${CLONED_VSCODE_DIR}"

popd || exit

# ------------------ #
# clone json service #
# ------------------ #

pushd "${REPO_DIR}" || exit

echo "Cloning ${JSON_SERVICE_REPO_URL}"
git clone ${JSON_SERVICE_REPO_URL} --branch "v${json_service_version}" --single-branch "${CLONED_JSON_SERVICE_DIR}" || echo "Repo already cloned. Continuing..."

popd || exit

# -------------------- #
# prepare json service #
# -------------------- #

pushd "${CLONED_JSON_SERVICE_DIR}" || exit

# Add support for sublime colors (implementation in https://github.com/rchl/vscode-json-languageservice/tree/feat/st-colors)
git apply "${REPO_DIR}/0001-feat-support-sublime-text-colors.patch" || exit

echo 'Installing dependencies...'
npm i || exit
pack_output=$(npm --silent --foreground-scripts=false pack --json --no-color --pack-destination "${REPO_DIR}" || exit)
archive_name=$(echo "$pack_output" | jq '.[0].filename' --raw-output) || exit

popd || exit

# -------------------------------- #
# override json service dependency #
# -------------------------------- #

pushd "${REPO_DIR}" || exit

rm -rf "${CLONED_JSON_SERVICE_DIR}"

# Override vscode-json-languageservice dependency with local one
jq ".dependencies[\"vscode-json-languageservice\"] = \"file:${archive_name}\"" package.json > temp.json || exit
mv temp.json package.json || exit

popd || exit

# ---------------- #
# Update lock file #
Expand Down
11 changes: 5 additions & 6 deletions language-server/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion language-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"@vscode/l10n": "^0.0.18",
"jsonc-parser": "^3.3.1",
"request-light": "^0.8.0",
"vscode-json-languageservice": "^5.7.1",
"vscode-json-languageservice": "file:vscode-json-languageservice-5.7.1.tgz",
"vscode-languageserver": "^10.0.0-next.15",
"vscode-uri": "^3.1.0"
},
Expand Down
2 changes: 1 addition & 1 deletion language-server/update-info.log
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
ref: 1.109.0
a32be4e86dc900c438beb7ddf718d029bce04347
4dd2190b030b0b5a4dc794f343b669c514c03e88
Binary file not shown.