Skip to content

Commit 5380a05

Browse files
committed
add color theme
1 parent b8b66ff commit 5380a05

File tree

9 files changed

+873
-3490
lines changed

9 files changed

+873
-3490
lines changed

client/modules/IDE/components/Editor/index.jsx

Lines changed: 84 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,26 @@ import React, {
66
useMemo
77
} from 'react';
88
import PropTypes from 'prop-types';
9-
import { EditorState, StateEffect, Prec, StateField } from '@codemirror/state';
9+
import { EditorState, StateEffect, Prec } from '@codemirror/state';
1010
import {
1111
EditorView,
1212
keymap,
1313
highlightActiveLine,
1414
lineNumbers,
15-
Decoration,
16-
DecorationSet
15+
Decoration
1716
} from '@codemirror/view';
1817
import Fuse from 'fuse.js';
1918
import { javascript } from '@codemirror/lang-javascript';
2019
import { css } from '@codemirror/lang-css';
21-
import { bracketMatching, foldGutter } from '@codemirror/language';
20+
import {
21+
bracketMatching,
22+
foldGutter,
23+
syntaxHighlighting,
24+
codeFolding
25+
} from '@codemirror/language';
2226
import { autocompletion, closeBrackets } from '@codemirror/autocomplete';
2327
import { linter, lintGutter } from '@codemirror/lint';
2428
import { standardKeymap } from '@codemirror/commands';
25-
import {
26-
colorPicker,
27-
wrapperClassName
28-
} from '@replit/codemirror-css-color-picker';
2929
import MediaQuery from 'react-responsive';
3030
import prettier from 'prettier/standalone';
3131
import babelParser from 'prettier/parser-babel';
@@ -67,6 +67,16 @@ import { selectActiveFile } from '../../selectors/files';
6767
import { EditorContainer, EditorHolder } from './MobileEditor';
6868
import { FolderIcon } from '../../../../common/icons';
6969
import IconButton from '../../../../common/IconButton';
70+
import {
71+
lightHighlightStyle,
72+
p5LightTheme
73+
} from '../../utils/p5-light-cm-theme';
74+
import { darkHighlightStyle, p5DarkTheme } from '../../utils/p5-dark-cm-theme';
75+
import {
76+
contrastHighlightStyle,
77+
p5ContrastTheme
78+
} from '../../utils/p5-contrast-cm-theme';
79+
import p5ViewPlugin from '../../utils/p5ViewPlugin';
7080

7181
import * as FileActions from '../../actions/files';
7282
import * as IDEActions from '../../actions/ide';
@@ -109,39 +119,29 @@ const prettierFormatWithCursor = (view, parser, plugins) => {
109119
}
110120
};
111121

112-
const createThemeExtension = (themeName) => {
113-
const themeClass = themeName === 'dark' ? 'cm-s-p5-dark' : 'cm-s-p5-light';
114-
return EditorView.theme({}, { dark: themeName === 'dark', themeClass });
122+
const getThemeByName = (themeName) => {
123+
switch (themeName) {
124+
case 'dark':
125+
return p5DarkTheme;
126+
case 'contrast':
127+
return p5ContrastTheme;
128+
case 'light':
129+
default:
130+
return p5LightTheme;
131+
}
115132
};
116133

117-
// Create an effect to add or remove decorations
118-
const addDecorationEffect = StateEffect.define();
119-
const removeDecorationEffect = StateEffect.define();
120-
121-
// Define a StateField to manage decorations
122-
const decorationsField =
123-
StateField.define <
124-
DecorationSet >
125-
{
126-
create() {
127-
return Decoration.none;
128-
},
129-
update(decorations, tr) {
130-
let newDecorations = decorations; // Create a new variable to hold the updated decorations
131-
132-
tr.effects.forEach((effect) => {
133-
if (effect.is(addDecorationEffect)) {
134-
newDecorations = newDecorations.update({ add: [effect.value] });
135-
}
136-
if (effect.is(removeDecorationEffect)) {
137-
newDecorations = Decoration.none;
138-
}
139-
});
140-
141-
return newDecorations; // Return the new variable instead of reassigning the parameter
142-
},
143-
provide: (f) => EditorView.decorations.from(f)
144-
};
134+
const getHighlightStyleByName = (themeName) => {
135+
switch (themeName) {
136+
case 'dark':
137+
return darkHighlightStyle;
138+
case 'contrast':
139+
return contrastHighlightStyle;
140+
case 'light':
141+
default:
142+
return lightHighlightStyle;
143+
}
144+
};
145145

146146
const Editor = (props) => {
147147
const {
@@ -195,7 +195,7 @@ const Editor = (props) => {
195195
beepRef.current.play();
196196
}
197197
}, 2000),
198-
[]
198+
[lintMessages]
199199
);
200200

