From 618a04387de76b65e0eb6da3c101b34ac89b0bad Mon Sep 17 00:00:00 2001 From: Parth Jadhao Date: Mon, 1 Jun 2026 17:47:09 +0530 Subject: [PATCH] fix: restored p5.js keyword highlighting for CM6 #3868 --- .../IDE/components/Editor/p5Highlight.js | 50 +++++++++++++++++++ .../IDE/components/Editor/stateUtils.js | 2 + client/styles/components/_editor.scss | 15 +++++- 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 client/modules/IDE/components/Editor/p5Highlight.js diff --git a/client/modules/IDE/components/Editor/p5Highlight.js b/client/modules/IDE/components/Editor/p5Highlight.js new file mode 100644 index 0000000000..d454f18b51 --- /dev/null +++ b/client/modules/IDE/components/Editor/p5Highlight.js @@ -0,0 +1,50 @@ +import { ViewPlugin, Decoration } from '@codemirror/view'; +import { RangeSetBuilder } from '@codemirror/state'; +import { syntaxTree } from '@codemirror/language'; +import { + p5FunctionKeywords, + p5VariableKeywords +} from '../../../../utils/p5-keywords'; + +const p5Functions = new Set(Object.keys(p5FunctionKeywords)); +const p5Variables = new Set(Object.keys(p5VariableKeywords)); + +const p5FunctionMark = Decoration.mark({ class: 'cm-p5-function' }); +const p5VariableMark = Decoration.mark({ class: 'cm-p5-variable' }); + +function buildDecorations(view) { + const builder = new RangeSetBuilder(); + view.visibleRanges.forEach(({ from, to }) => { + syntaxTree(view.state).iterate({ + from, + to, + enter(node) { + const isVariable = node.name === 'VariableName'; + const isDefinition = node.name === 'VariableDefinition'; + if (!isVariable && !isDefinition) return; + const name = view.state.doc.sliceString(node.from, node.to); + if (p5Functions.has(name)) { + builder.add(node.from, node.to, p5FunctionMark); + } else if (p5Variables.has(name)) { + builder.add(node.from, node.to, p5VariableMark); + } + } + }); + }); + return builder.finish(); +} + +export const p5Highlight = ViewPlugin.fromClass( + class { + constructor(view) { + this.decorations = buildDecorations(view); + } + + update(update) { + if (update.docChanged || update.viewportChanged) { + this.decorations = buildDecorations(update.view); + } + } + }, + { decorations: (v) => v.decorations } +); diff --git a/client/modules/IDE/components/Editor/stateUtils.js b/client/modules/IDE/components/Editor/stateUtils.js index c526f1b414..2bc1418c67 100644 --- a/client/modules/IDE/components/Editor/stateUtils.js +++ b/client/modules/IDE/components/Editor/stateUtils.js @@ -59,6 +59,7 @@ import { Linter as ESLinter } from 'eslint-linter-browserify'; import { tidyCodeWithPrettier } from './tidier'; import p5JavaScript from './p5JavaScript'; import { highlightStyle } from './highlightStyle'; +import { p5Highlight } from './p5Highlight'; import { errorDecorationStateField } from './consoleErrorDecoration'; // ----- TODOS ----- @@ -446,6 +447,7 @@ export function createNewFileState(filename, document, settings) { highlightSpecialChars(), highlightSelectionMatches(), syntaxHighlighting(highlightStyle), + p5Highlight, // Selection extensions drawSelection(), rectangularSelection(), diff --git a/client/styles/components/_editor.scss b/client/styles/components/_editor.scss index 93866036cd..145ceb1762 100644 --- a/client/styles/components/_editor.scss +++ b/client/styles/components/_editor.scss @@ -306,7 +306,20 @@ // contentSeparator, monospace, strikethrough, inserted, deleted, changed, invalid, meta, // documentMeta, annotation, processingInstruction, standard, special, macroName -// TODO(connie): Add p5 specific highlighting styles, like .cm-p5-function, .cm-p5-variable +.cm-p5-function, +.cm-p5-function .cm-variable { + @include themify() { + color: getThemifyVariable('highlight-style-variable-color'); + font-weight: bold; + } +} + +.cm-p5-variable, +.cm-p5-variable .cm-variable { + @include themify() { + color: getThemifyVariable('highlight-style-atom-color'); + } +} // Additional matching selection styling .cm-matchingBracket {