Skip to content

Commit aff8772

Browse files
authored
Merge pull request #4152 from processing/develop-codemirror-v6
Update to Codemirror v6
2 parents 69d20a1 + 6047133 commit aff8772

40 files changed

Lines changed: 3620 additions & 5685 deletions

client/modules/IDE/actions/ide.js

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -236,23 +236,10 @@ export function hideErrorModal() {
236236
};
237237
}
238238

239-
export function hideRuntimeErrorWarning() {
240-
return {
241-
type: ActionTypes.HIDE_RUNTIME_ERROR_WARNING
242-
};
243-
}
244-
245-
export function showRuntimeErrorWarning() {
246-
return {
247-
type: ActionTypes.SHOW_RUNTIME_ERROR_WARNING
248-
};
249-
}
250-
251239
export function startSketch() {
252240
return (dispatch, getState) => {
253241
dispatch(clearConsole());
254242
dispatch(startVisualSketch());
255-
dispatch(showRuntimeErrorWarning());
256243
const state = getState();
257244
dispatchMessage({
258245
type: MessageTypes.SKETCH,

client/modules/IDE/components/ConsoleInput.jsx

Lines changed: 95 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,41 @@
11
import PropTypes from 'prop-types';
2-
import React, { useRef, useEffect, useState } from 'react';
3-
import CodeMirror from 'codemirror';
2+
import React, { useRef, useEffect } from 'react';
3+
import { EditorState } from '@codemirror/state';
4+
import { EditorView, highlightSpecialChars, keymap } from '@codemirror/view';
5+
import { bracketMatching, syntaxHighlighting } from '@codemirror/language';
6+
import { closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete';
7+
import { defaultKeymap, history, historyKeymap } from '@codemirror/commands';
8+
import { javascript } from '@codemirror/lang-javascript';
9+
410
import { useDispatch } from 'react-redux';
511
import { Encode } from 'console-feed';
612

713
import RightArrowIcon from '../../../images/right-arrow.svg';
814
import { dispatchConsoleEvent } from '../actions/console';
915
import { dispatchMessage, MessageTypes } from '../../../utils/dispatcher';
16+
import { highlightStyle } from './Editor/utils/highlightStyle';
1017

1118
// heavily inspired by
1219
// https://github.com/codesandbox/codesandbox-client/blob/92a1131f4ded6f7d9c16945dc7c18aa97c8ada27/packages/app/src/app/components/Preview/DevTools/Console/Input/index.tsx
1320

21+
// TODO(connie): Add theme support?
1422
function ConsoleInput({ theme, fontSize }) {
15-
const [commandHistory, setCommandHistory] = useState([]);
16-
const [commandCursor, setCommandCursor] = useState(-1);
23+
const commandHistory = useRef([]);
24+
const commandCursor = useRef(-1);
1725
const codemirrorContainer = useRef(null);
18-
const cmInstance = useRef(null);
26+
const cmView = useRef(null);
1927
const dispatch = useDispatch();
2028

2129
useEffect(() => {
22-
cmInstance.current = CodeMirror(codemirrorContainer.current, {
23-
theme: `p5-${theme}`,
24-
scrollbarStyle: null,
25-
keymap: 'sublime',
26-
mode: 'javascript',
27-
inputStyle: 'contenteditable'
28-
});
29-
}, []);
30-
31-
useEffect(() => {
32-
const handleEnterKey = (cm, e) => {
33-
if (e.key === 'Enter' && !e.shiftKey) {
34-
e.preventDefault();
35-
e.stopPropagation();
36-
37-
const value = cm.getValue().trim();
38-
if (value === '') return;
30+
const enterKeymap = {
31+
key: 'Enter',
32+
shiftKey: () => false, // Treat like a normal Enter key press if the Shift key is held down.
33+
preventDefault: true,
34+
stopPropogation: true,
35+
run: (view) => {
36+
const value = view.state.doc.toString().trim();
37+
if (value === '' || view.state.selection.main.empty === false)
38+
return false;
3939

4040
const messages = [
4141
{ log: Encode({ method: 'command', data: [value] }) }
@@ -48,77 +48,91 @@ function ConsoleInput({ theme, fontSize }) {
4848
});
4949

5050
dispatch(dispatchConsoleEvent(consoleEvent));
51-
cm.setValue('');
52-
setCommandHistory([value, ...commandHistory]);
53-
setCommandCursor(-1);
54-
}
55-
};
56-
57-
if (cmInstance.current) {
58-
cmInstance.current.on('keydown', handleEnterKey);
59-
}
60-
61-
return () => {
62-
if (cmInstance.current) {
63-
cmInstance.current.off('keydown', handleEnterKey);
51+
view.dispatch({
52+
changes: { from: 0, to: view.state.doc.length, insert: '' }
53+
});
54+
commandHistory.current.unshift(value);
55+
commandCursor.current = -1;
56+
return true;
6457
}
6558
};
66-
}, [commandHistory]);
6759

68-
useEffect(() => {
69-
const handleUpArrowKey = (cm, e) => {
70-
if (e.key === 'ArrowUp') {
71-
const lineNumber = cm.getDoc().getCursor().line;
72-
if (lineNumber !== 0) return;
60+
const upArrowKeymap = {
61+
key: 'ArrowUp',
62+
run: (view) => {
63+
// Just let the cursor go up if we have a multiline input
64+
// and the cursor isn't at the first line.
65+
const currentLine = view.state.doc.lineAt(
66+
view.state.selection.main.head
67+
).number;
68+
// CM lines are 1-indexed, so the first line is 1.
69+
if (currentLine > 1) return false;
7370

7471
const newCursor = Math.min(
75-
commandCursor + 1,
76-
commandHistory.length - 1
72+
commandCursor.current + 1,
73+
commandHistory.current.length - 1
7774
);
78-
cm.setValue(commandHistory[newCursor] || '');
79-
const cursorPos = cm.getDoc().getLine(0).length - 1;
80-
cm.getDoc().setCursor({ line: 0, ch: cursorPos });
81-
setCommandCursor(newCursor);
82-
}
83-
};
84-
85-
if (cmInstance.current) {
86-
cmInstance.current.on('keydown', handleUpArrowKey);
87-
}
88-
89-
return () => {
90-
if (cmInstance.current) {
91-
cmInstance.current.off('keydown', handleUpArrowKey);
75+
const newValue = commandHistory.current[newCursor] || '';
76+
view.dispatch({
77+
changes: { from: 0, to: view.state.doc.length, insert: newValue }
78+
});
79+
const newCursorPos = newValue.length;
80+
view.dispatch({
81+
selection: { anchor: newCursorPos, head: newCursorPos }
82+
});
83+
commandCursor.current = newCursor;
84+
return true;
9285
}
9386
};
94-
}, [commandCursor, commandHistory]);
9587

96-
useEffect(() => {
97-
const handleArrowDownKey = (cm, e) => {
98-
if (e.key === 'ArrowDown') {
99-
const lineNumber = cm.getDoc().getCursor().line;
100-
const lineCount = cm.lineCount();
101-
if (lineNumber + 1 !== lineCount) return;
102-
103-
const newCursor = Math.max(commandCursor - 1, -1);
104-
cm.setValue(commandHistory[newCursor] || '');
105-
const newLine = cm.getDoc().getLine(lineCount - 1);
106-
const cursorPos = newLine ? newLine.length - 1 : 1;
107-
cm.getDoc().setCursor({ line: lineCount - 1, ch: cursorPos });
108-
setCommandCursor(newCursor);
88+
const downArrowKeymap = {
89+
key: 'ArrowDown',
90+
run: (view) => {
91+
// Just let the cursor go down if we have a multiline input
92+
// and the cursor isn't at the last line.
93+
const currentLine = view.state.doc.lineAt(
94+
view.state.selection.main.head
95+
).number;
96+
const docLength = view.state.doc.lines;
97+
if (currentLine !== docLength) return false;
98+
99+
const newCursor = Math.max(commandCursor.current - 1, -1);
100+
const newValue = commandHistory.current[newCursor] || '';
101+
view.dispatch({
102+
changes: { from: 0, to: view.state.doc.length, insert: newValue }
103+
});
104+
const newCursorPos = newValue.length;
105+
view.dispatch({
106+
selection: { anchor: newCursorPos, head: newCursorPos }
107+
});
108+
commandCursor.current = newCursor;
109+
return true;
109110
}
110111
};
111112

112-
if (cmInstance.current) {
113-
cmInstance.current.on('keydown', handleArrowDownKey);
114-
}
115-
116-
return () => {
117-
if (cmInstance.current) {
118-
cmInstance.current.off('keydown', handleArrowDownKey);
119-
}
120-
};
121-
}, [commandCursor, commandHistory]);
113+
const cmState = EditorState.create({
114+
extensions: [
115+
history(),
116+
highlightSpecialChars(),
117+
bracketMatching(),
118+
closeBrackets(),
119+
syntaxHighlighting(highlightStyle),
120+
javascript(),
121+
keymap.of([
122+
enterKeymap,
123+
upArrowKeymap,
124+
downArrowKeymap,
125+
...defaultKeymap,
126+
...closeBracketsKeymap,
127+
...historyKeymap
128+
])
129+
]
130+
});
131+
cmView.current = new EditorView({
132+
state: cmState,
133+
parent: codemirrorContainer.current
134+
});
135+
}, []);
122136

123137
return (
124138
<div className="console__input">

0 commit comments

Comments
 (0)