201201
const editorRef = useRef(null);
@@ -219,7 +219,7 @@ const Editor = (props) => {
219219

220220
const replaceCommand = metaKey === 'Ctrl' ? `Mod-h` : `Mod-Alt-f`;
221221

222-
// Handle document changes (debounced)
222+
// TODO: test debounce
223223
const handleEditorChange = useCallback(() => {
224224
setUnsavedChanges(true);
225225
hideRuntimeErrorWarning();
@@ -249,51 +249,45 @@ const Editor = (props) => {
249249
}
250250
};
251251

252-
const showFind = useCallback(() => {
252+
const showFind = () => {
253253
viewRef.current.dispatch({ effect: EditorView.findPersistent.of() });
254-
}, []);
254+
};
255255

256-
const showReplace = useCallback(() => {
256+
const showReplace = () => {
257257
viewRef.current.dispatch({ effect: EditorView.replacePersistent.of() });
258-
}, []);
258+
};
259259

260-
const getContent = useCallback(
261-
() => viewRef.current.state.doc.toString(),
262-
[]
263-
);
260+
const getContent = () => viewRef.current.state.doc.toString();
264261

265-
const handleKeyUp = useCallback(() => {
262+
const handleKeyUp = () => {
266263
const lineNumber = viewRef.current.state.doc.lineAt(
267264
viewRef.current.state.selection.main.head
268265
).number;
269266
setCurrentLine(lineNumber);
270-
}, []);
267+
};
271268

272269
const customLinterFunction = (view) => {
273270
const diagnostics = [];
274-
const content = view.state.doc.toString(); // Get the content of the editor
271+
const content = view.state.doc.toString();
275272

276-
// Pass the content through JSHint (or any other linter)
277273
JSHINT(content, {
278-
asi: true, // Allow missing semicolons
279-
eqeqeq: false, // Allow non-strict equality (== and !=)
280-
'-W041': false, // Disable warning for 'use of == null'
281-
esversion: 11 // Use ECMAScript version 11 (ES2020)
274+
asi: true,
275+
eqeqeq: false,
276+
'-W041': false,
277+
esversion: 11
282278
});
283279

284-
// Process JSHint results and convert them into CodeMirror diagnostics
285280
JSHINT.errors.forEach((error) => {
286281
if (!error) return;
287282

288283
diagnostics.push({
289-
from: view.state.doc.line(error.line).from + (error.character - 1), // Position of the error
290-
to: view.state.doc.line(error.line).from + error.character, // End position of the error
291-
severity: error.code.startsWith('W') ? 'warning' : 'error', // 'W' indicates a warning in JSHint
292-
message: error.reason // The error message
284+
from: view.state.doc.line(error.line).from + (error.character - 1),
285+
to: view.state.doc.line(error.line).from + error.character,
286+
severity: error.code.startsWith('W') ? 'warning' : 'error',
287+
message: error.reason
293288
});
294289
});
295290

296-
// Call the onUpdateLinting function to handle linting messages
297291
updateLintingMessageAccessibility(diagnostics);
298292

299293
return diagnostics;
@@ -303,7 +297,7 @@ const Editor = (props) => {
303297
const view = viewRef.current;
304298
if (view) {
305299
// TODO: fix this use persistent using codemirror search
306-
view.dispatch({ effects: findNext }); // Dispatch the find effect
300+
view.dispatch({ effects: findNext });
307301
}
308302
};
309303

@@ -340,6 +334,7 @@ const Editor = (props) => {
340334
run: () => null // No action, handle shift + meta + Enter
341335
},
342336
{
337+
// TODO: connect search widget
343338
key: `Mod-f`, // Meta + F to trigger findPersistent
344339
run: () => {
345340
triggerFindPersistent(); // Trigger the findPersistent functionality
@@ -364,7 +359,7 @@ const Editor = (props) => {
364359
},
365360
{
366361
key: `Mod-k`, // TODO: need to find a way to create custom color picker since codemirror 6 doesn't have one
367-
run: () => null
362+
run: (view) => view.state.colorpicker.popup_color_picker({ length: 0 })
368363
}
369364
]);
370365

@@ -454,21 +449,21 @@ const Editor = (props) => {
454449
const getCommonExtensions = () => [
455450
javascript(),
456451
autocompletion(),
457-
linter(customLinterFunction), // Linter extension
452+
linter(customLinterFunction),
458453
lintGutter(),
459454
abbreviationTracker(),
460-
lineNumbers(), // Line numbers
461-
highlightActiveLine(), // Highlight active line
462-
foldGutter(), // Fold gutter
463-
bracketMatching(), // Match brackets
464-
closeBrackets(), // Automatically close brackets
465-
highlightSelectionMatches(), // Highlight search matches
466-
css(), // CSS support
455+
lineNumbers(),
456+
highlightActiveLine(),
457+
foldGutter(),
458+
bracketMatching(),
459+
closeBrackets(),
460+
highlightSelectionMatches(),
461+
css(),
467462
keymap.of(standardKeymap),
468-
Prec.highest(customKeymap), // Ensure custom keymap has the highest precedence
463+
Prec.highest(customKeymap),
469464
EditorView.updateListener.of((update) => {
470465
if (update.docChanged) {
471-
handleEditorChange(); // Handle document changes
466+
handleEditorChange();
472467
}
473468
}),
474469
EditorView.updateListener.of((update) => {
@@ -478,21 +473,16 @@ const Editor = (props) => {
478473
}),
479474
EditorView.lineWrapping,
480475
hintExtension,
481-
// colorPicker,
482-
EditorView.theme({
483-
[`.${wrapperClassName}`]: {
484-
outlineColor: 'transparent'
485-
}
486-
})
487-
// decorationsField
476+
codeFolding(),
477+
p5ViewPlugin
488478
];
489479

490480
useEffect(() => {
491481
if (!editorRef.current) return;
492482

493483
const startState = EditorState.create({
494484
doc: file.content,
495-
extensions: [...getCommonExtensions(), createThemeExtension(theme)]
485+
extensions: [...getCommonExtensions()]
496486
});
497487

498488
const view = new EditorView({
@@ -549,7 +539,6 @@ const Editor = (props) => {
549539
const prevConsoleEventsLength = prevConsoleEventsLengthRef.current;
550540

551541
if (consoleEvents.length !== prevConsoleEventsLength) {
552-
// Process new console events
553542
consoleEvents.forEach((consoleEvent) => {
554543
if (consoleEvent.method === 'error') {
555544
const errorObj = { stack: consoleEvent.data[0].toString() };
@@ -571,21 +560,18 @@ const Editor = (props) => {
571560
if (fileWithError) {
572561
setSelectedFile(fileWithError.id);
573562

574-
// Create a line decoration for the error
575563
const decoration = Decoration.line({
576564
class: 'line-runtime-error'
577565
}).range(
578566
viewRef.current.state.doc.line(line.lineNumber - 1).from
579567
);
580568

581-
// Apply the decoration to highlight the line
582569
viewRef.current.dispatch({
583570
effects: StateEffect.appendConfig.of(
584571
EditorView.decorations.of(Decoration.set([decoration]))
585572
)
586573
});
587574

588-
// Store the decoration so it can be cleared later
589575
errorDecoration.current = Decoration.set([decoration]);
590576
}
591577
});
@@ -597,7 +583,7 @@ const Editor = (props) => {
597583
EditorView.decorations.of(Decoration.none)
598584
)
599585
});
600-
errorDecoration.current = Decoration.none; // Clear the stored decorations
586+
errorDecoration.current = Decoration.none;
601587
}
602588

603589
prevConsoleEventsLengthRef.current = consoleEvents.length;
@@ -644,12 +630,19 @@ const Editor = (props) => {
644630
// Handle theme change
645631
useEffect(() => {
646632
if (!viewRef.current) return;
647-
648633
const view = viewRef.current;
649-
const newTheme = createThemeExtension(theme);
634+
const newTheme = getThemeByName(theme);
635+
const newHighLightStyle = syntaxHighlighting(
636+
getHighlightStyleByName(theme)
637+
);
638+
console.log('theme', newTheme, newHighLightStyle);
650639

651640
view.dispatch({
652-
effects: StateEffect.reconfigure.of([...getCommonExtensions(), newTheme])
641+
effects: StateEffect.reconfigure.of([
642+
...getCommonExtensions(),
643+
newTheme,
644+
newHighLightStyle
645+
])
653646
});
654647
}, [props.file.content, theme]);
655648

@@ -691,7 +684,6 @@ const Editor = (props) => {
691684
'editor-holder--hidden': file.fileType === 'folder' || file.url
692685
})}
693686
/>
694-
<div id="color-picker" />
695687
{file.url ? <AssetPreview url={file.url} name={file.name} /> : null}
696688
<EditorAccessibility
697689
lintMessages={lintMessages}

0 commit comments

Comments
 (0)