aboutsummaryrefslogtreecommitdiffstats
path: root/language-web/src/main/js
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <marussy@mit.bme.hu>2021-10-02 02:11:31 +0200
committerLibravatar Kristóf Marussy <marussy@mit.bme.hu>2021-10-02 02:11:31 +0200
commitb834db0fd424e7ab02fcd5e509d855f2d97863bd (patch)
treeb56ce9b8f752d8ca98e1d9082c63542e5dd993c1 /language-web/src/main/js
parentfeat: skeleton for language to store mapping (diff)
downloadrefinery-b834db0fd424e7ab02fcd5e509d855f2d97863bd.tar.gz
refinery-b834db0fd424e7ab02fcd5e509d855f2d97863bd.tar.zst
refinery-b834db0fd424e7ab02fcd5e509d855f2d97863bd.zip
perf(web): split off CodeMirror chunks
Also optimizes statis asset caching.
Diffstat (limited to 'language-web/src/main/js')
-rw-r--r--language-web/src/main/js/App.tsx6
-rw-r--r--language-web/src/main/js/editor/Editor.tsx20
-rw-r--r--language-web/src/main/js/editor/EditorArea.tsx42
-rw-r--r--language-web/src/main/js/editor/EditorStore.ts75
-rw-r--r--language-web/src/main/js/editor/editor.ts18
-rw-r--r--language-web/src/main/js/theme/ThemeStore.ts4
6 files changed, 127 insertions, 38 deletions
diff --git a/language-web/src/main/js/App.tsx b/language-web/src/main/js/App.tsx
index 17d4f339..5cd157fa 100644
--- a/language-web/src/main/js/App.tsx
+++ b/language-web/src/main/js/App.tsx
@@ -9,12 +9,12 @@ import MenuIcon from '@material-ui/icons/Menu';
9import PlayArrowIcon from '@material-ui/icons/PlayArrow'; 9import PlayArrowIcon from '@material-ui/icons/PlayArrow';
10 10
11import { makeStyles } from './makeStyles'; 11import { makeStyles } from './makeStyles';
12import { Editor } from './editor/Editor'; 12import { EditorArea } from './editor/EditorArea';
13import { EditorButtons } from './editor/EditorButtons'; 13import { EditorButtons } from './editor/EditorButtons';
14 14
15const useStyles = makeStyles()((theme) => ({ 15const useStyles = makeStyles()((theme) => ({
16 container: { 16 container: {
17 maxHeight: '100vh', 17 height: '100vh',
18 }, 18 },
19 menuButton: { 19 menuButton: {
20 marginRight: theme.spacing(2), 20 marginRight: theme.spacing(2),
@@ -85,7 +85,7 @@ export const App = (): JSX.Element => {
85 flexShrink={1} 85 flexShrink={1}
86 className={cx(classes.editorBox)} 86 className={cx(classes.editorBox)}
87 > 87 >
88 <Editor /> 88 <EditorArea />
89 </Box> 89 </Box>
90 </Box> 90 </Box>
91 ); 91 );
diff --git a/language-web/src/main/js/editor/Editor.tsx b/language-web/src/main/js/editor/Editor.tsx
deleted file mode 100644
index 9badb6a3..00000000
--- a/language-web/src/main/js/editor/Editor.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
1import { observer } from 'mobx-react-lite';
2import React from 'react';
3import { Controlled as CodeMirror } from 'react-codemirror2';
4
5import { useRootStore } from '../RootStore';
6
7export const Editor = observer(() => {
8 const { editorStore } = useRootStore();
9
10 return (
11 <CodeMirror
12 value={editorStore.value}
13 options={editorStore.codeMirrorOptions}
14 editorDidMount={(editor) => editorStore.editorDidMount(editor)}
15 editorWillUnmount={() => editorStore.editorWillUnmount()}
16 onBeforeChange={(_editor, _data, value) => editorStore.updateValue(value)}
17 onChange={() => editorStore.reportChanged()}
18 />
19 );
20});
diff --git a/language-web/src/main/js/editor/EditorArea.tsx b/language-web/src/main/js/editor/EditorArea.tsx
new file mode 100644
index 00000000..f07a0ad8
--- /dev/null
+++ b/language-web/src/main/js/editor/EditorArea.tsx
@@ -0,0 +1,42 @@
1import { observer } from 'mobx-react-lite';
2import React, { useRef } from 'react';
3
4import { useRootStore } from '../RootStore';
5
6export const EditorArea = observer(() => {
7 const { editorStore } = useRootStore();
8 const { CodeMirror } = editorStore.chunk || {};
9 const fallbackTextarea = useRef<HTMLTextAreaElement>(null);
10
11 if (!CodeMirror) {
12 return (
13 <textarea
14 value={editorStore.value}
15 onChange={(e) => editorStore.updateValue(e.target.value)}
16 ref={fallbackTextarea}
17 className={`problem-fallback-editor cm-s-${editorStore.codeMirrorTheme}`}
18 >
19 </textarea>
20 );
21 }
22
23 const textarea = fallbackTextarea.current;
24 if (textarea) {
25 editorStore.setInitialSelection(
26 textarea.selectionStart,
27 textarea.selectionEnd,
28 document.activeElement === textarea,
29 );
30 }
31
32 return (
33 <CodeMirror
34 value={editorStore.value}
35 options={editorStore.codeMirrorOptions}
36 editorDidMount={(editor) => editorStore.editorDidMount(editor)}
37 editorWillUnmount={() => editorStore.editorWillUnmount()}
38 onBeforeChange={(_editor, _data, value) => editorStore.updateValue(value)}
39 onChange={() => editorStore.reportChanged()}
40 />
41 );
42});
diff --git a/language-web/src/main/js/editor/EditorStore.ts b/language-web/src/main/js/editor/EditorStore.ts
index 5da45ac1..1ac2e79f 100644
--- a/language-web/src/main/js/editor/EditorStore.ts
+++ b/language-web/src/main/js/editor/EditorStore.ts
@@ -1,19 +1,14 @@
1import { Editor, EditorConfiguration } from 'codemirror'; 1import type { Editor, EditorConfiguration } from 'codemirror';
2import 'codemirror/addon/selection/active-line';
3import { 2import {
4 createAtom, 3 createAtom,
5 makeAutoObservable, 4 makeAutoObservable,
6 observable, 5 observable,
6 runInAction,
7} from 'mobx'; 7} from 'mobx';
8import 'mode-problem'; 8import type { IXtextOptions, IXtextServices } from 'xtext/xtext-codemirror';
9import {
10 IXtextOptions,
11 IXtextServices,
12 createServices,
13 removeServices,
14} from 'xtext/xtext-codemirror';
15 9
16import { ThemeStore } from '../theme/ThemeStore'; 10import type { IEditorChunk } from './editor';
11import type { ThemeStore } from '../theme/ThemeStore';
17 12
18const xtextLang = 'problem'; 13const xtextLang = 'problem';
19 14
@@ -33,6 +28,8 @@ export class EditorStore {
33 28
34 atom; 29 atom;
35 30
31 chunk?: IEditorChunk;
32
36 editor?: Editor; 33 editor?: Editor;
37 34
38 xtextServices?: IXtextServices; 35 xtextServices?: IXtextServices;
@@ -41,15 +38,56 @@ export class EditorStore {
41 38
42 showLineNumbers = false; 39 showLineNumbers = false;
43 40
41 initialSelection!: { start: number, end: number, focused: boolean };
42
44 constructor(themeStore: ThemeStore) { 43 constructor(themeStore: ThemeStore) {
45 this.themeStore = themeStore; 44 this.themeStore = themeStore;
46 this.atom = createAtom('EditorStore'); 45 this.atom = createAtom('EditorStore');
46 this.resetInitialSelection();
47 makeAutoObservable(this, { 47 makeAutoObservable(this, {
48 themeStore: false, 48 themeStore: false,
49 atom: false, 49 atom: false,
50 chunk: observable.ref,
50 editor: observable.ref, 51 editor: observable.ref,
51 xtextServices: observable.ref, 52 xtextServices: observable.ref,
53 initialSelection: false,
52 }); 54 });
55 import('./editor').then(({ editorChunk }) => {
56 runInAction(() => {
57 this.chunk = editorChunk;
58 });
59 }).catch((error) => {
60 console.warn('Error while loading editor', error);
61 });
62 }
63
64 setInitialSelection(start: number, end: number, focused: boolean): void {
65 this.initialSelection = { start, end, focused };
66 this.applyInitialSelectionToEditor();
67 }
68
69 private resetInitialSelection(): void {
70 this.initialSelection = {
71 start: 0,
72 end: 0,
73 focused: false,
74 };
75 }
76
77 private applyInitialSelectionToEditor(): void {
78 if (this.editor) {
79 const { start, end, focused } = this.initialSelection;
80 const doc = this.editor.getDoc();
81 const startPos = doc.posFromIndex(start);
82 const endPos = doc.posFromIndex(end);
83 doc.setSelection(startPos, endPos, {
84 scroll: true,
85 });
86 if (focused) {
87 this.editor.focus();
88 }
89 this.resetInitialSelection();
90 }
53 } 91 }
54 92
55 /** 93 /**
@@ -61,16 +99,23 @@ export class EditorStore {
61 * @param newEditor The new CodeMirror instance 99 * @param newEditor The new CodeMirror instance
62 */ 100 */
63 editorDidMount(newEditor: Editor): void { 101 editorDidMount(newEditor: Editor): void {
102 if (!this.chunk) {
103 throw new Error('Editor not loaded yet');
104 }
64 if (this.editor) { 105 if (this.editor) {
65 throw new Error('CoreMirror editor mounted before unmounting'); 106 throw new Error('CoreMirror editor mounted before unmounting');
66 } 107 }
67 this.editor = newEditor; 108 this.editor = newEditor;
68 this.xtextServices = createServices(newEditor, xtextOptions); 109 this.xtextServices = this.chunk.createServices(newEditor, xtextOptions);
110 this.applyInitialSelectionToEditor();
69 } 111 }
70 112
71 editorWillUnmount(): void { 113 editorWillUnmount(): void {
114 if (!this.chunk) {
115 throw new Error('Editor not loaded yet');
116 }
72 if (this.editor) { 117 if (this.editor) {
73 removeServices(this.editor); 118 this.chunk.removeServices(this.editor);
74 } 119 }
75 delete this.editor; 120 delete this.editor;
76 delete this.xtextServices; 121 delete this.xtextServices;
@@ -93,10 +138,14 @@ export class EditorStore {
93 this.atom.reportObserved(); 138 this.atom.reportObserved();
94 } 139 }
95 140
141 get codeMirrorTheme(): string {
142 return `problem-${this.themeStore.className}`;
143 }
144
96 get codeMirrorOptions(): EditorConfiguration { 145 get codeMirrorOptions(): EditorConfiguration {
97 return { 146 return {
98 ...codeMirrorGlobalOptions, 147 ...codeMirrorGlobalOptions,
99 theme: this.themeStore.codeMirrorTheme, 148 theme: this.codeMirrorTheme,
100 lineNumbers: this.showLineNumbers, 149 lineNumbers: this.showLineNumbers,
101 }; 150 };
102 } 151 }
diff --git a/language-web/src/main/js/editor/editor.ts b/language-web/src/main/js/editor/editor.ts
new file mode 100644
index 00000000..fbf8796b
--- /dev/null
+++ b/language-web/src/main/js/editor/editor.ts
@@ -0,0 +1,18 @@
1import 'codemirror/addon/selection/active-line';
2import 'mode-problem';
3import { Controlled } from 'react-codemirror2';
4import { createServices, removeServices } from 'xtext/xtext-codemirror';
5
6export interface IEditorChunk {
7 CodeMirror: typeof Controlled;
8
9 createServices: typeof createServices;
10
11 removeServices: typeof removeServices;
12}
13
14export const editorChunk: IEditorChunk = {
15 CodeMirror: Controlled,
16 createServices,
17 removeServices,
18};
diff --git a/language-web/src/main/js/theme/ThemeStore.ts b/language-web/src/main/js/theme/ThemeStore.ts
index 0e4aeb23..2644a96a 100644
--- a/language-web/src/main/js/theme/ThemeStore.ts
+++ b/language-web/src/main/js/theme/ThemeStore.ts
@@ -51,7 +51,7 @@ export class ThemeStore {
51 return responsiveFontSizes(materialUiTheme); 51 return responsiveFontSizes(materialUiTheme);
52 } 52 }
53 53
54 get codeMirrorTheme(): string { 54 get className(): string {
55 return `problem-${this.currentThemeData.className}`; 55 return this.currentThemeData.className;
56 } 56 }
57} 57}