diff options
Diffstat (limited to 'subprojects/frontend/src/editor')
-rw-r--r-- | subprojects/frontend/src/editor/EditorArea.tsx | 41 | ||||
-rw-r--r-- | subprojects/frontend/src/editor/EditorButtons.tsx | 32 | ||||
-rw-r--r-- | subprojects/frontend/src/editor/EditorParent.ts | 74 | ||||
-rw-r--r-- | subprojects/frontend/src/editor/EditorStore.ts | 81 | ||||
-rw-r--r-- | subprojects/frontend/src/editor/GenerateButton.tsx | 10 | ||||
-rw-r--r-- | subprojects/frontend/src/editor/defineDecorationSetExtension.ts (renamed from subprojects/frontend/src/editor/decorationSetExtension.ts) | 19 | ||||
-rw-r--r-- | subprojects/frontend/src/editor/findOccurrences.ts | 13 | ||||
-rw-r--r-- | subprojects/frontend/src/editor/semanticHighlighting.ts | 22 |
8 files changed, 173 insertions, 119 deletions
diff --git a/subprojects/frontend/src/editor/EditorArea.tsx b/subprojects/frontend/src/editor/EditorArea.tsx index dba20f6e..14294371 100644 --- a/subprojects/frontend/src/editor/EditorArea.tsx +++ b/subprojects/frontend/src/editor/EditorArea.tsx | |||
@@ -1,17 +1,13 @@ | |||
1 | import { Command, EditorView } from '@codemirror/view'; | ||
2 | import { closeSearchPanel, openSearchPanel } from '@codemirror/search'; | ||
3 | import { closeLintPanel, openLintPanel } from '@codemirror/lint'; | 1 | import { closeLintPanel, openLintPanel } from '@codemirror/lint'; |
2 | import { closeSearchPanel, openSearchPanel } from '@codemirror/search'; | ||
3 | import { type Command, EditorView } from '@codemirror/view'; | ||
4 | import { observer } from 'mobx-react-lite'; | 4 | import { observer } from 'mobx-react-lite'; |
5 | import React, { | 5 | import React, { useCallback, useEffect, useRef, useState } from 'react'; |
6 | useCallback, | ||
7 | useEffect, | ||
8 | useRef, | ||
9 | useState, | ||
10 | } from 'react'; | ||
11 | 6 | ||
12 | import { EditorParent } from './EditorParent'; | ||
13 | import { useRootStore } from '../RootStore'; | 7 | import { useRootStore } from '../RootStore'; |
14 | import { getLogger } from '../utils/logger'; | 8 | import getLogger from '../utils/getLogger'; |
9 | |||
10 | import EditorParent from './EditorParent'; | ||
15 | 11 | ||
16 | const log = getLogger('editor.EditorArea'); | 12 | const log = getLogger('editor.EditorArea'); |
17 | 13 | ||
@@ -70,10 +66,12 @@ function fixCodeMirrorAccessibility(editorView: EditorView) { | |||
70 | contentDOM.setAttribute('aria-label', 'Code editor'); | 66 | contentDOM.setAttribute('aria-label', 'Code editor'); |
71 | } | 67 | } |
72 | 68 | ||
73 | export const EditorArea = observer(() => { | 69 | function EditorArea(): JSX.Element { |
74 | const { editorStore } = useRootStore(); | 70 | const { editorStore } = useRootStore(); |
75 | const editorParentRef = useRef<HTMLDivElement | null>(null); | 71 | const editorParentRef = useRef<HTMLDivElement | null>(null); |
76 | const [editorViewState, setEditorViewState] = useState<EditorView | null>(null); | 72 | const [editorViewState, setEditorViewState] = useState<EditorView | null>( |
73 | null, | ||
74 | ); | ||
77 | 75 | ||
78 | const setSearchPanelOpen = usePanel( | 76 | const setSearchPanelOpen = usePanel( |
79 | 'search', | 77 | 'search', |
@@ -131,22 +129,21 @@ export const EditorArea = observer(() => { | |||
131 | editorView.destroy(); | 129 | editorView.destroy(); |
132 | log.info('Editor destroyed'); | 130 | log.info('Editor destroyed'); |
133 | }; | 131 | }; |
134 | }, [ | 132 | }, [editorStore, setSearchPanelOpen, setLintPanelOpen]); |
135 | editorParentRef, | ||
136 | editorStore, | ||
137 | setSearchPanelOpen, | ||
138 | setLintPanelOpen, | ||
139 | ]); | ||
140 | 133 | ||
141 | return ( | 134 | return ( |
142 | <EditorParent | 135 | <EditorParent |
143 | className="dark" | 136 | className="dark" |
144 | sx={{ | 137 | sx={{ |
145 | '.cm-lineNumbers': editorStore.showLineNumbers ? {} : { | 138 | '.cm-lineNumbers': editorStore.showLineNumbers |
146 | display: 'none !important', | 139 | ? {} |
147 | }, | 140 | : { |
141 | display: 'none !important', | ||
142 | }, | ||
148 | }} | 143 | }} |
149 | ref={editorParentRef} | 144 | ref={editorParentRef} |
150 | /> | 145 | /> |
151 | ); | 146 | ); |
152 | }); | 147 | } |
148 | |||
149 | export default observer(EditorArea); | ||
diff --git a/subprojects/frontend/src/editor/EditorButtons.tsx b/subprojects/frontend/src/editor/EditorButtons.tsx index 150aa00d..652ca71e 100644 --- a/subprojects/frontend/src/editor/EditorButtons.tsx +++ b/subprojects/frontend/src/editor/EditorButtons.tsx | |||
@@ -1,9 +1,4 @@ | |||
1 | import type { Diagnostic } from '@codemirror/lint'; | 1 | import type { Diagnostic } from '@codemirror/lint'; |
2 | import { observer } from 'mobx-react-lite'; | ||
3 | import IconButton from '@mui/material/IconButton'; | ||
4 | import Stack from '@mui/material/Stack'; | ||
5 | import ToggleButton from '@mui/material/ToggleButton'; | ||
6 | import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'; | ||
7 | import CheckIcon from '@mui/icons-material/Check'; | 2 | import CheckIcon from '@mui/icons-material/Check'; |
8 | import ErrorIcon from '@mui/icons-material/Error'; | 3 | import ErrorIcon from '@mui/icons-material/Error'; |
9 | import FormatListNumberedIcon from '@mui/icons-material/FormatListNumbered'; | 4 | import FormatListNumberedIcon from '@mui/icons-material/FormatListNumbered'; |
@@ -13,6 +8,11 @@ import RedoIcon from '@mui/icons-material/Redo'; | |||
13 | import SearchIcon from '@mui/icons-material/Search'; | 8 | import SearchIcon from '@mui/icons-material/Search'; |
14 | import UndoIcon from '@mui/icons-material/Undo'; | 9 | import UndoIcon from '@mui/icons-material/Undo'; |
15 | import WarningIcon from '@mui/icons-material/Warning'; | 10 | import WarningIcon from '@mui/icons-material/Warning'; |
11 | import IconButton from '@mui/material/IconButton'; | ||
12 | import Stack from '@mui/material/Stack'; | ||
13 | import ToggleButton from '@mui/material/ToggleButton'; | ||
14 | import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'; | ||
15 | import { observer } from 'mobx-react-lite'; | ||
16 | import React from 'react'; | 16 | import React from 'react'; |
17 | 17 | ||
18 | import { useRootStore } from '../RootStore'; | 18 | import { useRootStore } from '../RootStore'; |
@@ -27,23 +27,17 @@ function getLintIcon(severity: Diagnostic['severity'] | null) { | |||
27 | return <WarningIcon fontSize="small" />; | 27 | return <WarningIcon fontSize="small" />; |
28 | case 'info': | 28 | case 'info': |
29 | return <InfoOutlinedIcon fontSize="small" />; | 29 | return <InfoOutlinedIcon fontSize="small" />; |
30 | case null: | 30 | default: |
31 | return <CheckIcon fontSize="small" />; | 31 | return <CheckIcon fontSize="small" />; |
32 | } | 32 | } |
33 | } | 33 | } |
34 | 34 | ||
35 | export const EditorButtons = observer(() => { | 35 | function EditorButtons(): JSX.Element { |
36 | const { editorStore } = useRootStore(); | 36 | const { editorStore } = useRootStore(); |
37 | 37 | ||
38 | return ( | 38 | return ( |
39 | <Stack | 39 | <Stack direction="row" spacing={1}> |
40 | direction="row" | 40 | <Stack direction="row" alignItems="center"> |
41 | spacing={1} | ||
42 | > | ||
43 | <Stack | ||
44 | direction="row" | ||
45 | alignItems="center" | ||
46 | > | ||
47 | <IconButton | 41 | <IconButton |
48 | disabled={!editorStore.canUndo} | 42 | disabled={!editorStore.canUndo} |
49 | onClick={() => editorStore.undo()} | 43 | onClick={() => editorStore.undo()} |
@@ -59,9 +53,7 @@ export const EditorButtons = observer(() => { | |||
59 | <RedoIcon fontSize="small" /> | 53 | <RedoIcon fontSize="small" /> |
60 | </IconButton> | 54 | </IconButton> |
61 | </Stack> | 55 | </Stack> |
62 | <ToggleButtonGroup | 56 | <ToggleButtonGroup size="small"> |
63 | size="small" | ||
64 | > | ||
65 | <ToggleButton | 57 | <ToggleButton |
66 | selected={editorStore.showLineNumbers} | 58 | selected={editorStore.showLineNumbers} |
67 | onClick={() => editorStore.toggleLineNumbers()} | 59 | onClick={() => editorStore.toggleLineNumbers()} |
@@ -95,4 +87,6 @@ export const EditorButtons = observer(() => { | |||
95 | </IconButton> | 87 | </IconButton> |
96 | </Stack> | 88 | </Stack> |
97 | ); | 89 | ); |
98 | }); | 90 | } |
91 | |||
92 | export default observer(EditorButtons); | ||
diff --git a/subprojects/frontend/src/editor/EditorParent.ts b/subprojects/frontend/src/editor/EditorParent.ts index 9aaf541a..dbc35a0d 100644 --- a/subprojects/frontend/src/editor/EditorParent.ts +++ b/subprojects/frontend/src/editor/EditorParent.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { styled } from '@mui/material/styles'; | 1 | import { alpha, styled } from '@mui/material/styles'; |
2 | 2 | ||
3 | /** | 3 | /** |
4 | * Returns a squiggly underline background image encoded as a CSS `url()` data URI with Base64. | 4 | * Returns a squiggly underline background image encoded as a CSS `url()` data URI with Base64. |
@@ -17,7 +17,9 @@ function underline(color: string) { | |||
17 | return `url('data:image/svg+xml;base64,${svgBase64}')`; | 17 | return `url('data:image/svg+xml;base64,${svgBase64}')`; |
18 | } | 18 | } |
19 | 19 | ||
20 | export const EditorParent = styled('div')(({ theme }) => { | 20 | export default styled('div', { |
21 | name: 'EditorParent', | ||
22 | })(({ theme }) => { | ||
21 | const codeMirrorLintStyle: Record<string, unknown> = {}; | 23 | const codeMirrorLintStyle: Record<string, unknown> = {}; |
22 | (['error', 'warning', 'info'] as const).forEach((severity) => { | 24 | (['error', 'warning', 'info'] as const).forEach((severity) => { |
23 | const color = theme.palette[severity].main; | 25 | const color = theme.palette[severity].main; |
@@ -37,19 +39,20 @@ export const EditorParent = styled('div')(({ theme }) => { | |||
37 | '.cm-content': { | 39 | '.cm-content': { |
38 | padding: 0, | 40 | padding: 0, |
39 | }, | 41 | }, |
40 | '.cm-scroller, .cm-tooltip-autocomplete, .cm-completionLabel, .cm-completionDetail': { | 42 | '.cm-scroller, .cm-tooltip-autocomplete, .cm-completionLabel, .cm-completionDetail': |
41 | fontSize: 16, | 43 | { |
42 | fontFamily: '"JetBrains MonoVariable", "JetBrains Mono", monospace', | 44 | fontSize: 16, |
43 | fontFeatureSettings: '"liga", "calt"', | 45 | fontFamily: '"JetBrains MonoVariable", "JetBrains Mono", monospace', |
44 | fontWeight: 400, | 46 | fontFeatureSettings: '"liga", "calt"', |
45 | letterSpacing: 0, | 47 | fontWeight: 400, |
46 | textRendering: 'optimizeLegibility', | 48 | letterSpacing: 0, |
47 | }, | 49 | textRendering: 'optimizeLegibility', |
50 | }, | ||
48 | '.cm-scroller': { | 51 | '.cm-scroller': { |
49 | color: theme.palette.text.secondary, | 52 | color: theme.palette.text.secondary, |
50 | }, | 53 | }, |
51 | '.cm-gutters': { | 54 | '.cm-gutters': { |
52 | background: 'rgba(255, 255, 255, 0.1)', | 55 | background: 'transparent', |
53 | color: theme.palette.text.disabled, | 56 | color: theme.palette.text.disabled, |
54 | border: 'none', | 57 | border: 'none', |
55 | }, | 58 | }, |
@@ -57,7 +60,19 @@ export const EditorParent = styled('div')(({ theme }) => { | |||
57 | color: theme.palette.secondary.main, | 60 | color: theme.palette.secondary.main, |
58 | }, | 61 | }, |
59 | '.cm-activeLine': { | 62 | '.cm-activeLine': { |
60 | background: 'rgba(0, 0, 0, 0.3)', | 63 | background: alpha(theme.palette.text.secondary, 0.06), |
64 | }, | ||
65 | '.cm-foldGutter': { | ||
66 | color: alpha(theme.palette.text.primary, 0), | ||
67 | transition: theme.transitions.create('color', { | ||
68 | duration: theme.transitions.duration.short, | ||
69 | }), | ||
70 | '@media (hover: none)': { | ||
71 | color: theme.palette.text.primary, | ||
72 | }, | ||
73 | }, | ||
74 | '.cm-gutters:hover .cm-foldGutter': { | ||
75 | color: theme.palette.text.primary, | ||
61 | }, | 76 | }, |
62 | '.cm-activeLineGutter': { | 77 | '.cm-activeLineGutter': { |
63 | background: 'transparent', | 78 | background: 'transparent', |
@@ -66,8 +81,7 @@ export const EditorParent = styled('div')(({ theme }) => { | |||
66 | color: theme.palette.text.primary, | 81 | color: theme.palette.text.primary, |
67 | }, | 82 | }, |
68 | '.cm-cursor, .cm-cursor-primary': { | 83 | '.cm-cursor, .cm-cursor-primary': { |
69 | borderColor: theme.palette.primary.main, | 84 | borderLeft: `2px solid ${theme.palette.primary.main}`, |
70 | background: theme.palette.common.black, | ||
71 | }, | 85 | }, |
72 | '.cm-selectionBackground': { | 86 | '.cm-selectionBackground': { |
73 | background: '#3e4453', | 87 | background: '#3e4453', |
@@ -115,9 +129,26 @@ export const EditorParent = styled('div')(({ theme }) => { | |||
115 | }, | 129 | }, |
116 | }, | 130 | }, |
117 | '.cm-foldPlaceholder': { | 131 | '.cm-foldPlaceholder': { |
118 | background: theme.palette.background.paper, | ||
119 | borderColor: theme.palette.text.disabled, | ||
120 | color: theme.palette.text.secondary, | 132 | color: theme.palette.text.secondary, |
133 | backgroundColor: alpha(theme.palette.text.secondary, 0), | ||
134 | border: `1px solid ${alpha(theme.palette.text.secondary, 0.5)}`, | ||
135 | borderRadius: theme.shape.borderRadius, | ||
136 | transition: theme.transitions.create( | ||
137 | ['background-color', 'border-color', 'color'], | ||
138 | { | ||
139 | duration: theme.transitions.duration.short, | ||
140 | }, | ||
141 | ), | ||
142 | '&:hover': { | ||
143 | backgroundColor: alpha( | ||
144 | theme.palette.text.secondary, | ||
145 | theme.palette.action.hoverOpacity, | ||
146 | ), | ||
147 | borderColor: theme.palette.text.secondary, | ||
148 | '@media (hover: none)': { | ||
149 | backgroundColor: 'transparent', | ||
150 | }, | ||
151 | }, | ||
121 | }, | 152 | }, |
122 | '.tok-comment': { | 153 | '.tok-comment': { |
123 | fontStyle: 'italic', | 154 | fontStyle: 'italic', |
@@ -168,9 +199,14 @@ export const EditorParent = styled('div')(({ theme }) => { | |||
168 | }, | 199 | }, |
169 | '.cm-tooltip-autocomplete': { | 200 | '.cm-tooltip-autocomplete': { |
170 | background: theme.palette.background.paper, | 201 | background: theme.palette.background.paper, |
171 | boxShadow: `0px 2px 4px -1px rgb(0 0 0 / 20%), | 202 | ...(theme.palette.mode === 'dark' && { |
172 | 0px 4px 5px 0px rgb(0 0 0 / 14%), | 203 | overflow: 'hidden', |
173 | 0px 1px 10px 0px rgb(0 0 0 / 12%)`, | 204 | borderRadius: theme.shape.borderRadius, |
205 | // https://github.com/mui/material-ui/blob/10c72729c7d03bab8cdce6eb422642684c56dca2/packages/mui-material/src/Paper/Paper.js#L18 | ||
206 | backgroundImage: | ||
207 | 'linear-gradient(rgba(255, 255, 255, 0.09), rgba(255, 255, 255, 0.09))', | ||
208 | }), | ||
209 | boxShadow: theme.shadows[4], | ||
174 | '.cm-completionIcon': { | 210 | '.cm-completionIcon': { |
175 | color: theme.palette.text.secondary, | 211 | color: theme.palette.text.secondary, |
176 | }, | 212 | }, |
diff --git a/subprojects/frontend/src/editor/EditorStore.ts b/subprojects/frontend/src/editor/EditorStore.ts index 0f4d2936..f75147a4 100644 --- a/subprojects/frontend/src/editor/EditorStore.ts +++ b/subprojects/frontend/src/editor/EditorStore.ts | |||
@@ -21,18 +21,14 @@ import { | |||
21 | indentOnInput, | 21 | indentOnInput, |
22 | syntaxHighlighting, | 22 | syntaxHighlighting, |
23 | } from '@codemirror/language'; | 23 | } from '@codemirror/language'; |
24 | import { | 24 | import { type Diagnostic, lintKeymap, setDiagnostics } from '@codemirror/lint'; |
25 | Diagnostic, | ||
26 | lintKeymap, | ||
27 | setDiagnostics, | ||
28 | } from '@codemirror/lint'; | ||
29 | import { search, searchKeymap } from '@codemirror/search'; | 25 | import { search, searchKeymap } from '@codemirror/search'; |
30 | import { | 26 | import { |
31 | EditorState, | 27 | EditorState, |
32 | StateCommand, | 28 | type StateCommand, |
33 | StateEffect, | 29 | StateEffect, |
34 | Transaction, | 30 | type Transaction, |
35 | TransactionSpec, | 31 | type TransactionSpec, |
36 | } from '@codemirror/state'; | 32 | } from '@codemirror/state'; |
37 | import { | 33 | import { |
38 | drawSelection, | 34 | drawSelection, |
@@ -45,26 +41,25 @@ import { | |||
45 | rectangularSelection, | 41 | rectangularSelection, |
46 | } from '@codemirror/view'; | 42 | } from '@codemirror/view'; |
47 | import { classHighlighter } from '@lezer/highlight'; | 43 | import { classHighlighter } from '@lezer/highlight'; |
48 | import { | 44 | import { makeAutoObservable, observable, reaction } from 'mobx'; |
49 | makeAutoObservable, | 45 | |
50 | observable, | 46 | import problemLanguageSupport from '../language/problemLanguageSupport'; |
51 | reaction, | 47 | import type ThemeStore from '../theme/ThemeStore'; |
52 | } from 'mobx'; | 48 | import getLogger from '../utils/getLogger'; |
53 | 49 | import XtextClient from '../xtext/XtextClient'; | |
54 | import { findOccurrences, IOccurrence, setOccurrences } from './findOccurrences'; | 50 | |
55 | import { problemLanguageSupport } from '../language/problemLanguageSupport'; | 51 | import findOccurrences, { |
56 | import { | 52 | type IOccurrence, |
57 | IHighlightRange, | 53 | setOccurrences, |
58 | semanticHighlighting, | 54 | } from './findOccurrences'; |
55 | import semanticHighlighting, { | ||
56 | type IHighlightRange, | ||
59 | setSemanticHighlighting, | 57 | setSemanticHighlighting, |
60 | } from './semanticHighlighting'; | 58 | } from './semanticHighlighting'; |
61 | import type { ThemeStore } from '../theme/ThemeStore'; | ||
62 | import { getLogger } from '../utils/logger'; | ||
63 | import { XtextClient } from '../xtext/XtextClient'; | ||
64 | 59 | ||
65 | const log = getLogger('editor.EditorStore'); | 60 | const log = getLogger('editor.EditorStore'); |
66 | 61 | ||
67 | export class EditorStore { | 62 | export default class EditorStore { |
68 | private readonly themeStore; | 63 | private readonly themeStore; |
69 | 64 | ||
70 | state: EditorState; | 65 | state: EditorState; |
@@ -96,17 +91,18 @@ export class EditorStore { | |||
96 | extensions: [ | 91 | extensions: [ |
97 | autocompletion({ | 92 | autocompletion({ |
98 | activateOnTyping: true, | 93 | activateOnTyping: true, |
99 | override: [ | 94 | override: [(context) => this.client.contentAssist(context)], |
100 | (context) => this.client.contentAssist(context), | ||
101 | ], | ||
102 | }), | 95 | }), |
103 | closeBrackets(), | 96 | closeBrackets(), |
104 | bracketMatching(), | 97 | bracketMatching(), |
105 | drawSelection(), | 98 | drawSelection(), |
106 | EditorState.allowMultipleSelections.of(true), | 99 | EditorState.allowMultipleSelections.of(true), |
107 | EditorView.theme({}, { | 100 | EditorView.theme( |
108 | dark: this.themeStore.darkMode, | 101 | {}, |
109 | }), | 102 | { |
103 | dark: this.themeStore.darkMode, | ||
104 | }, | ||
105 | ), | ||
110 | findOccurrences, | 106 | findOccurrences, |
111 | highlightActiveLine(), | 107 | highlightActiveLine(), |
112 | highlightActiveLineGutter(), | 108 | highlightActiveLineGutter(), |
@@ -134,8 +130,16 @@ export class EditorStore { | |||
134 | { key: 'Mod-Shift-m', run: () => this.setLintPanelOpen(true) }, | 130 | { key: 'Mod-Shift-m', run: () => this.setLintPanelOpen(true) }, |
135 | ...lintKeymap, | 131 | ...lintKeymap, |
136 | // Override keys in `searchKeymap` to go through the `EditorStore`. | 132 | // Override keys in `searchKeymap` to go through the `EditorStore`. |
137 | { key: 'Mod-f', run: () => this.setSearchPanelOpen(true), scope: 'editor search-panel' }, | 133 | { |
138 | { key: 'Escape', run: () => this.setSearchPanelOpen(false), scope: 'editor search-panel' }, | 134 | key: 'Mod-f', |
135 | run: () => this.setSearchPanelOpen(true), | ||
136 | scope: 'editor search-panel', | ||
137 | }, | ||
138 | { | ||
139 | key: 'Escape', | ||
140 | run: () => this.setSearchPanelOpen(false), | ||
141 | scope: 'editor search-panel', | ||
142 | }, | ||
139 | ...searchKeymap, | 143 | ...searchKeymap, |
140 | ...defaultKeymap, | 144 | ...defaultKeymap, |
141 | ]), | 145 | ]), |
@@ -149,9 +153,14 @@ export class EditorStore { | |||
149 | log.debug('Update editor dark mode', darkMode); | 153 | log.debug('Update editor dark mode', darkMode); |
150 | this.dispatch({ | 154 | this.dispatch({ |
151 | effects: [ | 155 | effects: [ |
152 | StateEffect.appendConfig.of(EditorView.theme({}, { | 156 | StateEffect.appendConfig.of( |
153 | dark: darkMode, | 157 | EditorView.theme( |
154 | })), | 158 | {}, |
159 | { | ||
160 | dark: darkMode, | ||
161 | }, | ||
162 | ), | ||
163 | ), | ||
155 | ], | 164 | ], |
156 | }); | 165 | }); |
157 | }, | 166 | }, |
@@ -198,6 +207,8 @@ export class EditorStore { | |||
198 | case 'info': | 207 | case 'info': |
199 | this.infoCount += 1; | 208 | this.infoCount += 1; |
200 | break; | 209 | break; |
210 | default: | ||
211 | throw new Error('Unknown severity'); | ||
201 | } | 212 | } |
202 | }); | 213 | }); |
203 | } | 214 | } |
@@ -261,7 +272,7 @@ export class EditorStore { | |||
261 | * This matches the behavior of the `openSearchPanel` and `closeSearchPanel` | 272 | * This matches the behavior of the `openSearchPanel` and `closeSearchPanel` |
262 | * commands from `'@codemirror/search'`. | 273 | * commands from `'@codemirror/search'`. |
263 | * | 274 | * |
264 | * @param newShosSearchPanel whether we should show the search panel | 275 | * @param newShowSearchPanel whether we should show the search panel |
265 | * @returns `true` if the state was changed, `false` otherwise | 276 | * @returns `true` if the state was changed, `false` otherwise |
266 | */ | 277 | */ |
267 | setSearchPanelOpen(newShowSearchPanel: boolean): boolean { | 278 | setSearchPanelOpen(newShowSearchPanel: boolean): boolean { |
diff --git a/subprojects/frontend/src/editor/GenerateButton.tsx b/subprojects/frontend/src/editor/GenerateButton.tsx index 3834cec4..fc337da9 100644 --- a/subprojects/frontend/src/editor/GenerateButton.tsx +++ b/subprojects/frontend/src/editor/GenerateButton.tsx | |||
@@ -1,13 +1,13 @@ | |||
1 | import { observer } from 'mobx-react-lite'; | ||
2 | import Button from '@mui/material/Button'; | ||
3 | import PlayArrowIcon from '@mui/icons-material/PlayArrow'; | 1 | import PlayArrowIcon from '@mui/icons-material/PlayArrow'; |
2 | import Button from '@mui/material/Button'; | ||
3 | import { observer } from 'mobx-react-lite'; | ||
4 | import React from 'react'; | 4 | import React from 'react'; |
5 | 5 | ||
6 | import { useRootStore } from '../RootStore'; | 6 | import { useRootStore } from '../RootStore'; |
7 | 7 | ||
8 | const GENERATE_LABEL = 'Generate'; | 8 | const GENERATE_LABEL = 'Generate'; |
9 | 9 | ||
10 | export const GenerateButton = observer(() => { | 10 | function GenerateButton(): JSX.Element { |
11 | const { editorStore } = useRootStore(); | 11 | const { editorStore } = useRootStore(); |
12 | const { errorCount, warningCount } = editorStore; | 12 | const { errorCount, warningCount } = editorStore; |
13 | 13 | ||
@@ -41,4 +41,6 @@ export const GenerateButton = observer(() => { | |||
41 | {summary === '' ? GENERATE_LABEL : `${GENERATE_LABEL} (${summary})`} | 41 | {summary === '' ? GENERATE_LABEL : `${GENERATE_LABEL} (${summary})`} |
42 | </Button> | 42 | </Button> |
43 | ); | 43 | ); |
44 | }); | 44 | } |
45 | |||
46 | export default observer(GenerateButton); | ||
diff --git a/subprojects/frontend/src/editor/decorationSetExtension.ts b/subprojects/frontend/src/editor/defineDecorationSetExtension.ts index 2d630c20..d9c7bc7d 100644 --- a/subprojects/frontend/src/editor/decorationSetExtension.ts +++ b/subprojects/frontend/src/editor/defineDecorationSetExtension.ts | |||
@@ -1,11 +1,16 @@ | |||
1 | import { StateEffect, StateField, TransactionSpec } from '@codemirror/state'; | 1 | import { StateEffect, StateField, TransactionSpec } from '@codemirror/state'; |
2 | import { EditorView, Decoration, DecorationSet } from '@codemirror/view'; | 2 | import { EditorView, Decoration, DecorationSet } from '@codemirror/view'; |
3 | 3 | ||
4 | export type TransactionSpecFactory = (decorations: DecorationSet) => TransactionSpec; | 4 | export type TransactionSpecFactory = ( |
5 | decorations: DecorationSet, | ||
6 | ) => TransactionSpec; | ||
5 | 7 | ||
6 | export function decorationSetExtension(): [TransactionSpecFactory, StateField<DecorationSet>] { | 8 | export default function defineDecorationSetExtension(): [ |
9 | TransactionSpecFactory, | ||
10 | StateField<DecorationSet>, | ||
11 | ] { | ||
7 | const setEffect = StateEffect.define<DecorationSet>(); | 12 | const setEffect = StateEffect.define<DecorationSet>(); |
8 | const field = StateField.define<DecorationSet>({ | 13 | const stateField = StateField.define<DecorationSet>({ |
9 | create() { | 14 | create() { |
10 | return Decoration.none; | 15 | return Decoration.none; |
11 | }, | 16 | }, |
@@ -24,16 +29,14 @@ export function decorationSetExtension(): [TransactionSpecFactory, StateField<De | |||
24 | } | 29 | } |
25 | return newDecorations; | 30 | return newDecorations; |
26 | }, | 31 | }, |
27 | provide: (f) => EditorView.decorations.from(f), | 32 | provide: (field) => EditorView.decorations.from(field), |
28 | }); | 33 | }); |
29 | 34 | ||
30 | function transactionSpecFactory(decorations: DecorationSet) { | 35 | function transactionSpecFactory(decorations: DecorationSet) { |
31 | return { | 36 | return { |
32 | effects: [ | 37 | effects: [setEffect.of(decorations)], |
33 | setEffect.of(decorations), | ||
34 | ], | ||
35 | }; | 38 | }; |
36 | } | 39 | } |
37 | 40 | ||
38 | return [transactionSpecFactory, field]; | 41 | return [transactionSpecFactory, stateField]; |
39 | } | 42 | } |
diff --git a/subprojects/frontend/src/editor/findOccurrences.ts b/subprojects/frontend/src/editor/findOccurrences.ts index c4a4e8ec..d7aae8d1 100644 --- a/subprojects/frontend/src/editor/findOccurrences.ts +++ b/subprojects/frontend/src/editor/findOccurrences.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { Range, RangeSet, type TransactionSpec } from '@codemirror/state'; | 1 | import { type Range, RangeSet, type TransactionSpec } from '@codemirror/state'; |
2 | import { Decoration } from '@codemirror/view'; | 2 | import { Decoration } from '@codemirror/view'; |
3 | 3 | ||
4 | import { decorationSetExtension } from './decorationSetExtension'; | 4 | import defineDecorationSetExtension from './defineDecorationSetExtension'; |
5 | 5 | ||
6 | export interface IOccurrence { | 6 | export interface IOccurrence { |
7 | from: number; | 7 | from: number; |
@@ -9,7 +9,7 @@ export interface IOccurrence { | |||
9 | to: number; | 9 | to: number; |
10 | } | 10 | } |
11 | 11 | ||
12 | const [setOccurrencesInteral, findOccurrences] = decorationSetExtension(); | 12 | const [setOccurrencesInteral, findOccurrences] = defineDecorationSetExtension(); |
13 | 13 | ||
14 | const writeDecoration = Decoration.mark({ | 14 | const writeDecoration = Decoration.mark({ |
15 | class: 'cm-problem-write', | 15 | class: 'cm-problem-write', |
@@ -19,7 +19,10 @@ const readDecoration = Decoration.mark({ | |||
19 | class: 'cm-problem-read', | 19 | class: 'cm-problem-read', |
20 | }); | 20 | }); |
21 | 21 | ||
22 | export function setOccurrences(write: IOccurrence[], read: IOccurrence[]): TransactionSpec { | 22 | export function setOccurrences( |
23 | write: IOccurrence[], | ||
24 | read: IOccurrence[], | ||
25 | ): TransactionSpec { | ||
23 | const decorations: Range<Decoration>[] = []; | 26 | const decorations: Range<Decoration>[] = []; |
24 | write.forEach(({ from, to }) => { | 27 | write.forEach(({ from, to }) => { |
25 | decorations.push(writeDecoration.range(from, to)); | 28 | decorations.push(writeDecoration.range(from, to)); |
@@ -31,4 +34,4 @@ export function setOccurrences(write: IOccurrence[], read: IOccurrence[]): Trans | |||
31 | return setOccurrencesInteral(rangeSet); | 34 | return setOccurrencesInteral(rangeSet); |
32 | } | 35 | } |
33 | 36 | ||
34 | export { findOccurrences }; | 37 | export default findOccurrences; |
diff --git a/subprojects/frontend/src/editor/semanticHighlighting.ts b/subprojects/frontend/src/editor/semanticHighlighting.ts index a5d0af7a..2c1bd67d 100644 --- a/subprojects/frontend/src/editor/semanticHighlighting.ts +++ b/subprojects/frontend/src/editor/semanticHighlighting.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { RangeSet, type TransactionSpec } from '@codemirror/state'; | 1 | import { RangeSet, type TransactionSpec } from '@codemirror/state'; |
2 | import { Decoration } from '@codemirror/view'; | 2 | import { Decoration } from '@codemirror/view'; |
3 | 3 | ||
4 | import { decorationSetExtension } from './decorationSetExtension'; | 4 | import defineDecorationSetExtension from './defineDecorationSetExtension'; |
5 | 5 | ||
6 | export interface IHighlightRange { | 6 | export interface IHighlightRange { |
7 | from: number; | 7 | from: number; |
@@ -11,13 +11,21 @@ export interface IHighlightRange { | |||
11 | classes: string[]; | 11 | classes: string[]; |
12 | } | 12 | } |
13 | 13 | ||
14 | const [setSemanticHighlightingInternal, semanticHighlighting] = decorationSetExtension(); | 14 | const [setSemanticHighlightingInternal, semanticHighlighting] = |
15 | defineDecorationSetExtension(); | ||
15 | 16 | ||
16 | export function setSemanticHighlighting(ranges: IHighlightRange[]): TransactionSpec { | 17 | export function setSemanticHighlighting( |
17 | const rangeSet = RangeSet.of(ranges.map(({ from, to, classes }) => Decoration.mark({ | 18 | ranges: IHighlightRange[], |
18 | class: classes.map((c) => `tok-problem-${c}`).join(' '), | 19 | ): TransactionSpec { |
19 | }).range(from, to)), true); | 20 | const rangeSet = RangeSet.of( |
21 | ranges.map(({ from, to, classes }) => | ||
22 | Decoration.mark({ | ||
23 | class: classes.map((c) => `tok-problem-${c}`).join(' '), | ||
24 | }).range(from, to), | ||
25 | ), | ||
26 | true, | ||
27 | ); | ||
20 | return setSemanticHighlightingInternal(rangeSet); | 28 | return setSemanticHighlightingInternal(rangeSet); |
21 | } | 29 | } |
22 | 30 | ||
23 | export { semanticHighlighting }; | 31 | export default semanticHighlighting; |