Skip to content

Commit 60759af

Browse files
authored
Merge pull request #736 from HKalbasi/master
Rename advanced editor to Ace and add Monaco
2 parents d5aca97 + 1b8120b commit 60759af

22 files changed

+536
-144
lines changed

tests/spec/features/editor_types_spec.rb

+26-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
before { visit '/' }
88

99
scenario "using the simple editor" do
10-
in_config_menu { choose("simple") }
10+
in_config_menu { select("simple") }
1111

1212
fill_in('editor-simple', with: simple_editor_code)
1313

@@ -25,4 +25,29 @@ def simple_editor_code
2525
}
2626
EOF
2727
end
28+
29+
scenario "using the Monaco editor" do
30+
in_config_menu { select("monaco") }
31+
32+
editor = page.find('.monaco-editor')
33+
34+
# Click on the last line as that will replace the entire content
35+
editor.find('.view-line:last-child').click
36+
t = editor.find('textarea', visible: false)
37+
t.set(monaco_editor_code, clear: :backspace)
38+
39+
click_on("Run")
40+
41+
within(:output, :stdout) do
42+
expect(page).to have_content 'Monaco editor'
43+
end
44+
end
45+
46+
# Missing indentation and closing curly braces as those are auto-inserted
47+
def monaco_editor_code
48+
<<~EOF
49+
fn main() {
50+
println!("Using the Monaco editor");
51+
EOF
52+
end
2853
end

ui/frontend/ConfigMenu.tsx

+31-14
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,24 @@ interface ConfigMenuProps {
2121
close: () => void;
2222
}
2323

24+
const MONACO_THEMES = [
25+
'vs', 'vs-dark', 'vscode-dark-plus',
26+
];
27+
2428
const ConfigMenu: React.SFC<ConfigMenuProps> = () => {
25-
const keybinding = useSelector((state: State) => state.configuration.keybinding);
26-
const theme = useSelector((state: State) => state.configuration.theme);
29+
const keybinding = useSelector((state: State) => state.configuration.ace.keybinding);
30+
const aceTheme = useSelector((state: State) => state.configuration.ace.theme);
31+
const monacoTheme = useSelector((state: State) => state.configuration.monaco.theme);
2732
const orientation = useSelector((state: State) => state.configuration.orientation);
2833
const editorStyle = useSelector((state: State) => state.configuration.editor);
29-
const pairCharacters = useSelector((state: State) => state.configuration.pairCharacters);
34+
const pairCharacters = useSelector((state: State) => state.configuration.ace.pairCharacters);
3035
const assemblyFlavor = useSelector((state: State) => state.configuration.assemblyFlavor);
3136
const demangleAssembly = useSelector((state: State) => state.configuration.demangleAssembly);
3237
const processAssembly = useSelector((state: State) => state.configuration.processAssembly);
3338

3439
const dispatch = useDispatch();
35-
const changeTheme = useCallback((t) => dispatch(actions.changeTheme(t)), [dispatch]);
40+
const changeAceTheme = useCallback((t) => dispatch(actions.changeAceTheme(t)), [dispatch]);
41+
const changeMonacoTheme = useCallback((t) => dispatch(actions.changeMonacoTheme(t)), [dispatch]);
3642
const changeKeybinding = useCallback((k) => dispatch(actions.changeKeybinding(k)), [dispatch]);
3743
const changeOrientation = useCallback((o) => dispatch(actions.changeOrientation(o)), [dispatch]);
3844
const changeEditorStyle = useCallback((e) => dispatch(actions.changeEditor(e)), [dispatch]);
@@ -44,15 +50,15 @@ const ConfigMenu: React.SFC<ConfigMenuProps> = () => {
4450
return (
4551
<Fragment>
4652
<MenuGroup title="Editor">
47-
<EitherConfig
48-
id="editor-style"
49-
name="Style"
50-
a={Editor.Simple}
51-
b={Editor.Advanced}
53+
<SelectConfig
54+
name="Editor"
5255
value={editorStyle}
53-
onChange={changeEditorStyle} />
54-
55-
{editorStyle === Editor.Advanced && (
56+
onChange={changeEditorStyle}
57+
>
58+
{[Editor.Simple, Editor.Ace, Editor.Monaco]
59+
.map(k => <option key={k} value={k}>{k}</option>)}
60+
</SelectConfig>
61+
{editorStyle === Editor.Ace && (
5662
<Fragment>
5763
<SelectConfig
5864
name="Keybinding"
@@ -64,8 +70,8 @@ const ConfigMenu: React.SFC<ConfigMenuProps> = () => {
6470

6571
<SelectConfig
6672
name="Theme"
67-
value={theme}
68-
onChange={changeTheme}
73+
value={aceTheme}
74+
onChange={changeAceTheme}
6975
>
7076
{ACE_THEMES.map(t => <option key={t} value={t}>{t}</option>)}
7177
</SelectConfig>
@@ -79,6 +85,17 @@ const ConfigMenu: React.SFC<ConfigMenuProps> = () => {
7985
onChange={changePairCharacters} />
8086
</Fragment>
8187
)}
88+
{editorStyle === Editor.Monaco && (
89+
<Fragment>
90+
<SelectConfig
91+
name="Theme"
92+
value={monacoTheme}
93+
onChange={changeMonacoTheme}
94+
>
95+
{MONACO_THEMES.map(t => <option key={t} value={t}>{t}</option>)}
96+
</SelectConfig>
97+
</Fragment>
98+
)}
8299
</MenuGroup>
83100

84101
<MenuGroup title="UI">

ui/frontend/Editor.module.css

-14
This file was deleted.

ui/frontend/Notifications.tsx

+10-30
Original file line numberDiff line numberDiff line change
@@ -9,49 +9,29 @@ import * as selectors from './selectors';
99

1010
import styles from './Notifications.module.css';
1111

12-
const EDITION_URL = 'https://doc.rust-lang.org/edition-guide/';
13-
const SURVEY_URL = 'https://blog.rust-lang.org/2021/12/08/survey-launch.html';
12+
const MONACO_EDITOR_URL = 'https://microsoft.github.io/monaco-editor/';
1413

1514
const Notifications: React.SFC = () => {
1615
return (
1716
<Portal>
1817
<div className={styles.container}>
19-
<Rust2021IsDefaultNotification />
20-
<RustSurvey2021Notification />
18+
<MonacoEditorAvailableNotification />
2119
</div>
2220
</Portal>
2321
);
2422
};
2523

26-
const Rust2021IsDefaultNotification: React.SFC = () => {
27-
const showRust2021IsDefault = useSelector(selectors.showRust2021IsDefaultSelector);
24+
const MonacoEditorAvailableNotification: React.SFC = () => {
25+
const monicoEditorAvailable = useSelector(selectors.showMonicoEditorAvailableSelector);
2826

2927
const dispatch = useDispatch();
30-
const seenRust2021IsDefault = useCallback(() => dispatch(actions.seenRust2021IsDefault()), [dispatch]);
28+
const seenMonicoEditorAvailable = useCallback(() => dispatch(actions.seenMonicoEditorAvailable()), [dispatch]);
3129

32-
return showRust2021IsDefault && (
33-
<Notification onClose={seenRust2021IsDefault}>
34-
As of Rust 1.56, the default edition of Rust is now Rust
35-
2021. Learn more about editions in the <a href={EDITION_URL}>Edition Guide</a>.
36-
To specify which edition to use, use the advanced compilation options menu.
37-
</Notification>
38-
);
39-
};
40-
41-
42-
const RustSurvey2021Notification: React.SFC = () => {
43-
const showRustSurvey2021 = useSelector(selectors.showRustSurvey2021Selector);
44-
45-
const dispatch = useDispatch();
46-
const seenRustSurvey2021 = useCallback(() => dispatch(actions.seenRustSurvey2021()), [dispatch]);
47-
48-
return showRustSurvey2021 && (
49-
<Notification onClose={seenRustSurvey2021}>
50-
Please help us take a look at who the Rust community is
51-
composed of, how the Rust project is doing, and how we can
52-
improve the Rust programming experience by completing the <a
53-
href={SURVEY_URL}>2021 State of Rust Survey</a>. Whether or
54-
not you use Rust today, we want to know your opinions.
30+
return monicoEditorAvailable && (
31+
<Notification onClose={seenMonicoEditorAvailable}>
32+
The <a href={MONACO_EDITOR_URL}>Monaco Editor</a>, the code editor
33+
that powers VS Code, is now available in the playground. Choose
34+
your preferred editor from the Config menu.
5535
</Notification>
5636
);
5737
};

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/actions.ts

+10-6
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ export enum ActionType {
6363
SetPage = 'SET_PAGE',
6464
ChangeEditor = 'CHANGE_EDITOR',
6565
ChangeKeybinding = 'CHANGE_KEYBINDING',
66-
ChangeTheme = 'CHANGE_THEME',
66+
ChangeAceTheme = 'CHANGE_ACE_THEME',
67+
ChangeMonacoTheme = 'CHANGE_MONACO_THEME',
6768
ChangePairCharacters = 'CHANGE_PAIR_CHARACTERS',
6869
ChangeOrientation = 'CHANGE_ORIENTATION',
6970
ChangeAssemblyFlavor = 'CHANGE_ASSEMBLY_FLAVOR',
@@ -138,8 +139,11 @@ export const changeEditor = (editor: Editor) =>
138139
export const changeKeybinding = (keybinding: string) =>
139140
createAction(ActionType.ChangeKeybinding, { keybinding });
140141

141-
export const changeTheme = (theme: string) =>
142-
createAction(ActionType.ChangeTheme, { theme });
142+
export const changeAceTheme = (theme: string) =>
143+
createAction(ActionType.ChangeAceTheme, { theme });
144+
145+
export const changeMonacoTheme = (theme: string) =>
146+
createAction(ActionType.ChangeMonacoTheme, { theme });
143147

144148
export const changePairCharacters = (pairCharacters: PairCharacters) =>
145149
createAction(ActionType.ChangePairCharacters, { pairCharacters });
@@ -701,8 +705,7 @@ export function performVersionsLoad(): ThunkAction {
701705
const notificationSeen = (notification: Notification) =>
702706
createAction(ActionType.NotificationSeen, { notification });
703707

704-
export const seenRust2021IsDefault = () => notificationSeen(Notification.Rust2021IsDefault);
705-
export const seenRustSurvey2021 = () => notificationSeen(Notification.RustSurvey2021);
708+
export const seenMonicoEditorAvailable = () => notificationSeen(Notification.MonacoEditorAvailable);
706709

707710
export const browserWidthChanged = (isSmall: boolean) =>
708711
createAction(ActionType.BrowserWidthChanged, { isSmall });
@@ -816,7 +819,8 @@ export type Action =
816819
| ReturnType<typeof changeOrientation>
817820
| ReturnType<typeof changePrimaryAction>
818821
| ReturnType<typeof changeProcessAssembly>
819-
| ReturnType<typeof changeTheme>
822+
| ReturnType<typeof changeAceTheme>
823+
| ReturnType<typeof changeMonacoTheme>
820824
| ReturnType<typeof requestExecute>
821825
| ReturnType<typeof receiveExecuteSuccess>
822826
| ReturnType<typeof receiveExecuteFailure>

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

+19-19
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(() => {
@@ -292,7 +292,7 @@ const AdvancedEditor: React.SFC<AdvancedEditorProps> = props => {
292292
}, []));
293293

294294
return (
295-
<div className={styles.advanced} ref={child} />
295+
<div className={styles.ace} ref={child} />
296296
);
297297
};
298298

@@ -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, AceEditorAsyncState> {
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
}
@@ -447,13 +447,13 @@ class AdvancedEditorAsync extends React.Component<AdvancedEditorAsyncProps, Adva
447447

448448
private async requireLibraries() {
449449
return import(
450-
/* webpackChunkName: "advanced-editor" */
451-
'./advanced-editor'
450+
/* webpackChunkName: "ace-editor" */
451+
'./ace-editor'
452452
);
453453
}
454454
}
455455

456-
interface AdvancedEditorAsyncState {
456+
interface AceEditorAsyncState {
457457
theme?: string;
458458
keybinding?: string;
459459
themeState: LoadState;
@@ -472,7 +472,7 @@ interface PropsFromState {
472472
}
473473

474474
const mapStateToProps = (state: State) => {
475-
const { configuration: { theme, keybinding, pairCharacters } } = state;
475+
const { configuration: { ace: { theme, keybinding, pairCharacters } } } = state;
476476

477477
return {
478478
theme,
@@ -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/Editor.module.css

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
.container {
2+
composes: -autoSize from '../shared.module.css';
3+
position: relative;
4+
}
5+
6+
.-advanced {
7+
composes: -bodyMonospace -autoSize from '../shared.module.css';
8+
position: absolute;
9+
}
10+
11+
.ace {
12+
composes: -advanced;
13+
}
14+
15+
.monaco {
16+
composes: -advanced;
17+
}
18+
19+
.simple {
20+
composes: -advanced;
21+
border: none;
22+
}

0 commit comments

Comments
 (0)