Skip to content

Commit ccba63f

Browse files
committed
Rename advanced editor to Ace and add Monaco
1 parent 8cfa7b2 commit ccba63f

11 files changed

+284
-33
lines changed

ui/frontend/ConfigMenu.tsx

+8-8
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,15 @@ const ConfigMenu: React.SFC<ConfigMenuProps> = () => {
4444
return (
4545
<Fragment>
4646
<MenuGroup title="Editor">
47-
<EitherConfig
48-
id="editor-style"
49-
name="Style"
50-
a={Editor.Simple}
51-
b={Editor.Advanced}
47+
<SelectConfig
48+
name="Editor"
5249
value={editorStyle}
53-
onChange={changeEditorStyle} />
54-
55-
{editorStyle === Editor.Advanced && (
50+
onChange={changeEditorStyle}
51+
>
52+
{[Editor.Simple, Editor.Ace, Editor.Monaco]
53+
.map(k => <option key={k} value={k}>{k}</option>)}
54+
</SelectConfig>
55+
{editorStyle === Editor.Ace && (
5656
<Fragment>
5757
<SelectConfig
5858
name="Keybinding"

ui/frontend/Playground.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useRef } from 'react';
22
import { useDispatch, useSelector } from 'react-redux';
33
import Split from 'split-grid';
44

5-
import Editor from './Editor';
5+
import Editor from './editor/Editor';
66
import Header from './Header';
77
import Notifications from './Notifications';
88
import Output from './Output';

ui/frontend/AdvancedEditor.tsx renamed to ui/frontend/editor/AceEditor.tsx

+15-15
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
22
import { connect } from 'react-redux';
3-
import { aceResizeKey, offerCrateAutocompleteOnUse } from './selectors';
3+
import { aceResizeKey, offerCrateAutocompleteOnUse } from '../selectors';
44

5-
import State from './state';
6-
import { AceResizeKey, CommonEditorProps, Crate, PairCharacters, Position, Selection } from './types';
5+
import State from '../state';
6+
import { AceResizeKey, CommonEditorProps, Crate, PairCharacters, Position, Selection } from '../types';
77

88
import styles from './Editor.module.css';
99

1010
type Ace = typeof import('ace-builds');
11-
type AceEditor = import('ace-builds').Ace.Editor;
11+
type AceModule = import('ace-builds').Ace.Editor;
1212
type AceCompleter = import('ace-builds').Ace.Completer;
1313

14-
const displayExternCrateAutocomplete = (editor: AceEditor, autocompleteOnUse: boolean) => {
14+
const displayExternCrateAutocomplete = (editor: AceModule, autocompleteOnUse: boolean) => {
1515
const { session } = editor;
1616
const pos = editor.getCursorPosition();
1717
const line = session.getLine(pos.row);
@@ -55,11 +55,11 @@ function useRafDebouncedFunction<A extends any[]>(fn: (...args: A) => void, onCa
5555
}, [fn, onCall, timeout]);
5656
}
5757

58-
interface AdvancedEditorProps extends AdvancedEditorAsyncProps {
58+
interface AceEditorProps extends AceEditorAsyncProps {
5959
ace: Ace;
6060
}
6161

62-
interface AdvancedEditorProps {
62+
interface AceEditorProps {
6363
ace: Ace;
6464
autocompleteOnUse: boolean;
6565
code: string;
@@ -75,16 +75,16 @@ interface AdvancedEditorProps {
7575
}
7676

7777
// Run an effect when the editor or prop changes
78-
function useEditorProp<T>(editor: AceEditor, prop: T, whenPresent: (editor: AceEditor, prop: T) => void) {
78+
function useEditorProp<T>(editor: AceModule, prop: T, whenPresent: (editor: AceModule, prop: T) => void) {
7979
useEffect(() => {
8080
if (editor) {
8181
return whenPresent(editor, prop);
8282
}
8383
}, [editor, prop, whenPresent]);
8484
}
8585

86-
const AdvancedEditor: React.SFC<AdvancedEditorProps> = props => {
87-
const [editor, setEditor] = useState<AceEditor>(null);
86+
const AceEditor: React.SFC<AceEditorProps> = props => {
87+
const [editor, setEditor] = useState<AceModule>(null);
8888
const child = useRef<HTMLDivElement>(null);
8989

9090
useEffect(() => {
@@ -315,7 +315,7 @@ enum LoadState {
315315
//
316316
// Themes and keybindings can be changed at runtime.
317317

318-
interface AdvancedEditorAsyncProps {
318+
interface AceEditorAsyncProps {
319319
autocompleteOnUse: boolean;
320320
code: string;
321321
execute: () => any;
@@ -329,7 +329,7 @@ interface AdvancedEditorAsyncProps {
329329
pairCharacters: PairCharacters;
330330
}
331331

332-
class AdvancedEditorAsync extends React.Component<AdvancedEditorAsyncProps, AdvancedEditorAsyncState> {
332+
class AceEditorAsync extends React.Component<AceEditorAsyncProps, AdvancedEditorAsyncState> {
333333
public constructor(props) {
334334
super(props);
335335
this.state = {
@@ -342,7 +342,7 @@ class AdvancedEditorAsync extends React.Component<AdvancedEditorAsyncProps, Adva
342342
public render() {
343343
if (this.isLoaded()) {
344344
const { ace, theme, keybinding } = this.state;
345-
return <AdvancedEditor {...this.props} ace={ace} theme={theme} keybinding={keybinding} />;
345+
return <AceEditor {...this.props} ace={ace} theme={theme} keybinding={keybinding} />;
346346
} else {
347347
return <div>Loading the ACE editor...</div>;
348348
}
@@ -448,7 +448,7 @@ class AdvancedEditorAsync extends React.Component<AdvancedEditorAsyncProps, Adva
448448
private async requireLibraries() {
449449
return import(
450450
/* webpackChunkName: "advanced-editor" */
451-
'./advanced-editor'
451+
'../advanced-editor'
452452
);
453453
}
454454
}
@@ -483,4 +483,4 @@ const mapStateToProps = (state: State) => {
483483
};
484484
};
485485

486-
export default connect<PropsFromState, undefined, CommonEditorProps>(mapStateToProps)(AdvancedEditorAsync);
486+
export default connect<PropsFromState, undefined, CommonEditorProps>(mapStateToProps)(AceEditorAsync);

ui/frontend/Editor.module.css renamed to ui/frontend/editor/Editor.module.css

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
.container {
2-
composes: -autoSize from './shared.module.css';
2+
composes: -autoSize from '../shared.module.css';
33
position: relative;
44
}
55

66
.advanced {
7-
composes: -bodyMonospace -autoSize from './shared.module.css';
7+
composes: -bodyMonospace -autoSize from '../shared.module.css';
88
position: absolute;
99
}
1010

ui/frontend/Editor.tsx renamed to ui/frontend/editor/Editor.tsx

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import React, { useCallback } from 'react';
22
import { useSelector, useDispatch } from 'react-redux';
33

4-
import * as actions from './actions';
5-
import AdvancedEditor from './AdvancedEditor';
6-
import { CommonEditorProps, Editor as EditorType, Position, Selection } from './types';
7-
import { State } from './reducers';
4+
import * as actions from '../actions';
5+
import AceEditor from './AceEditor';
6+
import { CommonEditorProps, Editor as EditorType, Position, Selection } from '../types';
7+
import { State } from '../reducers';
88

99
import styles from './Editor.module.css';
10+
import MonacoEditor from './MonacoEditor';
1011

1112
class CodeByteOffsets {
1213
readonly code: string;
@@ -118,7 +119,7 @@ const Editor: React.SFC = () => {
118119
const execute = useCallback(() => dispatch(actions.performPrimaryAction()), [dispatch]);
119120
const onEditCode = useCallback((c) => dispatch(actions.editCode(c)), [dispatch]);
120121

121-
const SelectedEditor = editor === EditorType.Simple ? SimpleEditor : AdvancedEditor;
122+
const SelectedEditor = editor === EditorType.Simple ? SimpleEditor : editor === EditorType.Monaco ? MonacoEditor : AceEditor;
122123

123124
return (
124125
<div className={styles.container}>

ui/frontend/editor/MonacoEditor.tsx

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import React from 'react';
2+
import { CommonEditorProps, Position } from '../types';
3+
import MonacoReact, { Monaco } from "@monaco-editor/react";
4+
5+
import styles from './Editor.module.css';
6+
import { config, grammar } from './rust_monaco_def';
7+
8+
class CodeByteOffsets {
9+
readonly code: string;
10+
readonly lines: string[];
11+
12+
constructor(code: string) {
13+
this.code = code;
14+
this.lines = code.split('\n');
15+
}
16+
17+
public lineToOffsets(line: number) {
18+
const precedingBytes = this.bytesBeforeLine(line);
19+
20+
const highlightedLine = this.lines[line];
21+
const highlightedBytes = highlightedLine.length;
22+
23+
return [precedingBytes, precedingBytes + highlightedBytes];
24+
}
25+
26+
public rangeToOffsets(start: Position, end: Position) {
27+
const startBytes = this.positionToBytes(start);
28+
const endBytes = this.positionToBytes(end);
29+
return [startBytes, endBytes];
30+
}
31+
32+
private positionToBytes(position: Position) {
33+
// Subtract one as this logic is zero-based and the columns are one-based
34+
return this.bytesBeforeLine(position.line) + position.column - 1;
35+
}
36+
37+
private bytesBeforeLine(line: number) {
38+
// Subtract one as this logic is zero-based and the lines are one-based
39+
line -= 1;
40+
41+
const precedingLines = this.lines.slice(0, line);
42+
43+
// Add one to account for the newline we split on and removed
44+
return precedingLines.map(l => l.length + 1).reduce((a, b) => a + b);
45+
}
46+
}
47+
48+
const modeId = 'my-rust';
49+
50+
const initMonaco = (monaco: Monaco) => {
51+
monaco.editor.defineTheme('vscode-dark-plus', {
52+
base: 'vs-dark',
53+
inherit: true,
54+
rules: [
55+
{ token: 'keyword.control', foreground: 'C586C0' },
56+
{ token: 'variable', foreground: '9CDCFE' },
57+
{ token: 'support.function', foreground: 'DCDCAA' },
58+
]
59+
});
60+
monaco.languages.register({ // language for editor
61+
id: modeId,
62+
});
63+
64+
monaco.languages.onLanguage(modeId, async () => {
65+
console.log(modeId);
66+
67+
monaco.languages.setLanguageConfiguration(modeId, config);
68+
monaco.languages.setMonarchTokensProvider(modeId, grammar);
69+
});
70+
};
71+
72+
const MonacoEditor: React.SFC<CommonEditorProps> = props => {
73+
return (
74+
<MonacoReact
75+
language={modeId}
76+
theme="vscode-dark-plus"
77+
loading="Loading the Monaco editor..."
78+
className={styles.advanced}
79+
value={props.code}
80+
onChange={props.onEditCode}
81+
beforeMount={initMonaco}
82+
/>
83+
);
84+
}
85+
86+
export default MonacoEditor;

0 commit comments

Comments
 (0)