diff options
Diffstat (limited to 'language-web')
-rw-r--r-- | language-web/package.json | 17 | ||||
-rw-r--r-- | language-web/src/main/css/index.scss | 213 | ||||
-rw-r--r-- | language-web/src/main/js/RootStore.tsx | 4 | ||||
-rw-r--r-- | language-web/src/main/js/editor/EditorArea.tsx | 142 | ||||
-rw-r--r-- | language-web/src/main/js/editor/EditorButtons.tsx | 35 | ||||
-rw-r--r-- | language-web/src/main/js/editor/EditorParent.ts | 61 | ||||
-rw-r--r-- | language-web/src/main/js/editor/EditorStore.ts | 294 | ||||
-rw-r--r-- | language-web/src/main/js/editor/editor.ts | 18 | ||||
-rw-r--r-- | language-web/src/main/js/index.tsx | 3 | ||||
-rw-r--r-- | language-web/src/main/js/theme/ThemeStore.ts | 4 | ||||
-rw-r--r-- | language-web/yarn.lock | 220 |
11 files changed, 588 insertions, 423 deletions
diff --git a/language-web/package.json b/language-web/package.json index 7a931d95..d55968ee 100644 --- a/language-web/package.json +++ b/language-web/package.json | |||
@@ -62,20 +62,33 @@ | |||
62 | "dependencies": { | 62 | "dependencies": { |
63 | "ansi-styles": "^6.1.0", | 63 | "ansi-styles": "^6.1.0", |
64 | "@babel/runtime": "^7.15.4", | 64 | "@babel/runtime": "^7.15.4", |
65 | "@codemirror/autocomplete": "^0.19.3", | ||
66 | "@codemirror/closebrackets": "^0.19.0", | ||
67 | "@codemirror/commands": "^0.19.5", | ||
68 | "@codemirror/comment": "^0.19.0", | ||
69 | "@codemirror/fold": "^0.19.0", | ||
70 | "@codemirror/gutter": "^0.19.2", | ||
71 | "@codemirror/highlight": "^0.19.6", | ||
72 | "@codemirror/history": "^0.19.0", | ||
73 | "@codemirror/language": "^0.19.3", | ||
74 | "@codemirror/lint": "^0.19.2", | ||
75 | "@codemirror/matchbrackets": "^0.19.3", | ||
76 | "@codemirror/rectangular-selection": "^0.19.1", | ||
77 | "@codemirror/search": "^0.19.2", | ||
78 | "@codemirror/state": "^0.19.0", | ||
79 | "@codemirror/view": "^0.19.9", | ||
65 | "@emotion/react": "^11.4.1", | 80 | "@emotion/react": "^11.4.1", |
66 | "@emotion/styled": "^11.3.0", | 81 | "@emotion/styled": "^11.3.0", |
67 | "@fontsource/jetbrains-mono": "^4.5.0", | 82 | "@fontsource/jetbrains-mono": "^4.5.0", |
68 | "@fontsource/roboto": "^4.5.0", | 83 | "@fontsource/roboto": "^4.5.0", |
69 | "@mui/material": "5.0.2", | 84 | "@mui/material": "5.0.2", |
70 | "@mui/icons-material": "5.0.1", | 85 | "@mui/icons-material": "5.0.1", |
71 | "codemirror": "^5.63.1", | ||
72 | "jquery": "^3.6.0", | 86 | "jquery": "^3.6.0", |
73 | "loglevel": "^1.7.1", | 87 | "loglevel": "^1.7.1", |
74 | "loglevel-plugin-prefix": "^0.8.4", | 88 | "loglevel-plugin-prefix": "^0.8.4", |
75 | "mobx": "^6.3.3", | 89 | "mobx": "^6.3.3", |
76 | "mobx-react-lite": "^3.2.1", | 90 | "mobx-react-lite": "^3.2.1", |
77 | "react": "^17.0.2", | 91 | "react": "^17.0.2", |
78 | "react-codemirror2": "npm:react17-codemirror2@^7.2.3", | ||
79 | "react-dom": "^17.0.2" | 92 | "react-dom": "^17.0.2" |
80 | } | 93 | } |
81 | } | 94 | } |
diff --git a/language-web/src/main/css/index.scss b/language-web/src/main/css/index.scss index 54f3a654..ad876aaf 100644 --- a/language-web/src/main/css/index.scss +++ b/language-web/src/main/css/index.scss | |||
@@ -1,13 +1,6 @@ | |||
1 | @use 'sass:map'; | ||
2 | @use '@fontsource/roboto/scss/mixins' as Roboto; | 1 | @use '@fontsource/roboto/scss/mixins' as Roboto; |
3 | @use '@fontsource/jetbrains-mono/scss/mixins' as JetbrainsMono; | 2 | @use '@fontsource/jetbrains-mono/scss/mixins' as JetbrainsMono; |
4 | 3 | ||
5 | @import 'codemirror/lib/codemirror'; | ||
6 | @import 'codemirror/addon/hint/show-hint'; | ||
7 | @import 'codemirror/theme/material-darker'; | ||
8 | |||
9 | @import './themes'; | ||
10 | |||
11 | $fontWeights: 300, 400, 500, 700; | 4 | $fontWeights: 300, 400, 500, 700; |
12 | @each $weight in $fontWeights { | 5 | @each $weight in $fontWeights { |
13 | @include Roboto.fontFace($fontName: 'Roboto', $weight: $weight); | 6 | @include Roboto.fontFace($fontName: 'Roboto', $weight: $weight); |
@@ -21,209 +14,3 @@ $monoFontWeights: 400, 700; | |||
21 | } | 14 | } |
22 | @include JetbrainsMono.fontFaceVariable($fontName: 'JetBrains MonoVariable'); | 15 | @include JetbrainsMono.fontFaceVariable($fontName: 'JetBrains MonoVariable'); |
23 | @include JetbrainsMono.fontFaceVariable($fontName: 'JetBrains MonoVariable', $style: italic); | 16 | @include JetbrainsMono.fontFaceVariable($fontName: 'JetBrains MonoVariable', $style: italic); |
24 | |||
25 | body { | ||
26 | font-family: 'Roboto', sans-serif; | ||
27 | } | ||
28 | |||
29 | .CodeMirror { | ||
30 | height: 100%; | ||
31 | } | ||
32 | |||
33 | .problem-fallback-editor { | ||
34 | display: block; | ||
35 | height: 100%; | ||
36 | width: 100%; | ||
37 | resize: none; | ||
38 | border: none; | ||
39 | outline: none; | ||
40 | padding: 4px 4px 4px 16px; | ||
41 | white-space: pre; | ||
42 | overflow-wrap: normal; | ||
43 | overflow: auto; | ||
44 | } | ||
45 | |||
46 | .CodeMirror, .CodeMirror-hints, .problem-fallback-editor { | ||
47 | font-size: 16px; | ||
48 | font-family: 'JetBrains MonoVariable', 'JetBrains Mono', monospace; | ||
49 | font-feature-settings: 'liga', 'calt'; | ||
50 | font-weight: 400; | ||
51 | text-rendering: optimizeLegibility; | ||
52 | line-height: 1.35; | ||
53 | letter-spacing: 0; | ||
54 | } | ||
55 | |||
56 | @each $themeName, $theme in $themes { | ||
57 | .cm-s-problem-#{$themeName} { | ||
58 | &.CodeMirror { | ||
59 | background: map.get($theme, 'background'); | ||
60 | color: map.get($theme, 'foreground'); | ||
61 | } | ||
62 | |||
63 | &.problem-fallback-editor { | ||
64 | background: map.get($theme, 'background'); | ||
65 | color: map.get($theme, 'foreground'); | ||
66 | caret-color: map.get($theme, 'cursor'); | ||
67 | |||
68 | &::selection { | ||
69 | background: map.get($theme, 'selection'); | ||
70 | } | ||
71 | } | ||
72 | |||
73 | .CodeMirror-gutters { | ||
74 | background: map.get($theme, 'background'); | ||
75 | border: none; | ||
76 | } | ||
77 | |||
78 | .CodeMirror-cursor { | ||
79 | border-left: 1px solid map.get($theme, 'cursor'); | ||
80 | } | ||
81 | |||
82 | div.CodeMirror-selected, | ||
83 | &.CodeMirror-focused div.CodeMirror-selected, | ||
84 | .CodeMirror-line::selection, | ||
85 | .CodeMirror-line > span::selection, | ||
86 | .CodeMirror-line > span > span::selection { | ||
87 | background: map.get($theme, 'selection'); | ||
88 | } | ||
89 | |||
90 | .CodeMirror-guttermarker, | ||
91 | .CodeMirror-guttermarker-subtle, | ||
92 | .CodeMirror-linenumber { | ||
93 | color: map.get($theme, 'lineNumber'); | ||
94 | } | ||
95 | |||
96 | .CodeMirror-activeline-background { | ||
97 | background: map.get($theme, 'currentLine'); | ||
98 | } | ||
99 | |||
100 | .CodeMirror-activeline-gutter { | ||
101 | background: map.get($theme, 'currentLine'); | ||
102 | |||
103 | .CodeMirror-guttermarker, | ||
104 | .CodeMirror-guttermarker-subtle, | ||
105 | .CodeMirror-linenumber { | ||
106 | color: map.get($theme, 'foreground'); | ||
107 | } | ||
108 | } | ||
109 | |||
110 | .cm-keyword { | ||
111 | color: map.get($theme, 'keyword'); | ||
112 | } | ||
113 | |||
114 | .cm-number { | ||
115 | color: map.get($theme, 'number'); | ||
116 | } | ||
117 | |||
118 | .cm-lparen, .cm-rparen { | ||
119 | color: map.get($theme, 'delimiter'); | ||
120 | } | ||
121 | |||
122 | .cm-comment { | ||
123 | color: map.get($theme, 'comment'); | ||
124 | font-style: italic; | ||
125 | } | ||
126 | |||
127 | .problem-predicate, .problem-class, .problem-reference, .problem-enum { | ||
128 | color: map.get($theme, 'predicate'); | ||
129 | } | ||
130 | |||
131 | .problem-unique-node { | ||
132 | color: map.get($theme, 'uniqueNode'); | ||
133 | } | ||
134 | |||
135 | .problem-variable { | ||
136 | color: map.get($theme, 'variable'); | ||
137 | } | ||
138 | } | ||
139 | } | ||
140 | |||
141 | .CodeMirror-hints { | ||
142 | background: #333; | ||
143 | border: 0; | ||
144 | border-radius: 4px; | ||
145 | box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.2), | ||
146 | 0 5px 8px 0 rgba(0, 0, 0, 0.14), | ||
147 | 0 1px 8px 0 rgba(0, 0, 0, 0.12); | ||
148 | padding: 0; | ||
149 | } | ||
150 | |||
151 | .CodeMirror-hint { | ||
152 | color: #fff; | ||
153 | border-radius: 0; | ||
154 | } | ||
155 | |||
156 | li.CodeMirror-hint-active { | ||
157 | background: rgba(128, 203, 196, 0.2); | ||
158 | } | ||
159 | |||
160 | .annotations-gutter { | ||
161 | width: 12px; | ||
162 | } | ||
163 | |||
164 | .xtext-annotation_error { | ||
165 | width: 12px; | ||
166 | height: 1em; | ||
167 | background-image: url('images/error_an.gif'); | ||
168 | background-repeat: no-repeat; | ||
169 | background-position: bottom; | ||
170 | } | ||
171 | |||
172 | .xtext-annotation_warning { | ||
173 | width: 12px; | ||
174 | height: 1em; | ||
175 | background-image: url('images/warning_an.gif'); | ||
176 | background-repeat: no-repeat; | ||
177 | background-position: bottom; | ||
178 | } | ||
179 | |||
180 | .xtext-annotation_info { | ||
181 | width: 12px; | ||
182 | height: 1em; | ||
183 | background-image: url('images/info_an.gif'); | ||
184 | background-repeat: no-repeat; | ||
185 | background-position: bottom; | ||
186 | } | ||
187 | |||
188 | .xtext-marker_error { | ||
189 | z-index: 30; | ||
190 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAABmJLR0QA/wD/AP+gvaeTAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg=="); | ||
191 | background-repeat: repeat-x; | ||
192 | background-position: left bottom; | ||
193 | } | ||
194 | |||
195 | .xtext-marker_warning { | ||
196 | z-index: 20; | ||
197 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAABmJLR0QA/wD/AP+gvaeTAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII="); | ||
198 | background-repeat: repeat-x; | ||
199 | background-position: left bottom; | ||
200 | } | ||
201 | |||
202 | .xtext-marker_info { | ||
203 | z-index: 10; | ||
204 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAABmJLR0QA/wD/AP+gvaeTAAAANklEQVQI12NkgIIVRx8tZGBg6GZccfRRKAMDgw8DA0M3AwPDIiYGBoZKBgaG7ghruSsMDAwpABH5CoqwzCoTAAAAAElFTkSuQmCC"); | ||
205 | background-repeat: repeat-x; | ||
206 | background-position: left bottom; | ||
207 | } | ||
208 | |||
209 | .xtext-marker_read { | ||
210 | background: rgba(128, 203, 196, 0.2); | ||
211 | display: inline-block; | ||
212 | } | ||
213 | |||
214 | |||
215 | .xtext-marker_write { | ||
216 | background: rgba(255, 229, 100, 0.2); | ||
217 | display: inline-block; | ||
218 | } | ||
219 | |||
220 | .problem-abstract { | ||
221 | font-style: italic; | ||
222 | } | ||
223 | |||
224 | .problem-containment { | ||
225 | font-weight: 700; | ||
226 | } | ||
227 | .problem-new-node { | ||
228 | font-style: italic; | ||
229 | } | ||
diff --git a/language-web/src/main/js/RootStore.tsx b/language-web/src/main/js/RootStore.tsx index 88b8a445..96e1b26a 100644 --- a/language-web/src/main/js/RootStore.tsx +++ b/language-web/src/main/js/RootStore.tsx | |||
@@ -8,9 +8,9 @@ export class RootStore { | |||
8 | 8 | ||
9 | themeStore; | 9 | themeStore; |
10 | 10 | ||
11 | constructor() { | 11 | constructor(initialValue: string) { |
12 | this.themeStore = new ThemeStore(); | 12 | this.themeStore = new ThemeStore(); |
13 | this.editorStore = new EditorStore(this.themeStore); | 13 | this.editorStore = new EditorStore(initialValue, this.themeStore); |
14 | } | 14 | } |
15 | } | 15 | } |
16 | 16 | ||
diff --git a/language-web/src/main/js/editor/EditorArea.tsx b/language-web/src/main/js/editor/EditorArea.tsx index 531a57c9..58d65184 100644 --- a/language-web/src/main/js/editor/EditorArea.tsx +++ b/language-web/src/main/js/editor/EditorArea.tsx | |||
@@ -1,41 +1,123 @@ | |||
1 | import { Command, EditorView } from '@codemirror/view'; | ||
2 | import { closeSearchPanel, openSearchPanel } from '@codemirror/search'; | ||
3 | import { closeLintPanel, openLintPanel } from '@codemirror/lint'; | ||
1 | import { observer } from 'mobx-react-lite'; | 4 | import { observer } from 'mobx-react-lite'; |
2 | import React, { useRef } from 'react'; | 5 | import React, { useEffect, useRef, useState } from 'react'; |
3 | 6 | ||
7 | import { EditorParent } from './EditorParent'; | ||
8 | import { getLogger } from '../logging'; | ||
4 | import { useRootStore } from '../RootStore'; | 9 | import { useRootStore } from '../RootStore'; |
5 | 10 | ||
11 | const log = getLogger('EditorArea'); | ||
12 | |||
13 | function usePanel( | ||
14 | label: string, | ||
15 | stateToSet: boolean, | ||
16 | editorView: EditorView | null, | ||
17 | openCommand: Command, | ||
18 | closeCommand: Command, | ||
19 | ) { | ||
20 | const [cachedViewState, setCachedViewState] = useState<boolean>(false); | ||
21 | useEffect(() => { | ||
22 | if (editorView === null || cachedViewState === stateToSet) { | ||
23 | return; | ||
24 | } | ||
25 | const success = stateToSet ? openCommand(editorView) : closeCommand(editorView); | ||
26 | if (!success) { | ||
27 | log.error( | ||
28 | 'Failed to synchronize', | ||
29 | label, | ||
30 | 'panel state - store state:', | ||
31 | cachedViewState, | ||
32 | 'view state:', | ||
33 | stateToSet, | ||
34 | ); | ||
35 | } | ||
36 | setCachedViewState(stateToSet); | ||
37 | }, [ | ||
38 | stateToSet, | ||
39 | editorView, | ||
40 | cachedViewState, | ||
41 | label, | ||
42 | openCommand, | ||
43 | closeCommand, | ||
44 | ]); | ||
45 | return setCachedViewState; | ||
46 | } | ||
47 | |||
6 | export const EditorArea = observer(() => { | 48 | export const EditorArea = observer(() => { |
7 | const { editorStore } = useRootStore(); | 49 | const { editorStore } = useRootStore(); |
8 | const { CodeMirror } = editorStore.chunk || {}; | 50 | const editorParentRef = useRef<HTMLDivElement | null>(null); |
9 | const fallbackTextarea = useRef<HTMLTextAreaElement>(null); | 51 | const [editorViewState, setEditorViewState] = useState<EditorView | null>(null); |
10 | 52 | ||
11 | if (!CodeMirror) { | 53 | const setSearchPanelOpen = usePanel( |
12 | return ( | 54 | 'search', |
13 | <textarea | 55 | editorStore.showSearchPanel, |
14 | value={editorStore.value} | 56 | editorViewState, |
15 | onChange={(e) => editorStore.updateValue(e.target.value)} | 57 | openSearchPanel, |
16 | ref={fallbackTextarea} | 58 | closeSearchPanel, |
17 | className={`problem-fallback-editor cm-s-${editorStore.codeMirrorTheme}`} | 59 | ); |
18 | /> | 60 | |
19 | ); | 61 | const setLintPanelOpen = usePanel( |
20 | } | 62 | 'lint', |
21 | 63 | editorStore.showLintPanel, | |
22 | const textarea = fallbackTextarea.current; | 64 | editorViewState, |
23 | if (textarea) { | 65 | openLintPanel, |
24 | editorStore.setInitialSelection( | 66 | closeLintPanel, |
25 | textarea.selectionStart, | 67 | ); |
26 | textarea.selectionEnd, | 68 | |
27 | document.activeElement === textarea, | 69 | useEffect(() => { |
28 | ); | 70 | if (editorParentRef.current === null) { |
29 | } | 71 | // Nothing to clean up. |
72 | return () => {}; | ||
73 | } | ||
74 | |||
75 | const editorView = new EditorView({ | ||
76 | state: editorStore.state, | ||
77 | parent: editorParentRef.current, | ||
78 | dispatch: (transaction) => { | ||
79 | editorStore.onTransaction(transaction); | ||
80 | editorView.update([transaction]); | ||
81 | if (editorView.state !== editorStore.state) { | ||
82 | log.error( | ||
83 | 'Failed to synchronize editor state - store state:', | ||
84 | editorStore.state, | ||
85 | 'view state:', | ||
86 | editorView.state, | ||
87 | ); | ||
88 | } | ||
89 | }, | ||
90 | }); | ||
91 | setEditorViewState(editorView); | ||
92 | setSearchPanelOpen(false); | ||
93 | setLintPanelOpen(false); | ||
94 | // `dispatch` is bound to the view instance, | ||
95 | // so it does not have to be called as a method. | ||
96 | // eslint-disable-next-line @typescript-eslint/unbound-method | ||
97 | editorStore.updateDispatcher(editorView.dispatch); | ||
98 | log.info('Editor created'); | ||
99 | |||
100 | return () => { | ||
101 | editorStore.updateDispatcher(null); | ||
102 | editorView.destroy(); | ||
103 | log.info('Editor destroyed'); | ||
104 | }; | ||
105 | }, [ | ||
106 | editorParentRef, | ||
107 | editorStore, | ||
108 | setSearchPanelOpen, | ||
109 | setLintPanelOpen, | ||
110 | ]); | ||
30 | 111 | ||
31 | return ( | 112 | return ( |
32 | <CodeMirror | 113 | <EditorParent |
33 | value={editorStore.value} | 114 | className="dark" |
34 | options={editorStore.codeMirrorOptions} | 115 | sx={{ |
35 | editorDidMount={(editor) => editorStore.editorDidMount(editor)} | 116 | '.cm-lineNumbers': editorStore.showLineNumbers ? {} : { |
36 | editorWillUnmount={() => editorStore.editorWillUnmount()} | 117 | display: 'none !important', |
37 | onBeforeChange={(_editor, _data, value) => editorStore.updateValue(value)} | 118 | }, |
38 | onChange={() => editorStore.reportChanged()} | 119 | }} |
120 | ref={editorParentRef} | ||
39 | /> | 121 | /> |
40 | ); | 122 | ); |
41 | }); | 123 | }); |
diff --git a/language-web/src/main/js/editor/EditorButtons.tsx b/language-web/src/main/js/editor/EditorButtons.tsx index 56577e82..9622475c 100644 --- a/language-web/src/main/js/editor/EditorButtons.tsx +++ b/language-web/src/main/js/editor/EditorButtons.tsx | |||
@@ -2,8 +2,10 @@ import { observer } from 'mobx-react-lite'; | |||
2 | import Stack from '@mui/material/Stack'; | 2 | import Stack from '@mui/material/Stack'; |
3 | import ToggleButton from '@mui/material/ToggleButton'; | 3 | import ToggleButton from '@mui/material/ToggleButton'; |
4 | import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'; | 4 | import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'; |
5 | import CheckIcon from '@mui/icons-material/Check'; | ||
5 | import FormatListNumberedIcon from '@mui/icons-material/FormatListNumbered'; | 6 | import FormatListNumberedIcon from '@mui/icons-material/FormatListNumbered'; |
6 | import RedoIcon from '@mui/icons-material/Redo'; | 7 | import RedoIcon from '@mui/icons-material/Redo'; |
8 | import SearchIcon from '@mui/icons-material/Search'; | ||
7 | import UndoIcon from '@mui/icons-material/Undo'; | 9 | import UndoIcon from '@mui/icons-material/Undo'; |
8 | import React from 'react'; | 10 | import React from 'react'; |
9 | 11 | ||
@@ -37,15 +39,34 @@ export const EditorButtons = observer(() => { | |||
37 | <RedoIcon fontSize="small" /> | 39 | <RedoIcon fontSize="small" /> |
38 | </ToggleButton> | 40 | </ToggleButton> |
39 | </ToggleButtonGroup> | 41 | </ToggleButtonGroup> |
40 | <ToggleButton | 42 | <ToggleButtonGroup |
41 | selected={editorStore.showLineNumbers} | ||
42 | onChange={() => editorStore.toggleLineNumbers()} | ||
43 | size="small" | 43 | size="small" |
44 | aria-label="Show line numbers" | ||
45 | value="show-line-numbers" | ||
46 | > | 44 | > |
47 | <FormatListNumberedIcon fontSize="small" /> | 45 | <ToggleButton |
48 | </ToggleButton> | 46 | selected={editorStore.showLineNumbers} |
47 | onClick={() => editorStore.toggleLineNumbers()} | ||
48 | aria-label="Show line numbers" | ||
49 | value="show-line-numbers" | ||
50 | > | ||
51 | <FormatListNumberedIcon fontSize="small" /> | ||
52 | </ToggleButton> | ||
53 | <ToggleButton | ||
54 | selected={editorStore.showSearchPanel} | ||
55 | onClick={() => editorStore.toggleSearchPanel()} | ||
56 | aria-label="Show find/replace" | ||
57 | value="show-search-panel" | ||
58 | > | ||
59 | <SearchIcon fontSize="small" /> | ||
60 | </ToggleButton> | ||
61 | <ToggleButton | ||
62 | selected={editorStore.showLintPanel} | ||
63 | onClick={() => editorStore.toggleLintPanel()} | ||
64 | aria-label="Show errors and warnings" | ||
65 | value="show-lint-panel" | ||
66 | > | ||
67 | <CheckIcon fontSize="small" /> | ||
68 | </ToggleButton> | ||
69 | </ToggleButtonGroup> | ||
49 | </Stack> | 70 | </Stack> |
50 | ); | 71 | ); |
51 | }); | 72 | }); |
diff --git a/language-web/src/main/js/editor/EditorParent.ts b/language-web/src/main/js/editor/EditorParent.ts new file mode 100644 index 00000000..bf67522b --- /dev/null +++ b/language-web/src/main/js/editor/EditorParent.ts | |||
@@ -0,0 +1,61 @@ | |||
1 | import { styled } from '@mui/material/styles'; | ||
2 | |||
3 | export const EditorParent = styled('div')(({ theme }) => ({ | ||
4 | background: theme.palette.background.default, | ||
5 | '&, .cm-editor': { | ||
6 | height: '100%', | ||
7 | }, | ||
8 | '.cm-scroller': { | ||
9 | fontSize: 16, | ||
10 | fontFamily: '"JetBrains MonoVariable", "JetBrains Mono", monospace', | ||
11 | fontFeatureSettings: '"liga", "calt"', | ||
12 | fontWeight: 400, | ||
13 | letterSpacing: 0, | ||
14 | textRendering: 'optimizeLegibility', | ||
15 | color: theme.palette.text.secondary, | ||
16 | }, | ||
17 | '.cm-gutters': { | ||
18 | background: theme.palette.background.default, | ||
19 | color: theme.palette.text.disabled, | ||
20 | border: 'none', | ||
21 | }, | ||
22 | '.cm-specialChar': { | ||
23 | color: theme.palette.secondary.main, | ||
24 | }, | ||
25 | '.cm-activeLine': { | ||
26 | background: 'rgba(0, 0, 0, 0.3)', | ||
27 | }, | ||
28 | '.cm-activeLineGutter': { | ||
29 | background: 'rgba(0, 0, 0, 0.3)', | ||
30 | color: theme.palette.text.primary, | ||
31 | }, | ||
32 | '.cm-cursor, .cm-cursor-primary': { | ||
33 | borderColor: theme.palette.primary.main, | ||
34 | background: theme.palette.common.black, | ||
35 | }, | ||
36 | '.cm-selectionBackground': { | ||
37 | background: '#3e4453', | ||
38 | }, | ||
39 | '.cm-focused': { | ||
40 | outline: 'none', | ||
41 | '.cm-selectionBackground': { | ||
42 | background: '#3e4453', | ||
43 | }, | ||
44 | }, | ||
45 | '.cm-panels-top': { | ||
46 | color: theme.palette.text.secondary, | ||
47 | }, | ||
48 | '.cm-panel': { | ||
49 | background: theme.palette.background.paper, | ||
50 | borderTop: `1px solid ${theme.palette.divider}`, | ||
51 | 'button[name="close"]': { | ||
52 | // HACK We can't hook the panel close button to go through `EditorStore`, | ||
53 | // so we hide it altogether. | ||
54 | display: 'none', | ||
55 | }, | ||
56 | }, | ||
57 | '.cmt-comment': { | ||
58 | fontVariant: 'italic', | ||
59 | color: theme.palette.text.disabled, | ||
60 | }, | ||
61 | })); | ||
diff --git a/language-web/src/main/js/editor/EditorStore.ts b/language-web/src/main/js/editor/EditorStore.ts index 705020b9..326c02a1 100644 --- a/language-web/src/main/js/editor/EditorStore.ts +++ b/language-web/src/main/js/editor/EditorStore.ts | |||
@@ -1,201 +1,217 @@ | |||
1 | import type { Editor, EditorConfiguration } from 'codemirror'; | 1 | import { autocompletion, completionKeymap } from '@codemirror/autocomplete'; |
2 | import { closeBrackets, closeBracketsKeymap } from '@codemirror/closebrackets'; | ||
3 | import { defaultKeymap, indentWithTab } from '@codemirror/commands'; | ||
4 | import { commentKeymap } from '@codemirror/comment'; | ||
5 | import { foldGutter, foldKeymap } from '@codemirror/fold'; | ||
6 | import { highlightActiveLineGutter, lineNumbers } from '@codemirror/gutter'; | ||
7 | import { classHighlightStyle } from '@codemirror/highlight'; | ||
8 | import { | ||
9 | history, | ||
10 | historyKeymap, | ||
11 | redo, | ||
12 | redoDepth, | ||
13 | undo, | ||
14 | undoDepth, | ||
15 | } from '@codemirror/history'; | ||
16 | import { indentOnInput } from '@codemirror/language'; | ||
17 | import { lintKeymap } from '@codemirror/lint'; | ||
18 | import { bracketMatching } from '@codemirror/matchbrackets'; | ||
19 | import { rectangularSelection } from '@codemirror/rectangular-selection'; | ||
20 | import { searchConfig, searchKeymap } from '@codemirror/search'; | ||
21 | import { | ||
22 | EditorState, | ||
23 | StateCommand, | ||
24 | StateEffect, | ||
25 | Transaction, | ||
26 | TransactionSpec, | ||
27 | } from '@codemirror/state'; | ||
28 | import { | ||
29 | drawSelection, | ||
30 | EditorView, | ||
31 | highlightActiveLine, | ||
32 | highlightSpecialChars, | ||
33 | keymap, | ||
34 | } from '@codemirror/view'; | ||
2 | import { | 35 | import { |
3 | createAtom, | ||
4 | makeAutoObservable, | 36 | makeAutoObservable, |
5 | observable, | 37 | observable, |
6 | runInAction, | 38 | reaction, |
7 | } from 'mobx'; | 39 | } from 'mobx'; |
8 | import type { IXtextOptions, IXtextServices } from 'xtext/xtext-codemirror'; | ||
9 | 40 | ||
10 | import type { IEditorChunk } from './editor'; | ||
11 | import { getLogger } from '../logging'; | 41 | import { getLogger } from '../logging'; |
12 | import type { ThemeStore } from '../theme/ThemeStore'; | 42 | import type { ThemeStore } from '../theme/ThemeStore'; |
13 | 43 | ||
14 | const log = getLogger('EditorStore'); | 44 | const log = getLogger('EditorStore'); |
15 | 45 | ||
16 | const xtextLang = 'problem'; | ||
17 | |||
18 | const xtextOptions: IXtextOptions = { | ||
19 | xtextLang, | ||
20 | enableFormattingAction: true, | ||
21 | }; | ||
22 | |||
23 | const codeMirrorGlobalOptions: EditorConfiguration = { | ||
24 | mode: `xtext/${xtextLang}`, | ||
25 | indentUnit: 2, | ||
26 | styleActiveLine: true, | ||
27 | screenReaderLabel: 'Model source code', | ||
28 | inputStyle: 'contenteditable', | ||
29 | }; | ||
30 | |||
31 | export class EditorStore { | 46 | export class EditorStore { |
32 | themeStore; | 47 | themeStore; |
33 | 48 | ||
34 | atom; | 49 | state: EditorState; |
35 | 50 | ||
36 | chunk?: IEditorChunk; | 51 | emptyHistory: unknown; |
37 | 52 | ||
38 | editor?: Editor; | 53 | showLineNumbers = false; |
39 | 54 | ||
40 | xtextServices?: IXtextServices; | 55 | showSearchPanel = false; |
41 | 56 | ||
42 | value = ''; | 57 | showLintPanel = false; |
43 | 58 | ||
44 | showLineNumbers = false; | 59 | readonly defaultDispatcher = (tr: Transaction): void => { |
60 | this.onTransaction(tr); | ||
61 | }; | ||
45 | 62 | ||
46 | initialSelection!: { start: number, end: number, focused: boolean }; | 63 | dispatcher = this.defaultDispatcher; |
47 | 64 | ||
48 | constructor(themeStore: ThemeStore) { | 65 | constructor(initialValue: string, themeStore: ThemeStore) { |
49 | this.themeStore = themeStore; | 66 | this.themeStore = themeStore; |
50 | this.atom = createAtom('EditorStore'); | 67 | this.state = EditorState.create({ |
51 | this.resetInitialSelection(); | 68 | doc: initialValue, |
69 | extensions: [ | ||
70 | autocompletion(), | ||
71 | classHighlightStyle.extension, | ||
72 | closeBrackets(), | ||
73 | bracketMatching(), | ||
74 | drawSelection(), | ||
75 | EditorState.allowMultipleSelections.of(true), | ||
76 | EditorView.theme({}, { | ||
77 | dark: this.themeStore.darkMode, | ||
78 | }), | ||
79 | highlightActiveLine(), | ||
80 | highlightActiveLineGutter(), | ||
81 | highlightSpecialChars(), | ||
82 | history(), | ||
83 | indentOnInput(), | ||
84 | rectangularSelection(), | ||
85 | searchConfig({ | ||
86 | top: true, | ||
87 | matchCase: true, | ||
88 | }), | ||
89 | // We add the gutters to `extensions` in the order we want them to appear. | ||
90 | foldGutter(), | ||
91 | lineNumbers(), | ||
92 | keymap.of([ | ||
93 | ...closeBracketsKeymap, | ||
94 | ...commentKeymap, | ||
95 | ...completionKeymap, | ||
96 | ...foldKeymap, | ||
97 | ...historyKeymap, | ||
98 | indentWithTab, | ||
99 | // Override keys in `lintKeymap` to go through the `EditorStore`. | ||
100 | { key: 'Mod-Shift-m', run: () => this.setLintPanelOpen(true) }, | ||
101 | ...lintKeymap, | ||
102 | // Override keys in `searchKeymap` to go through the `EditorStore`. | ||
103 | { key: 'Mod-f', run: () => this.setSearchPanelOpen(true), scope: 'editor search-panel' }, | ||
104 | { key: 'Escape', run: () => this.setSearchPanelOpen(false), scope: 'editor search-panel' }, | ||
105 | ...searchKeymap, | ||
106 | ...defaultKeymap, | ||
107 | ]), | ||
108 | ], | ||
109 | }); | ||
110 | reaction( | ||
111 | () => this.themeStore.darkMode, | ||
112 | (darkMode) => { | ||
113 | log.debug('Update editor dark mode', darkMode); | ||
114 | this.dispatch({ | ||
115 | effects: [ | ||
116 | StateEffect.appendConfig.of(EditorView.theme({}, { | ||
117 | dark: darkMode, | ||
118 | })), | ||
119 | ], | ||
120 | }); | ||
121 | }, | ||
122 | ); | ||
52 | makeAutoObservable(this, { | 123 | makeAutoObservable(this, { |
53 | themeStore: false, | 124 | themeStore: false, |
54 | atom: false, | 125 | state: observable.ref, |
55 | chunk: observable.ref, | 126 | defaultDispatcher: false, |
56 | editor: observable.ref, | 127 | dispatcher: false, |
57 | xtextServices: observable.ref, | ||
58 | initialSelection: false, | ||
59 | }); | 128 | }); |
60 | this.loadChunk(); | ||
61 | } | 129 | } |
62 | 130 | ||
63 | private loadChunk(): void { | 131 | updateDispatcher(newDispatcher: ((tr: Transaction) => void) | null): void { |
64 | const loadingStartMillis = Date.now(); | 132 | this.dispatcher = newDispatcher || this.defaultDispatcher; |
65 | log.info('Requesting editor chunk'); | ||
66 | import('./editor').then(({ editorChunk }) => { | ||
67 | runInAction(() => { | ||
68 | this.chunk = editorChunk; | ||
69 | }); | ||
70 | const loadingDurationMillis = Date.now() - loadingStartMillis; | ||
71 | log.info('Loaded editor chunk in', loadingDurationMillis, 'ms'); | ||
72 | }).catch((error) => { | ||
73 | log.error('Error while loading editor', error); | ||
74 | }); | ||
75 | } | 133 | } |
76 | 134 | ||
77 | setInitialSelection(start: number, end: number, focused: boolean): void { | 135 | onTransaction(tr: Transaction): void { |
78 | this.initialSelection = { start, end, focused }; | 136 | log.trace('Editor transaction', tr); |
79 | this.applyInitialSelectionToEditor(); | 137 | this.state = tr.state; |
80 | } | 138 | } |
81 | 139 | ||
82 | private resetInitialSelection(): void { | 140 | dispatch(...specs: readonly TransactionSpec[]): void { |
83 | this.initialSelection = { | 141 | this.dispatcher(this.state.update(...specs)); |
84 | start: 0, | ||
85 | end: 0, | ||
86 | focused: false, | ||
87 | }; | ||
88 | } | 142 | } |
89 | 143 | ||
90 | private applyInitialSelectionToEditor(): void { | 144 | doStateCommand(command: StateCommand): boolean { |
91 | if (this.editor) { | 145 | return command({ |
92 | const { start, end, focused } = this.initialSelection; | 146 | state: this.state, |
93 | const doc = this.editor.getDoc(); | 147 | dispatch: this.dispatcher, |
94 | const startPos = doc.posFromIndex(start); | 148 | }); |
95 | const endPos = doc.posFromIndex(end); | ||
96 | doc.setSelection(startPos, endPos, { | ||
97 | scroll: true, | ||
98 | }); | ||
99 | if (focused) { | ||
100 | this.editor.focus(); | ||
101 | } | ||
102 | this.resetInitialSelection(); | ||
103 | } | ||
104 | } | 149 | } |
105 | 150 | ||
106 | /** | 151 | /** |
107 | * Attaches a new CodeMirror instance and creates Xtext services. | 152 | * @returns `true` if there is history to undo |
108 | * | ||
109 | * The store will not subscribe to any CodeMirror events. Instead, | ||
110 | * the editor component should subscribe to them and relay them to the store. | ||
111 | * | ||
112 | * @param newEditor The new CodeMirror instance | ||
113 | */ | 153 | */ |
114 | editorDidMount(newEditor: Editor): void { | 154 | get canUndo(): boolean { |
115 | if (!this.chunk) { | 155 | return undoDepth(this.state) > 0; |
116 | throw new Error('Editor not loaded yet'); | ||
117 | } | ||
118 | if (this.editor) { | ||
119 | throw new Error('CoreMirror editor mounted before unmounting'); | ||
120 | } | ||
121 | this.editor = newEditor; | ||
122 | this.xtextServices = this.chunk.createServices(newEditor, xtextOptions); | ||
123 | this.applyInitialSelectionToEditor(); | ||
124 | } | 156 | } |
125 | 157 | ||
126 | editorWillUnmount(): void { | 158 | // eslint-disable-next-line class-methods-use-this |
127 | if (!this.chunk) { | 159 | undo(): void { |
128 | throw new Error('Editor not loaded yet'); | 160 | log.debug('Undo', this.doStateCommand(undo)); |
129 | } | ||
130 | if (this.editor) { | ||
131 | this.chunk.removeServices(this.editor); | ||
132 | } | ||
133 | delete this.editor; | ||
134 | delete this.xtextServices; | ||
135 | } | 161 | } |
136 | 162 | ||
137 | /** | 163 | /** |
138 | * Updates the contents of the editor. | 164 | * @returns `true` if there is history to redo |
139 | * | ||
140 | * @param newValue The new contents of the editor | ||
141 | */ | 165 | */ |
142 | updateValue(newValue: string): void { | 166 | get canRedo(): boolean { |
143 | this.value = newValue; | 167 | return redoDepth(this.state) > 0; |
144 | } | ||
145 | |||
146 | reportChanged(): void { | ||
147 | this.atom.reportChanged(); | ||
148 | } | ||
149 | |||
150 | protected observeEditorChanges(): void { | ||
151 | this.atom.reportObserved(); | ||
152 | } | 168 | } |
153 | 169 | ||
154 | get codeMirrorTheme(): string { | 170 | // eslint-disable-next-line class-methods-use-this |
155 | return `problem-${this.themeStore.className}`; | 171 | redo(): void { |
172 | log.debug('Redo', this.doStateCommand(redo)); | ||
156 | } | 173 | } |
157 | 174 | ||
158 | get codeMirrorOptions(): EditorConfiguration { | 175 | toggleLineNumbers(): void { |
159 | return { | 176 | this.showLineNumbers = !this.showLineNumbers; |
160 | ...codeMirrorGlobalOptions, | 177 | log.debug('Show line numbers', this.showLineNumbers); |
161 | theme: this.codeMirrorTheme, | ||
162 | lineNumbers: this.showLineNumbers, | ||
163 | }; | ||
164 | } | 178 | } |
165 | 179 | ||
166 | /** | 180 | /** |
167 | * @returns `true` if there is history to undo | 181 | * Sets whether the CodeMirror search panel should be open. |
182 | * | ||
183 | * This method can be used as a CodeMirror command, | ||
184 | * because it returns `false` if it didn't execute, | ||
185 | * allowing other commands for the same keybind to run instead. | ||
186 | * This matches the behavior of the `openSearchPanel` and `closeSearchPanel` | ||
187 | * commands from `'@codemirror/search'`. | ||
188 | * | ||
189 | * @param newShosSearchPanel whether we should show the search panel | ||
190 | * @returns `true` if the state was changed, `false` otherwise | ||
168 | */ | 191 | */ |
169 | get canUndo(): boolean { | 192 | setSearchPanelOpen(newShowSearchPanel: boolean): boolean { |
170 | this.observeEditorChanges(); | 193 | if (this.showSearchPanel === newShowSearchPanel) { |
171 | if (!this.editor) { | ||
172 | return false; | 194 | return false; |
173 | } | 195 | } |
174 | const { undo: undoSize } = this.editor.historySize(); | 196 | this.showSearchPanel = newShowSearchPanel; |
175 | return undoSize > 0; | 197 | log.debug('Show search panel', this.showSearchPanel); |
198 | return true; | ||
176 | } | 199 | } |
177 | 200 | ||
178 | undo(): void { | 201 | toggleSearchPanel(): void { |
179 | this.editor?.undo(); | 202 | this.setSearchPanelOpen(!this.showSearchPanel); |
180 | } | 203 | } |
181 | 204 | ||
182 | /** | 205 | setLintPanelOpen(newShowLintPanel: boolean): boolean { |
183 | * @returns `true` if there is history to redo | 206 | if (this.showLintPanel === newShowLintPanel) { |
184 | */ | ||
185 | get canRedo(): boolean { | ||
186 | this.observeEditorChanges(); | ||
187 | if (!this.editor) { | ||
188 | return false; | 207 | return false; |
189 | } | 208 | } |
190 | const { redo: redoSize } = this.editor.historySize(); | 209 | this.showLintPanel = newShowLintPanel; |
191 | return redoSize > 0; | 210 | log.debug('Show lint panel', this.showLintPanel); |
211 | return true; | ||
192 | } | 212 | } |
193 | 213 | ||
194 | redo(): void { | 214 | toggleLintPanel(): void { |
195 | this.editor?.redo(); | 215 | this.setLintPanelOpen(!this.showLintPanel); |
196 | } | ||
197 | |||
198 | toggleLineNumbers(): void { | ||
199 | this.showLineNumbers = !this.showLineNumbers; | ||
200 | } | 216 | } |
201 | } | 217 | } |
diff --git a/language-web/src/main/js/editor/editor.ts b/language-web/src/main/js/editor/editor.ts deleted file mode 100644 index fbf8796b..00000000 --- a/language-web/src/main/js/editor/editor.ts +++ /dev/null | |||
@@ -1,18 +0,0 @@ | |||
1 | import 'codemirror/addon/selection/active-line'; | ||
2 | import 'mode-problem'; | ||
3 | import { Controlled } from 'react-codemirror2'; | ||
4 | import { createServices, removeServices } from 'xtext/xtext-codemirror'; | ||
5 | |||
6 | export interface IEditorChunk { | ||
7 | CodeMirror: typeof Controlled; | ||
8 | |||
9 | createServices: typeof createServices; | ||
10 | |||
11 | removeServices: typeof removeServices; | ||
12 | } | ||
13 | |||
14 | export const editorChunk: IEditorChunk = { | ||
15 | CodeMirror: Controlled, | ||
16 | createServices, | ||
17 | removeServices, | ||
18 | }; | ||
diff --git a/language-web/src/main/js/index.tsx b/language-web/src/main/js/index.tsx index 80c70f23..66ad1f28 100644 --- a/language-web/src/main/js/index.tsx +++ b/language-web/src/main/js/index.tsx | |||
@@ -44,8 +44,7 @@ age(bob, bobAge). | |||
44 | scope Family = 1, Person += 5..10. | 44 | scope Family = 1, Person += 5..10. |
45 | `; | 45 | `; |
46 | 46 | ||
47 | const rootStore = new RootStore(); | 47 | const rootStore = new RootStore(initialValue); |
48 | rootStore.editorStore.updateValue(initialValue); | ||
49 | 48 | ||
50 | const app = ( | 49 | const app = ( |
51 | <RootStoreProvider rootStore={rootStore}> | 50 | <RootStoreProvider rootStore={rootStore}> |
diff --git a/language-web/src/main/js/theme/ThemeStore.ts b/language-web/src/main/js/theme/ThemeStore.ts index 3bbea3a1..db94d9f7 100644 --- a/language-web/src/main/js/theme/ThemeStore.ts +++ b/language-web/src/main/js/theme/ThemeStore.ts | |||
@@ -51,6 +51,10 @@ export class ThemeStore { | |||
51 | return responsiveFontSizes(materialUiTheme); | 51 | return responsiveFontSizes(materialUiTheme); |
52 | } | 52 | } |
53 | 53 | ||
54 | get darkMode(): boolean { | ||
55 | return this.currentThemeData.paletteMode === 'dark'; | ||
56 | } | ||
57 | |||
54 | get className(): string { | 58 | get className(): string { |
55 | return this.currentThemeData.className; | 59 | return this.currentThemeData.className; |
56 | } | 60 | } |
diff --git a/language-web/yarn.lock b/language-web/yarn.lock index e87bc6ee..51c551a7 100644 --- a/language-web/yarn.lock +++ b/language-web/yarn.lock | |||
@@ -972,6 +972,189 @@ | |||
972 | "@babel/helper-validator-identifier" "^7.14.9" | 972 | "@babel/helper-validator-identifier" "^7.14.9" |
973 | to-fast-properties "^2.0.0" | 973 | to-fast-properties "^2.0.0" |
974 | 974 | ||
975 | "@codemirror/autocomplete@^0.19.3": | ||
976 | version "0.19.3" | ||
977 | resolved "https://registry.yarnpkg.com/@codemirror/autocomplete/-/autocomplete-0.19.3.tgz#0266ac29617277d2d8cc7dcfb600c1e50c332d35" | ||
978 | integrity sha512-5juP6hVrHAogzQ0JUTQuibE8j1seqeGNJ98qLUPuliI6kLBg5INS4qvUI1Brqye+wYPFu7UHqrrn13RLh5YSzw== | ||
979 | dependencies: | ||
980 | "@codemirror/language" "^0.19.0" | ||
981 | "@codemirror/state" "^0.19.0" | ||
982 | "@codemirror/text" "^0.19.2" | ||
983 | "@codemirror/tooltip" "^0.19.0" | ||
984 | "@codemirror/view" "^0.19.0" | ||
985 | "@lezer/common" "^0.15.0" | ||
986 | |||
987 | "@codemirror/closebrackets@^0.19.0": | ||
988 | version "0.19.0" | ||
989 | resolved "https://registry.yarnpkg.com/@codemirror/closebrackets/-/closebrackets-0.19.0.tgz#69fdcee85779d638a00a42becd9f53a33a26d77f" | ||
990 | integrity sha512-dFWX5OEVYWRNtGaifSbwIAlymnRRjxWMiMbffbAjF7p0zfGHDbdGkiT56q3Xud63h5/tQdSo5dK1iyNTzHz5vg== | ||
991 | dependencies: | ||
992 | "@codemirror/language" "^0.19.0" | ||
993 | "@codemirror/rangeset" "^0.19.0" | ||
994 | "@codemirror/state" "^0.19.0" | ||
995 | "@codemirror/text" "^0.19.0" | ||
996 | "@codemirror/view" "^0.19.0" | ||
997 | |||
998 | "@codemirror/commands@^0.19.5": | ||
999 | version "0.19.5" | ||
1000 | resolved "https://registry.yarnpkg.com/@codemirror/commands/-/commands-0.19.5.tgz#2607b5c12c5c96df2cabce2e43f6285c07cfaf11" | ||
1001 | integrity sha512-8PZOtx7d/GbKhFYA88zs2wINDtaUgj3pEjLYScKTd/Vsyw8qOp86tJQQNnMFTRZj/ISQl9Lbg3aAmHvroMqspw== | ||
1002 | dependencies: | ||
1003 | "@codemirror/language" "^0.19.0" | ||
1004 | "@codemirror/matchbrackets" "^0.19.0" | ||
1005 | "@codemirror/state" "^0.19.2" | ||
1006 | "@codemirror/text" "^0.19.0" | ||
1007 | "@codemirror/view" "^0.19.0" | ||
1008 | "@lezer/common" "^0.15.0" | ||
1009 | |||
1010 | "@codemirror/comment@^0.19.0": | ||
1011 | version "0.19.0" | ||
1012 | resolved "https://registry.yarnpkg.com/@codemirror/comment/-/comment-0.19.0.tgz#4f23497924e9346898c2e0123011acc535a0bea6" | ||
1013 | integrity sha512-3hqAd0548fxqOBm4khFMcXVIivX8p0bSlbAuZJ6PNoUn/0wXhxkxowPp0FmFzU2+y37Z+ZQF5cRB5EREWPRIiQ== | ||
1014 | dependencies: | ||
1015 | "@codemirror/state" "^0.19.0" | ||
1016 | "@codemirror/text" "^0.19.0" | ||
1017 | "@codemirror/view" "^0.19.0" | ||
1018 | |||
1019 | "@codemirror/fold@^0.19.0": | ||
1020 | version "0.19.0" | ||
1021 | resolved "https://registry.yarnpkg.com/@codemirror/fold/-/fold-0.19.0.tgz#cd57537870ff581ae57199a7658abe3f9908f2ed" | ||
1022 | integrity sha512-cHbr2hqKe2pMls1Ia1a4IwXly87ljpmvPUKkul6H/Uv9kCwpQLOP3dGMPXbMGm/1hGjBF/0wnc+m2iba9iJBTQ== | ||
1023 | dependencies: | ||
1024 | "@codemirror/gutter" "^0.19.0" | ||
1025 | "@codemirror/language" "^0.19.0" | ||
1026 | "@codemirror/rangeset" "^0.19.0" | ||
1027 | "@codemirror/state" "^0.19.0" | ||
1028 | "@codemirror/view" "^0.19.0" | ||
1029 | |||
1030 | "@codemirror/gutter@^0.19.0", "@codemirror/gutter@^0.19.2": | ||
1031 | version "0.19.2" | ||
1032 | resolved "https://registry.yarnpkg.com/@codemirror/gutter/-/gutter-0.19.2.tgz#afc61dbe2977ff83aff8675200deab6d04244c5c" | ||
1033 | integrity sha512-xiayxhc9uq1UXiG/r/E3AAYXZt+EtSdgdQXC4nlMHvbfw2EmtryfzGO3HrgasHRyAW8uNCaO9JO4n7mU7rm1rQ== | ||
1034 | dependencies: | ||
1035 | "@codemirror/rangeset" "^0.19.0" | ||
1036 | "@codemirror/state" "^0.19.0" | ||
1037 | "@codemirror/view" "^0.19.0" | ||
1038 | |||
1039 | "@codemirror/highlight@^0.19.6": | ||
1040 | version "0.19.6" | ||
1041 | resolved "https://registry.yarnpkg.com/@codemirror/highlight/-/highlight-0.19.6.tgz#7f2e066f83f5649e8e0748a3abe0aaeaf64b8ac2" | ||
1042 | integrity sha512-+eibu6on9quY8uN3xJ/n3rH+YIDLlpX7YulVmFvqAIz/ukRQ5tWaBmB7fMixHmnmRIRBRZgB8rNtonuMwZSAHQ== | ||
1043 | dependencies: | ||
1044 | "@codemirror/language" "^0.19.0" | ||
1045 | "@codemirror/rangeset" "^0.19.0" | ||
1046 | "@codemirror/state" "^0.19.0" | ||
1047 | "@codemirror/view" "^0.19.0" | ||
1048 | "@lezer/common" "^0.15.0" | ||
1049 | style-mod "^4.0.0" | ||
1050 | |||
1051 | "@codemirror/history@^0.19.0": | ||
1052 | version "0.19.0" | ||
1053 | resolved "https://registry.yarnpkg.com/@codemirror/history/-/history-0.19.0.tgz#cc8095c927c9566f7b69fa404074edde4c54d39c" | ||
1054 | integrity sha512-E0H+lncH66IMDhaND9jgkjE7s0dhYfjCPmS+Ig2Yes9I8+UIEecIdObj8c8HPCFGctGg3fxXqRAw2mdHl2Wouw== | ||
1055 | dependencies: | ||
1056 | "@codemirror/state" "^0.19.0" | ||
1057 | "@codemirror/view" "^0.19.0" | ||
1058 | |||
1059 | "@codemirror/language@^0.19.0", "@codemirror/language@^0.19.3": | ||
1060 | version "0.19.3" | ||
1061 | resolved "https://registry.yarnpkg.com/@codemirror/language/-/language-0.19.3.tgz#e4f61555dec0787f757b78348a54a00f3bb23c9c" | ||
1062 | integrity sha512-6vjkRYHRJg/z9wdAk75nU2fQwCJBsh2HpkIjKXIHfzISSgLt5qSDxVhPd8Uu8PD5WMmFFP8tX7I9kdIt873o0A== | ||
1063 | dependencies: | ||
1064 | "@codemirror/state" "^0.19.0" | ||
1065 | "@codemirror/text" "^0.19.0" | ||
1066 | "@codemirror/view" "^0.19.0" | ||
1067 | "@lezer/common" "^0.15.5" | ||
1068 | "@lezer/lr" "^0.15.0" | ||
1069 | |||
1070 | "@codemirror/lint@^0.19.2": | ||
1071 | version "0.19.2" | ||
1072 | resolved "https://registry.yarnpkg.com/@codemirror/lint/-/lint-0.19.2.tgz#1c3932cffd635da796bea3384256b0a315b6fe1e" | ||
1073 | integrity sha512-477qvXWwuf24YsBi1DzjrGyzM+qfPe5L4xEHGxQTGOMq6R0+QAFKppOJsxN3y7gzDpLrZSYZdhJzWevOuliZQg== | ||
1074 | dependencies: | ||
1075 | "@codemirror/panel" "^0.19.0" | ||
1076 | "@codemirror/state" "^0.19.0" | ||
1077 | "@codemirror/tooltip" "^0.19.0" | ||
1078 | "@codemirror/view" "^0.19.0" | ||
1079 | crelt "^1.0.5" | ||
1080 | |||
1081 | "@codemirror/matchbrackets@^0.19.0", "@codemirror/matchbrackets@^0.19.3": | ||
1082 | version "0.19.3" | ||
1083 | resolved "https://registry.yarnpkg.com/@codemirror/matchbrackets/-/matchbrackets-0.19.3.tgz#1f430ada6fa21af2205280ff344ef57bb95dd3cb" | ||
1084 | integrity sha512-ljkrBxaLgh8jesroUiBa57pdEwqJamxkukXrJpL9LdyFZVJaF+9TldhztRaMsMZO1XnCSSHQ9sg32iuHo7Sc2g== | ||
1085 | dependencies: | ||
1086 | "@codemirror/language" "^0.19.0" | ||
1087 | "@codemirror/state" "^0.19.0" | ||
1088 | "@codemirror/view" "^0.19.0" | ||
1089 | "@lezer/common" "^0.15.0" | ||
1090 | |||
1091 | "@codemirror/panel@^0.19.0": | ||
1092 | version "0.19.0" | ||
1093 | resolved "https://registry.yarnpkg.com/@codemirror/panel/-/panel-0.19.0.tgz#18c7a253a7a1ef686bece1ef13ec0e5eb6603265" | ||
1094 | integrity sha512-LJuu49xnuhaAztlhnLJQ57ddOirSyf8/lnl7twsQUG/05RkxodBZ9F7q8r5AOLqOkaQOy9WySEKX1Ur8lD9Q5w== | ||
1095 | dependencies: | ||
1096 | "@codemirror/state" "^0.19.0" | ||
1097 | "@codemirror/view" "^0.19.0" | ||
1098 | |||
1099 | "@codemirror/rangeset@^0.19.0": | ||
1100 | version "0.19.1" | ||
1101 | resolved "https://registry.yarnpkg.com/@codemirror/rangeset/-/rangeset-0.19.1.tgz#03ab6f93fb60d9ba98f810b98ed9471cba1e3854" | ||
1102 | integrity sha512-WaKTEw8JB/3QFlQzpdgRoklopcWvG8O/Xp+rxxOfFKYTaeaejpY/tjpyBBg+Ea65Ka3m7+pPp9d5j/oR2rd9NA== | ||
1103 | dependencies: | ||
1104 | "@codemirror/state" "^0.19.0" | ||
1105 | |||
1106 | "@codemirror/rectangular-selection@^0.19.1": | ||
1107 | version "0.19.1" | ||
1108 | resolved "https://registry.yarnpkg.com/@codemirror/rectangular-selection/-/rectangular-selection-0.19.1.tgz#5a88ece4fb68ce5682539497db8a64fc015aae63" | ||
1109 | integrity sha512-9ElnqOg3mpZIWe0prPRd1SZ48Q9QB3bR8Aocq8UtjboJSUG8ABhRrbuTZMW/rMqpBPSjVpCe9xkCCkEQMYQVmw== | ||
1110 | dependencies: | ||
1111 | "@codemirror/state" "^0.19.0" | ||
1112 | "@codemirror/text" "^0.19.4" | ||
1113 | "@codemirror/view" "^0.19.0" | ||
1114 | |||
1115 | "@codemirror/search@^0.19.2": | ||
1116 | version "0.19.2" | ||
1117 | resolved "https://registry.yarnpkg.com/@codemirror/search/-/search-0.19.2.tgz#d549c3daa527e17c173cdfc90b7c1b02deab1502" | ||
1118 | integrity sha512-TrRxUxyJ/a7HXtUvMZhgkOUbKE1xO33UhXjn1XACEHKWhgovw1vEeEEti9dZejN8/QOOFJed39InUxmp7oQ8HA== | ||
1119 | dependencies: | ||
1120 | "@codemirror/panel" "^0.19.0" | ||
1121 | "@codemirror/rangeset" "^0.19.0" | ||
1122 | "@codemirror/state" "^0.19.2" | ||
1123 | "@codemirror/text" "^0.19.0" | ||
1124 | "@codemirror/view" "^0.19.0" | ||
1125 | crelt "^1.0.5" | ||
1126 | |||
1127 | "@codemirror/state@^0.19.0", "@codemirror/state@^0.19.2": | ||
1128 | version "0.19.2" | ||
1129 | resolved "https://registry.yarnpkg.com/@codemirror/state/-/state-0.19.2.tgz#81de81f89e87b9362b8bc6d51135637dddd4d33d" | ||
1130 | integrity sha512-dDqCrtkb0c/LYUlvQBLyLfkISEskbZnhvBbcVOF4j2AusJ1ptJ3EGMxBL9G16GP1TOdC1T613gA1J1qc3pbfGQ== | ||
1131 | dependencies: | ||
1132 | "@codemirror/text" "^0.19.0" | ||
1133 | |||
1134 | "@codemirror/text@^0.19.0", "@codemirror/text@^0.19.2", "@codemirror/text@^0.19.4": | ||
1135 | version "0.19.4" | ||
1136 | resolved "https://registry.yarnpkg.com/@codemirror/text/-/text-0.19.4.tgz#522cbe93becdf7637ca7148b67f628b3d8b8fdf5" | ||
1137 | integrity sha512-Msd1ImS+Mmref28+oyoOrBwqjH4lbCVO/5ol2jRaCEYH7+KaMocxFADZomPLnMPgDW6Gc4mu/K2lj8IjAOFdTA== | ||
1138 | |||
1139 | "@codemirror/tooltip@^0.19.0": | ||
1140 | version "0.19.2" | ||
1141 | resolved "https://registry.yarnpkg.com/@codemirror/tooltip/-/tooltip-0.19.2.tgz#a8f6b703edb64748106e136ec1f099a981a3f1d4" | ||
1142 | integrity sha512-FMMGGLrr62Ck54NEz8yTGpgo8ihobAsC3sbeQg+OpY4jv9dt1yIP5B9LzsIV+TXQB57JZQZxtTqzkhnFq76haw== | ||
1143 | dependencies: | ||
1144 | "@codemirror/state" "^0.19.0" | ||
1145 | "@codemirror/view" "^0.19.0" | ||
1146 | |||
1147 | "@codemirror/view@^0.19.0", "@codemirror/view@^0.19.9": | ||
1148 | version "0.19.9" | ||
1149 | resolved "https://registry.yarnpkg.com/@codemirror/view/-/view-0.19.9.tgz#cbdcbc398818e7c9603c81e4b2a2868ec0a1f954" | ||
1150 | integrity sha512-d2Z2rFl53yTwvhsRYtS97xT7ce1f/Q/NNE2uDg/Be1MVjP2v1OlwUTyTosBmxiCt4oUvMklfa+EdPGOATVJhtg== | ||
1151 | dependencies: | ||
1152 | "@codemirror/rangeset" "^0.19.0" | ||
1153 | "@codemirror/state" "^0.19.2" | ||
1154 | "@codemirror/text" "^0.19.0" | ||
1155 | style-mod "^4.0.0" | ||
1156 | w3c-keyname "^2.2.4" | ||
1157 | |||
975 | "@discoveryjs/json-ext@^0.5.0": | 1158 | "@discoveryjs/json-ext@^0.5.0": |
976 | version "0.5.5" | 1159 | version "0.5.5" |
977 | resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.5.tgz#9283c9ce5b289a3c4f61c12757469e59377f81f3" | 1160 | resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.5.tgz#9283c9ce5b289a3c4f61c12757469e59377f81f3" |
@@ -1117,6 +1300,18 @@ | |||
1117 | resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz#87de7af9c231826fdd68ac7258f77c429e0e5fcf" | 1300 | resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz#87de7af9c231826fdd68ac7258f77c429e0e5fcf" |
1118 | integrity sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w== | 1301 | integrity sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w== |
1119 | 1302 | ||
1303 | "@lezer/common@^0.15.0", "@lezer/common@^0.15.5": | ||
1304 | version "0.15.7" | ||
1305 | resolved "https://registry.yarnpkg.com/@lezer/common/-/common-0.15.7.tgz#8b445dae9777f689783132cf490770ece3c03d7b" | ||
1306 | integrity sha512-Rw8TDJnBzZnkyzIXs1Tmmd241FrBLJBj8gkdy3y0joGFb8Z4I/joKEsR+gv1pb13o1TMsZxm3fmP+d/wPt2CTQ== | ||
1307 | |||
1308 | "@lezer/lr@^0.15.0": | ||
1309 | version "0.15.4" | ||
1310 | resolved "https://registry.yarnpkg.com/@lezer/lr/-/lr-0.15.4.tgz#634670d7224040fddac1370af01211eecd9ac0a0" | ||
1311 | integrity sha512-vwgG80sihEGJn6wJp6VijXrnzVai/KPva/OzYKaWvIx0IiXKjoMQ8UAwcgpSBwfS4Fbz3IKOX/cCNXU3r1FvpQ== | ||
1312 | dependencies: | ||
1313 | "@lezer/common" "^0.15.0" | ||
1314 | |||
1120 | "@mui/core@5.0.0-alpha.49": | 1315 | "@mui/core@5.0.0-alpha.49": |
1121 | version "5.0.0-alpha.49" | 1316 | version "5.0.0-alpha.49" |
1122 | resolved "https://registry.yarnpkg.com/@mui/core/-/core-5.0.0-alpha.49.tgz#e74d6ec7f83f85b55d48aa05ea6b7cefff88ce1b" | 1317 | resolved "https://registry.yarnpkg.com/@mui/core/-/core-5.0.0-alpha.49.tgz#e74d6ec7f83f85b55d48aa05ea6b7cefff88ce1b" |
@@ -2294,11 +2489,6 @@ clsx@^1.1.1: | |||
2294 | resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188" | 2489 | resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188" |
2295 | integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA== | 2490 | integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA== |
2296 | 2491 | ||
2297 | codemirror@^5.63.1: | ||
2298 | version "5.63.1" | ||
2299 | resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.63.1.tgz#b0b9e8444206fd6a43a58a4b31d5740bb891fa57" | ||
2300 | integrity sha512-baivaNZreZOGh1/tYyTvCupC9NeWk7qlZeGUDi4nFKj/J0JU8FYKZND4QqLw70P7HOttlCt4JJAOj9GoIhHEkA== | ||
2301 | |||
2302 | color-convert@^1.9.0: | 2492 | color-convert@^1.9.0: |
2303 | version "1.9.3" | 2493 | version "1.9.3" |
2304 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" | 2494 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" |
@@ -2470,6 +2660,11 @@ cosmiconfig@^7.0.0: | |||
2470 | path-type "^4.0.0" | 2660 | path-type "^4.0.0" |
2471 | yaml "^1.10.0" | 2661 | yaml "^1.10.0" |
2472 | 2662 | ||
2663 | crelt@^1.0.5: | ||
2664 | version "1.0.5" | ||
2665 | resolved "https://registry.yarnpkg.com/crelt/-/crelt-1.0.5.tgz#57c0d52af8c859e354bace1883eb2e1eb182bb94" | ||
2666 | integrity sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA== | ||
2667 | |||
2473 | cross-spawn@^5.0.1: | 2668 | cross-spawn@^5.0.1: |
2474 | version "5.1.0" | 2669 | version "5.1.0" |
2475 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" | 2670 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" |
@@ -6095,11 +6290,6 @@ raw-body@2.4.0: | |||
6095 | iconv-lite "0.4.24" | 6290 | iconv-lite "0.4.24" |
6096 | unpipe "1.0.0" | 6291 | unpipe "1.0.0" |
6097 | 6292 | ||
6098 | "react-codemirror2@npm:react17-codemirror2@^7.2.3": | ||
6099 | version "7.2.3" | ||
6100 | resolved "https://registry.yarnpkg.com/react17-codemirror2/-/react17-codemirror2-7.2.3.tgz#ff1ec8f5feadf9c03fb25095c40326971f3ce106" | ||
6101 | integrity sha512-XOIu5k3IFH6ajunRw5HO9BX9N6u9T7tby7BN4tKi/sz0Yq3pvd0xylbvq5PbScl2oowRMcyiJXkpPRI8+h0H9Q== | ||
6102 | |||
6103 | react-dom@^17.0.2: | 6293 | react-dom@^17.0.2: |
6104 | version "17.0.2" | 6294 | version "17.0.2" |
6105 | resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" | 6295 | resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" |
@@ -6947,6 +7137,11 @@ style-loader@^3.3.0: | |||
6947 | resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.0.tgz#d66ea95fc50b22f8b79b69a9e414760fcf58d8d8" | 7137 | resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.0.tgz#d66ea95fc50b22f8b79b69a9e414760fcf58d8d8" |
6948 | integrity sha512-szANub7ksJtQioJYtpbWwh1hUl99uK15n5HDlikeCRil/zYMZgSxucHddyF/4A3qJMUiAjPhFowrrQuNMA7jwQ== | 7138 | integrity sha512-szANub7ksJtQioJYtpbWwh1hUl99uK15n5HDlikeCRil/zYMZgSxucHddyF/4A3qJMUiAjPhFowrrQuNMA7jwQ== |
6949 | 7139 | ||
7140 | style-mod@^4.0.0: | ||
7141 | version "4.0.0" | ||
7142 | resolved "https://registry.yarnpkg.com/style-mod/-/style-mod-4.0.0.tgz#97e7c2d68b592975f2ca7a63d0dd6fcacfe35a01" | ||
7143 | integrity sha512-OPhtyEjyyN9x3nhPsu76f52yUGXiZcgvsrFVtvTkyGRQJ0XK+GPc6ov1z+lRpbeabka+MYEQxOYRnt5nF30aMw== | ||
7144 | |||
6950 | style-search@^0.1.0: | 7145 | style-search@^0.1.0: |
6951 | version "0.1.0" | 7146 | version "0.1.0" |
6952 | resolved "https://registry.yarnpkg.com/style-search/-/style-search-0.1.0.tgz#7958c793e47e32e07d2b5cafe5c0bf8e12e77902" | 7147 | resolved "https://registry.yarnpkg.com/style-search/-/style-search-0.1.0.tgz#7958c793e47e32e07d2b5cafe5c0bf8e12e77902" |
@@ -7480,6 +7675,11 @@ vfile@^4.0.0: | |||
7480 | unist-util-stringify-position "^2.0.0" | 7675 | unist-util-stringify-position "^2.0.0" |
7481 | vfile-message "^2.0.0" | 7676 | vfile-message "^2.0.0" |
7482 | 7677 | ||
7678 | w3c-keyname@^2.2.4: | ||
7679 | version "2.2.4" | ||
7680 | resolved "https://registry.yarnpkg.com/w3c-keyname/-/w3c-keyname-2.2.4.tgz#4ade6916f6290224cdbd1db8ac49eab03d0eef6b" | ||
7681 | integrity sha512-tOhfEwEzFLJzf6d1ZPkYfGj+FWhIpBux9ppoP3rlclw3Z0BZv3N7b7030Z1kYth+6rDuAsXUFr+d0VE6Ed1ikw== | ||
7682 | |||
7483 | watchpack@^2.2.0: | 7683 | watchpack@^2.2.0: |
7484 | version "2.2.0" | 7684 | version "2.2.0" |
7485 | resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.2.0.tgz#47d78f5415fe550ecd740f99fe2882323a58b1ce" | 7685 | resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.2.0.tgz#47d78f5415fe550ecd740f99fe2882323a58b1ce" |