diff --git a/e2e/playwright/snapshot-tests.spec.ts b/e2e/playwright/snapshot-tests.spec.ts index 81f05c480d8..17dd9bf5e3a 100644 --- a/e2e/playwright/snapshot-tests.spec.ts +++ b/e2e/playwright/snapshot-tests.spec.ts @@ -834,7 +834,13 @@ test('theme persists', async ({ page, context }) => { }) test.describe('code color goober', { tag: '@snapshot' }, () => { - test('code color goober', async ({ page, context, scene, cmdBar }) => { + test('code color goober', async ({ + page, + context, + scene, + cmdBar, + editor, + }) => { const u = await getUtils(page) await context.addInitScript(async () => { localStorage.setItem( @@ -879,6 +885,7 @@ sweepSketch = startSketchOn(XY) context, scene, cmdBar, + editor, }) => { const u = await getUtils(page) await context.addInitScript(async () => { diff --git a/packages/codemirror-lsp-client/src/client/index.ts b/packages/codemirror-lsp-client/src/client/index.ts index 2398c25bfbf..08be3d7597c 100644 --- a/packages/codemirror-lsp-client/src/client/index.ts +++ b/packages/codemirror-lsp-client/src/client/index.ts @@ -41,6 +41,14 @@ interface LSPRequestMap { LSP.DefinitionParams, LSP.Definition | LSP.DefinitionLink[] | null, ] + 'textDocument/documentColor': [ + LSP.DocumentColorParams, + LSP.ColorInformation[] | null, + ] + 'textDocument/colorPresentation': [ + LSP.ColorPresentationParams, + LSP.ColorPresentation[] | null, + ] } // Client to server @@ -229,6 +237,22 @@ export class LanguageServerClient { return await this.request('textDocument/definition', params) } + async textDocumentDocumentColor(params: LSP.DocumentColorParams) { + const serverCapabilities = this.getServerCapabilities() + if (!serverCapabilities.colorProvider) { + return null + } + return await this.request('textDocument/documentColor', params) + } + + async textDocumentColorPresentation(params: LSP.ColorPresentationParams) { + const serverCapabilities = this.getServerCapabilities() + if (!serverCapabilities.colorProvider) { + return null + } + return await this.request('textDocument/colorPresentation', params) + } + attachPlugin(plugin: LanguageServerPlugin) { this.plugins.push(plugin) } diff --git a/packages/codemirror-lsp-client/src/index.ts b/packages/codemirror-lsp-client/src/index.ts index b38fb803413..71ed4919031 100644 --- a/packages/codemirror-lsp-client/src/index.ts +++ b/packages/codemirror-lsp-client/src/index.ts @@ -21,6 +21,7 @@ export { lspRenameEvent, lspSemanticTokensEvent, lspCodeActionEvent, + lspColorUpdateEvent, } from './plugin/annotation' export { LanguageServerPlugin, diff --git a/packages/codemirror-lsp-client/src/plugin/annotation.ts b/packages/codemirror-lsp-client/src/plugin/annotation.ts index afe9d72120c..2e3e98f8f70 100644 --- a/packages/codemirror-lsp-client/src/plugin/annotation.ts +++ b/packages/codemirror-lsp-client/src/plugin/annotation.ts @@ -6,6 +6,7 @@ export enum LspAnnotation { Diagnostics = 'diagnostics', Rename = 'rename', CodeAction = 'code-action', + ColorUpdate = 'color-update', } const lspEvent = Annotation.define() @@ -14,3 +15,4 @@ export const lspFormatCodeEvent = lspEvent.of(LspAnnotation.FormatCode) export const lspDiagnosticsEvent = lspEvent.of(LspAnnotation.Diagnostics) export const lspRenameEvent = lspEvent.of(LspAnnotation.Rename) export const lspCodeActionEvent = lspEvent.of(LspAnnotation.CodeAction) +export const lspColorUpdateEvent = lspEvent.of(LspAnnotation.ColorUpdate) diff --git a/packages/codemirror-lsp-client/src/plugin/colors.ts b/packages/codemirror-lsp-client/src/plugin/colors.ts new file mode 100644 index 00000000000..adc2281a81a --- /dev/null +++ b/packages/codemirror-lsp-client/src/plugin/colors.ts @@ -0,0 +1,278 @@ +import { + StateEffect, + StateField, + type Extension, + type Range, +} from '@codemirror/state' +import { + Decoration, + type DecorationSet, + EditorView, + ViewPlugin, + WidgetType, + type ViewUpdate, +} from '@codemirror/view' + +import type { LanguageServerPlugin } from './lsp' +import { lspColorUpdateEvent } from './annotation' +import { isArray } from '../lib/utils' +import { offsetToPos, posToOffset, posToOffsetOrZero } from './util' +import type * as LSP from 'vscode-languageserver-protocol' + +/* ------------------------------------------------------------------ */ +/* ---------- original helpers / widget / color utilities ---------- */ +/* ------------------------------------------------------------------ */ + +interface PickerState { + from: number + to: number + red: number + green: number + blue: number + alpha: number +} + +export interface WidgetOptions extends PickerState { + color: string +} + +export type ColorData = Omit + +const pickerState = new WeakMap() + +function rgbaToHex(color: LSP.Color): string { + return ( + '#' + + [color.red, color.green, color.blue] + .map((c) => + Math.round(c * 255) + .toString(16) + .padStart(2, '0') + ) + .join('') + ) +} + +function hexToRGBComponents(hex: string): number[] { + const r = hex.slice(1, 3) + const g = hex.slice(3, 5) + const b = hex.slice(5, 7) + return [parseInt(r, 16) / 255, parseInt(g, 16) / 255, parseInt(b, 16) / 255] +} + +async function discoverColorsViaLsp( + view: EditorView, + plugin: LanguageServerPlugin +): Promise | null> { + const responses = await plugin.requestDocumentColors() + if (!responses) return null + + const colors: Array = [] + for (const color of responses) { + if (!color.range || !color.color) continue + + const { start, end } = color.range + const from = posToOffset(view.state.doc, start) + const to = posToOffset(view.state.doc, end) + if (from == null || to == null) continue + + colors.push({ + color: rgbaToHex(color.color), + ...color.color, + from, + to, + }) + } + return colors +} + +async function colorPickersDecorations( + view: EditorView, + plugin: LanguageServerPlugin +): Promise { + const widgets: Array> = [] + const maybe = await discoverColorsViaLsp(view, plugin) + if (!maybe) return Decoration.none + + const optionsList = isArray(maybe) ? maybe : [maybe] + for (const wo of optionsList) { + widgets.push( + Decoration.widget({ + widget: new ColorPickerWidget(wo), + side: 1, + }).range(wo.from) + ) + } + return Decoration.set(widgets) +} + +export const wrapperClassName = 'cm-css-color-picker-wrapper' + +class ColorPickerWidget extends WidgetType { + private readonly state: PickerState + private readonly color: string + + constructor({ color, ...state }: WidgetOptions) { + super() + this.state = state + this.color = color + } + + eq(other: ColorPickerWidget) { + return ( + other.color === this.color && + other.state.from === this.state.from && + other.state.to === this.state.to && + other.state.alpha === this.state.alpha + ) + } + + toDOM() { + const picker = document.createElement('input') + pickerState.set(picker, this.state) + picker.type = 'color' + picker.value = this.color + + const wrapper = document.createElement('span') + wrapper.appendChild(picker) + wrapper.className = wrapperClassName + return wrapper + } + + ignoreEvent() { + return false + } +} + +export const colorPickerTheme = EditorView.baseTheme({ + [`.${wrapperClassName}`]: { + display: 'inline-block', + outline: '1px solid #eee', + marginRight: '0.6ch', + height: '1em', + width: '1em', + transform: 'translateY(1px)', + }, + [`.${wrapperClassName} input[type="color"]`]: { + cursor: 'pointer', + height: '100%', + width: '100%', + padding: 0, + border: 'none', + '&::-webkit-color-swatch-wrapper': { padding: 0 }, + '&::-webkit-color-swatch': { border: 'none' }, + '&::-moz-color-swatch': { border: 'none' }, + }, +}) + +/* ------------------------------------------------------------------ */ +/* ------------------- ✅ new state machinery -------------------- */ +/* ------------------------------------------------------------------ */ + +// Effect that carries a fresh DecorationSet +const setColorDecorations = StateEffect.define() + +// Field that stores the current DecorationSet +const colorDecorationsField = StateField.define({ + create: () => Decoration.none, + update(value, tr) { + value = value.map(tr.changes) + for (const e of tr.effects) if (e.is(setColorDecorations)) value = e.value + return value + }, + provide: (f) => EditorView.decorations.from(f), +}) + +/* ------------------------------------------------------------------ */ +/* ------------------ original ViewPlugin, patched ---------------- */ +/* ------------------------------------------------------------------ */ + +export const makeColorPicker = (plugin: ViewPlugin) => + ViewPlugin.fromClass( + class ColorPickerViewPlugin { + plugin: LanguageServerPlugin | null + + constructor(view: EditorView) { + this.plugin = view.plugin(plugin) + if (!this.plugin) return + + // initial async load → dispatch decorations + // eslint-disable-next-line @typescript-eslint/no-floating-promises + colorPickersDecorations(view, this.plugin).then((deco) => { + view.dispatch({ effects: setColorDecorations.of(deco) }) + }) + } + + async update(update: ViewUpdate) { + if (!this.plugin) return + if (!(update.docChanged || update.viewportChanged)) return + + const deco = await colorPickersDecorations(update.view, this.plugin) + update.view.dispatch({ effects: setColorDecorations.of(deco) }) + } + }, + { + eventHandlers: { + change: (e: Event, view: EditorView) => { + // eslint-disable-next-line @typescript-eslint/no-floating-promises + colorPickerChange(e, view, plugin) + }, + }, + } + ) + +/* ------------------------------------------------------------------ */ +/* -------------------- unchanged event handler ------------------- */ +/* ------------------------------------------------------------------ */ + +async function colorPickerChange( + e: Event, + view: EditorView, + plugin: ViewPlugin +): Promise { + const value = view.plugin(plugin) + if (!value) return false + + const target = e.target as HTMLInputElement + if ( + target.nodeName !== 'INPUT' || + !target.parentElement?.classList.contains(wrapperClassName) + ) + return false + + const data = pickerState.get(target)! + const converted = target.value + data.alpha + const [red, green, blue] = hexToRGBComponents(converted) + + const responses = await value.requestColorPresentation( + { red, green, blue, alpha: data.alpha }, + { + start: offsetToPos(view.state.doc, data.from), + end: offsetToPos(view.state.doc, data.to), + } + ) + if (!responses?.length) return false + + for (const resp of responses) { + const changes = resp.textEdit + ? { + from: posToOffsetOrZero(view.state.doc, resp.textEdit.range.start), + to: posToOffsetOrZero(view.state.doc, resp.textEdit.range.end), + insert: resp.textEdit.newText, + } + : { from: data.from, to: data.to, insert: resp.label } + + view.dispatch({ changes, annotations: [lspColorUpdateEvent] }) + } + return true +} + +/* ------------------------------------------------------------------ */ +/* ------------------------- public API --------------------------- */ +/* ------------------------------------------------------------------ */ + +export default function lspColorsExt( + plugin: ViewPlugin +): Extension { + return [colorDecorationsField, makeColorPicker(plugin), colorPickerTheme] +} diff --git a/packages/codemirror-lsp-client/src/plugin/lsp.ts b/packages/codemirror-lsp-client/src/plugin/lsp.ts index f6ac9f1ebd4..641b919abef 100644 --- a/packages/codemirror-lsp-client/src/plugin/lsp.ts +++ b/packages/codemirror-lsp-client/src/plugin/lsp.ts @@ -48,6 +48,7 @@ import { isArray } from '../lib/utils' import lspGoToDefinitionExt from './go-to-definition' import lspRenameExt from './rename' import lspSignatureHelpExt from './signature-help' +import lspColorsExt from './colors' const useLast = (values: readonly any[]) => values.reduce((_, v) => v, '') export const docPathFacet = Facet.define({ @@ -534,6 +535,37 @@ export class LanguageServerPlugin implements PluginValue { }) } + async requestDocumentColors() { + if ( + !(this.client.getServerCapabilities().colorProvider && this.client.ready) + ) { + return + } + + const result = await this.client.textDocumentDocumentColor({ + textDocument: { uri: this.getDocUri() }, + }) + + if (!result) return + return result + } + + async requestColorPresentation(color: LSP.Color, range: LSP.Range) { + if ( + !(this.client.getServerCapabilities().colorProvider && this.client.ready) + ) { + return + } + + const result = await this.client.textDocumentColorPresentation({ + textDocument: { uri: this.getDocUri() }, + color, + range, + }) + if (!result) return + return result + } + async requestRename( view: EditorView, { line, character }: { line: number; character: number } @@ -1318,6 +1350,7 @@ export class LanguageServerPluginSpec return [ linter(null), lspAutocompleteExt(plugin), + lspColorsExt(plugin), lspFormatExt(plugin), lspGoToDefinitionExt(plugin), lspHoverExt(plugin), diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 2ef5acc794c..eef5ef90d5c 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -535,7 +535,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -963,7 +963,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1746,7 +1746,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2987,7 +2987,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3306,7 +3306,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3900,7 +3900,7 @@ dependencies = [ "getrandom 0.3.1", "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -4753,7 +4753,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] diff --git a/rust/kcl-lib/src/lsp/tests.rs b/rust/kcl-lib/src/lsp/tests.rs index 62531f022e1..5a055828714 100644 --- a/rust/kcl-lib/src/lsp/tests.rs +++ b/rust/kcl-lib/src/lsp/tests.rs @@ -4220,8 +4220,8 @@ sketch001 = startSketchOn(XY) result, vec![tower_lsp::lsp_types::ColorInformation { range: tower_lsp::lsp_types::Range { - start: tower_lsp::lsp_types::Position { line: 4, character: 24 }, - end: tower_lsp::lsp_types::Position { line: 4, character: 33 }, + start: tower_lsp::lsp_types::Position { line: 4, character: 25 }, + end: tower_lsp::lsp_types::Position { line: 4, character: 32 }, }, color: tower_lsp::lsp_types::Color { red: 1.0, @@ -4272,8 +4272,8 @@ sketch001 = startSketchOn(XY) result, vec![tower_lsp::lsp_types::ColorInformation { range: tower_lsp::lsp_types::Range { - start: tower_lsp::lsp_types::Position { line: 4, character: 24 }, - end: tower_lsp::lsp_types::Position { line: 4, character: 33 }, + start: tower_lsp::lsp_types::Position { line: 4, character: 25 }, + end: tower_lsp::lsp_types::Position { line: 4, character: 32 }, }, color: tower_lsp::lsp_types::Color { red: 1.0, @@ -4291,8 +4291,8 @@ sketch001 = startSketchOn(XY) uri: "file:///test.kcl".try_into().unwrap(), }, range: tower_lsp::lsp_types::Range { - start: tower_lsp::lsp_types::Position { line: 4, character: 24 }, - end: tower_lsp::lsp_types::Position { line: 4, character: 33 }, + start: tower_lsp::lsp_types::Position { line: 4, character: 25 }, + end: tower_lsp::lsp_types::Position { line: 4, character: 32 }, }, color: tower_lsp::lsp_types::Color { red: 1.0, diff --git a/rust/kcl-lib/src/parsing/ast/types/mod.rs b/rust/kcl-lib/src/parsing/ast/types/mod.rs index 2d0fa7bc545..bfe225465da 100644 --- a/rust/kcl-lib/src/parsing/ast/types/mod.rs +++ b/rust/kcl-lib/src/parsing/ast/types/mod.rs @@ -438,8 +438,15 @@ impl Node { let add_color = |literal: &Node| { // Check if the string is a color. if let Some(c) = literal.value.is_color() { + let source_range = literal.as_source_range(); + // We subtract 1 from either side because of the "'s in the literal. + let fixed_source_range = SourceRange::new( + source_range.start() + 1, + source_range.end() - 1, + source_range.module_id(), + ); let color = ColorInformation { - range: literal.as_source_range().to_lsp_range(code), + range: fixed_source_range.to_lsp_range(code), color: tower_lsp::lsp_types::Color { red: c.r, green: c.g, @@ -498,7 +505,11 @@ impl Node { crate::walk::walk(self, |node: crate::walk::Node<'a>| { match node { crate::walk::Node::Literal(literal) => { - if literal.start == pos_start && literal.end == pos_end && literal.value.is_color().is_some() { + // Account for the quotes in the literal. + if (literal.start + 1) == pos_start + && (literal.end - 1) == pos_end + && literal.value.is_color().is_some() + { found.replace(true); return Ok(true); } diff --git a/src/editor/plugins/lsp/kcl/colors.ts b/src/editor/plugins/lsp/kcl/colors.ts deleted file mode 100644 index 374860b2c76..00000000000 --- a/src/editor/plugins/lsp/kcl/colors.ts +++ /dev/null @@ -1,328 +0,0 @@ -import { language, syntaxTree } from '@codemirror/language' -import type { Extension, Range, Text } from '@codemirror/state' -import type { DecorationSet, ViewUpdate } from '@codemirror/view' -import { - Decoration, - EditorView, - ViewPlugin, - WidgetType, -} from '@codemirror/view' -import type { Tree } from '@lezer/common' -import { NodeProp } from '@lezer/common' -import { isArray } from '@src/lib/utils' - -interface PickerState { - from: number - to: number - alpha: string - colorType: ColorType -} - -export interface WidgetOptions extends PickerState { - color: string -} - -export type ColorData = Omit - -const pickerState = new WeakMap() - -export enum ColorType { - hex = 'HEX', -} - -const hexRegex = /(^|\b)(#[0-9a-f]{3,9})(\b|$)/i - -function discoverColorsInKCL( - syntaxTree: Tree, - from: number, - to: number, - typeName: string, - doc: Text, - language?: string -): WidgetOptions | Array | null { - switch (typeName) { - case 'Program': - case 'VariableDeclaration': - case 'CallExpressionKw': - case 'ObjectExpression': - case 'ObjectProperty': - case 'ArgumentList': - case 'PipeExpression': { - let innerTree = syntaxTree.resolveInner(from, 0).tree - - if (!innerTree) { - innerTree = syntaxTree.resolveInner(from, 1).tree - if (!innerTree) { - return null - } - } - - const overlayTree = innerTree.prop(NodeProp.mounted)?.tree - - if (overlayTree?.type.name !== 'Styles') { - return null - } - - const ret: Array = [] - overlayTree.iterate({ - from: 0, - to: overlayTree.length, - enter: ({ type, from: overlayFrom, to: overlayTo }) => { - const maybeWidgetOptions = discoverColorsInKCL( - syntaxTree, - // We add one because the tree doesn't include the - // quotation mark from the style tag - from + 1 + overlayFrom, - from + 1 + overlayTo, - type.name, - doc, - language - ) - - if (maybeWidgetOptions) { - if (isArray(maybeWidgetOptions)) { - console.error('Unexpected nested overlays') - ret.push(...maybeWidgetOptions) - } else { - ret.push(maybeWidgetOptions) - } - } - }, - }) - - return ret - } - - case 'String': { - const result = parseColorLiteral(doc.sliceString(from, to)) - if (!result) { - return null - } - return { - ...result, - from, - to, - } - } - - default: - return null - } -} - -export function parseColorLiteral(colorLiteral: string): ColorData | null { - const literal = colorLiteral.replace(/"/g, '') - const match = hexRegex.exec(literal) - if (!match) { - return null - } - const [color, alpha] = toFullHex(literal) - - return { - colorType: ColorType.hex, - color, - alpha, - } -} - -function colorPickersDecorations( - view: EditorView, - discoverColors: typeof discoverColorsInKCL -) { - const widgets: Array> = [] - - const st = syntaxTree(view.state) - - for (const range of view.visibleRanges) { - st.iterate({ - from: range.from, - to: range.to, - enter: ({ type, from, to }) => { - const maybeWidgetOptions = discoverColors( - st, - from, - to, - type.name, - view.state.doc, - view.state.facet(language)?.name - ) - - if (!maybeWidgetOptions) { - return - } - - if (!isArray(maybeWidgetOptions)) { - widgets.push( - Decoration.widget({ - widget: new ColorPickerWidget(maybeWidgetOptions), - side: 1, - }).range(maybeWidgetOptions.from) - ) - - return - } - - for (const wo of maybeWidgetOptions) { - widgets.push( - Decoration.widget({ - widget: new ColorPickerWidget(wo), - side: 1, - }).range(wo.from) - ) - } - }, - }) - } - - return Decoration.set(widgets) -} - -function toFullHex(color: string): string[] { - if (color.length === 4) { - // 3-char hex - return [ - `#${color[1].repeat(2)}${color[2].repeat(2)}${color[3].repeat(2)}`, - '', - ] - } - - if (color.length === 5) { - // 4-char hex (alpha) - return [ - `#${color[1].repeat(2)}${color[2].repeat(2)}${color[3].repeat(2)}`, - color[4].repeat(2), - ] - } - - if (color.length === 9) { - // 8-char hex (alpha) - return [`#${color.slice(1, -2)}`, color.slice(-2)] - } - - return [color, ''] -} - -export const wrapperClassName = 'cm-css-color-picker-wrapper' - -class ColorPickerWidget extends WidgetType { - private readonly state: PickerState - private readonly color: string - - constructor({ color, ...state }: WidgetOptions) { - super() - this.state = state - this.color = color - } - - eq(other: ColorPickerWidget) { - return ( - other.state.colorType === this.state.colorType && - other.color === this.color && - other.state.from === this.state.from && - other.state.to === this.state.to && - other.state.alpha === this.state.alpha - ) - } - - toDOM() { - const picker = document.createElement('input') - pickerState.set(picker, this.state) - picker.type = 'color' - picker.value = this.color - - const wrapper = document.createElement('span') - wrapper.appendChild(picker) - wrapper.className = wrapperClassName - - return wrapper - } - - ignoreEvent() { - return false - } -} - -export const colorPickerTheme = EditorView.baseTheme({ - [`.${wrapperClassName}`]: { - display: 'inline-block', - outline: '1px solid #eee', - marginRight: '0.6ch', - height: '1em', - width: '1em', - transform: 'translateY(1px)', - }, - [`.${wrapperClassName} input[type="color"]`]: { - cursor: 'pointer', - height: '100%', - width: '100%', - padding: 0, - border: 'none', - '&::-webkit-color-swatch-wrapper': { - padding: 0, - }, - '&::-webkit-color-swatch': { - border: 'none', - }, - '&::-moz-color-swatch': { - border: 'none', - }, - }, -}) - -interface IFactoryOptions { - discoverColors: typeof discoverColorsInKCL -} - -export const makeColorPicker = (options: IFactoryOptions) => - ViewPlugin.fromClass( - class ColorPickerViewPlugin { - decorations: DecorationSet - - constructor(view: EditorView) { - this.decorations = colorPickersDecorations(view, options.discoverColors) - } - - update(update: ViewUpdate) { - if (update.docChanged || update.viewportChanged) { - this.decorations = colorPickersDecorations( - update.view, - options.discoverColors - ) - } - } - }, - { - decorations: (v) => v.decorations, - eventHandlers: { - change: (e, view) => { - const target = e.target as HTMLInputElement - if ( - target.nodeName !== 'INPUT' || - !target.parentElement || - !target.parentElement.classList.contains(wrapperClassName) - ) { - return false - } - - const data = pickerState.get(target)! - - let converted = '"' + target.value + data.alpha + '"' - - view.dispatch({ - changes: { - from: data.from, - to: data.to, - insert: converted, - }, - }) - - return true - }, - }, - } - ) - -export const colorPicker: Extension = [ - makeColorPicker({ discoverColors: discoverColorsInKCL }), - colorPickerTheme, -] diff --git a/src/editor/plugins/lsp/kcl/index.ts b/src/editor/plugins/lsp/kcl/index.ts index 2328078ac6d..f1f4d4935ba 100644 --- a/src/editor/plugins/lsp/kcl/index.ts +++ b/src/editor/plugins/lsp/kcl/index.ts @@ -7,6 +7,7 @@ import type { } from '@kittycad/codemirror-lsp-client' import { lspCodeActionEvent, + lspColorUpdateEvent, lspFormatCodeEvent, lspPlugin, lspRenameEvent, @@ -88,6 +89,8 @@ export class KclPlugin implements PluginValue { isRelevant = true } else if (tr.annotation(lspCodeActionEvent.type)) { isRelevant = true + } else if (tr.annotation(lspColorUpdateEvent.type)) { + isRelevant = true } // Don't make this an else. diff --git a/src/editor/plugins/lsp/kcl/language.ts b/src/editor/plugins/lsp/kcl/language.ts index a9a7ac4aba2..d2aa963bae9 100644 --- a/src/editor/plugins/lsp/kcl/language.ts +++ b/src/editor/plugins/lsp/kcl/language.ts @@ -8,7 +8,6 @@ import type { import type * as LSP from 'vscode-languageserver-protocol' import { kclPlugin } from '@src/editor/plugins/lsp/kcl' -import { colorPicker } from '@src/editor/plugins/lsp/kcl/colors' export interface LanguageOptions { workspaceFolders: LSP.WorkspaceFolder[] @@ -22,7 +21,6 @@ export interface LanguageOptions { export function kcl(options: LanguageOptions) { return new LanguageSupport(KclLanguage, [ - colorPicker, kclPlugin({ documentUri: options.documentUri, workspaceFolders: options.workspaceFolders,