summaryrefslogtreecommitdiffstats
path: root/subprojects/frontend/src
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <marussy@mit.bme.hu>2023-06-18 18:04:43 +0200
committerLibravatar GitHub <noreply@github.com>2023-06-18 18:04:43 +0200
commita0619c0fbf1fab2304ca683343803d1f37c2ef7c (patch)
tree6d6e342b019a1f3ad0baae710cf520a5dffe4b9e /subprojects/frontend/src
parentMerge pull request #24 from kris7t/partial-interpretation (diff)
parentchore(deps): bump dependencies (diff)
downloadrefinery-a0619c0fbf1fab2304ca683343803d1f37c2ef7c.tar.gz
refinery-a0619c0fbf1fab2304ca683343803d1f37c2ef7c.tar.zst
refinery-a0619c0fbf1fab2304ca683343803d1f37c2ef7c.zip
Merge pull request #26 from kris7t/query-refactor
Query refactor
Diffstat (limited to 'subprojects/frontend/src')
-rw-r--r--subprojects/frontend/src/App.tsx6
-rw-r--r--subprojects/frontend/src/Loading.tsx6
-rw-r--r--subprojects/frontend/src/PWAStore.ts6
-rw-r--r--subprojects/frontend/src/Refinery.tsx6
-rw-r--r--subprojects/frontend/src/RootStore.ts6
-rw-r--r--subprojects/frontend/src/RootStoreProvider.tsx6
-rw-r--r--subprojects/frontend/src/ToggleDarkModeButton.tsx6
-rw-r--r--subprojects/frontend/src/TopBar.tsx6
-rw-r--r--subprojects/frontend/src/UpdateNotification.tsx6
-rw-r--r--subprojects/frontend/src/WindowControlsOverlayColor.tsx6
-rw-r--r--subprojects/frontend/src/editor/AnimatedButton.tsx6
-rw-r--r--subprojects/frontend/src/editor/ConnectButton.tsx6
-rw-r--r--subprojects/frontend/src/editor/ConnectionStatusNotification.tsx6
-rw-r--r--subprojects/frontend/src/editor/DiagnosticValue.ts6
-rw-r--r--subprojects/frontend/src/editor/EditorArea.tsx6
-rw-r--r--subprojects/frontend/src/editor/EditorButtons.tsx6
-rw-r--r--subprojects/frontend/src/editor/EditorPane.tsx6
-rw-r--r--subprojects/frontend/src/editor/EditorStore.ts6
-rw-r--r--subprojects/frontend/src/editor/EditorTheme.ts132
-rw-r--r--subprojects/frontend/src/editor/GenerateButton.tsx6
-rw-r--r--subprojects/frontend/src/editor/LintPanelStore.ts6
-rw-r--r--subprojects/frontend/src/editor/PanelStore.ts6
-rw-r--r--subprojects/frontend/src/editor/SearchPanel.ts6
-rw-r--r--subprojects/frontend/src/editor/SearchPanelPortal.tsx6
-rw-r--r--subprojects/frontend/src/editor/SearchPanelStore.ts6
-rw-r--r--subprojects/frontend/src/editor/SearchToolbar.tsx6
-rw-r--r--subprojects/frontend/src/editor/createEditorState.ts10
-rw-r--r--subprojects/frontend/src/editor/defineDecorationSetExtension.ts6
-rw-r--r--subprojects/frontend/src/editor/exposeDiagnostics.ts6
-rw-r--r--subprojects/frontend/src/editor/findOccurrences.ts6
-rw-r--r--subprojects/frontend/src/editor/indentationMarkerViewPlugin.ts341
-rw-r--r--subprojects/frontend/src/editor/scrollbarViewPlugin.ts357
-rw-r--r--subprojects/frontend/src/editor/semanticHighlighting.ts6
-rw-r--r--subprojects/frontend/src/index.tsx6
-rw-r--r--subprojects/frontend/src/language/folding.ts6
-rw-r--r--subprojects/frontend/src/language/indentation.ts7
-rw-r--r--subprojects/frontend/src/language/problem.grammar6
-rw-r--r--subprojects/frontend/src/language/problemLanguageSupport.ts6
-rw-r--r--subprojects/frontend/src/language/props.ts6
-rw-r--r--subprojects/frontend/src/theme/ThemeProvider.tsx10
-rw-r--r--subprojects/frontend/src/theme/ThemeStore.ts6
-rw-r--r--subprojects/frontend/src/utils/CancelledError.ts6
-rw-r--r--subprojects/frontend/src/utils/PendingTask.ts6
-rw-r--r--subprojects/frontend/src/utils/PriorityMutex.ts6
-rw-r--r--subprojects/frontend/src/utils/TimeoutError.ts6
-rw-r--r--subprojects/frontend/src/utils/getLogger.ts6
-rw-r--r--subprojects/frontend/src/utils/useDelayedSnackbar.ts6
-rw-r--r--subprojects/frontend/src/xtext/BackendConfig.ts6
-rw-r--r--subprojects/frontend/src/xtext/ContentAssistService.ts6
-rw-r--r--subprojects/frontend/src/xtext/HighlightingService.ts6
-rw-r--r--subprojects/frontend/src/xtext/OccurrencesService.ts6
-rw-r--r--subprojects/frontend/src/xtext/UpdateService.ts6
-rw-r--r--subprojects/frontend/src/xtext/UpdateStateTracker.ts6
-rw-r--r--subprojects/frontend/src/xtext/ValidationService.ts6
-rw-r--r--subprojects/frontend/src/xtext/XtextClient.ts6
-rw-r--r--subprojects/frontend/src/xtext/XtextWebSocketClient.ts6
-rw-r--r--subprojects/frontend/src/xtext/fetchBackendConfig.ts6
-rw-r--r--subprojects/frontend/src/xtext/webSocketMachine.ts6
-rw-r--r--subprojects/frontend/src/xtext/xtextMessages.ts6
-rw-r--r--subprojects/frontend/src/xtext/xtextServiceResults.ts6
60 files changed, 354 insertions, 827 deletions
diff --git a/subprojects/frontend/src/App.tsx b/subprojects/frontend/src/App.tsx
index cd394345..7f242529 100644
--- a/subprojects/frontend/src/App.tsx
+++ b/subprojects/frontend/src/App.tsx
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import Box from '@mui/material/Box'; 7import Box from '@mui/material/Box';
2import CssBaseline from '@mui/material/CssBaseline'; 8import CssBaseline from '@mui/material/CssBaseline';
3import { throttle } from 'lodash-es'; 9import { throttle } from 'lodash-es';
diff --git a/subprojects/frontend/src/Loading.tsx b/subprojects/frontend/src/Loading.tsx
index 489563e0..adee4f0e 100644
--- a/subprojects/frontend/src/Loading.tsx
+++ b/subprojects/frontend/src/Loading.tsx
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import CircularProgress from '@mui/material/CircularProgress'; 7import CircularProgress from '@mui/material/CircularProgress';
2import { styled } from '@mui/material/styles'; 8import { styled } from '@mui/material/styles';
3 9
diff --git a/subprojects/frontend/src/PWAStore.ts b/subprojects/frontend/src/PWAStore.ts
index e9f99e2a..a1b3ffd9 100644
--- a/subprojects/frontend/src/PWAStore.ts
+++ b/subprojects/frontend/src/PWAStore.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import { makeAutoObservable, observable } from 'mobx'; 7import { makeAutoObservable, observable } from 'mobx';
2import ms from 'ms'; 8import ms from 'ms';
3// eslint-disable-next-line import/no-unresolved -- Importing virtual module. 9// eslint-disable-next-line import/no-unresolved -- Importing virtual module.
diff --git a/subprojects/frontend/src/Refinery.tsx b/subprojects/frontend/src/Refinery.tsx
index f0162349..b5ff94e1 100644
--- a/subprojects/frontend/src/Refinery.tsx
+++ b/subprojects/frontend/src/Refinery.tsx
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import Grow from '@mui/material/Grow'; 7import Grow from '@mui/material/Grow';
2import Stack from '@mui/material/Stack'; 8import Stack from '@mui/material/Stack';
3import { SnackbarProvider } from 'notistack'; 9import { SnackbarProvider } from 'notistack';
diff --git a/subprojects/frontend/src/RootStore.ts b/subprojects/frontend/src/RootStore.ts
index 2e76d66d..b84c0ce0 100644
--- a/subprojects/frontend/src/RootStore.ts
+++ b/subprojects/frontend/src/RootStore.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import { getLogger } from 'loglevel'; 7import { getLogger } from 'loglevel';
2import { makeAutoObservable, runInAction } from 'mobx'; 8import { makeAutoObservable, runInAction } from 'mobx';
3 9
diff --git a/subprojects/frontend/src/RootStoreProvider.tsx b/subprojects/frontend/src/RootStoreProvider.tsx
index 2c11a0f9..7cb89af1 100644
--- a/subprojects/frontend/src/RootStoreProvider.tsx
+++ b/subprojects/frontend/src/RootStoreProvider.tsx
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import { type ReactNode, createContext, useContext } from 'react'; 7import { type ReactNode, createContext, useContext } from 'react';
2 8
3import type RootStore from './RootStore'; 9import type RootStore from './RootStore';
diff --git a/subprojects/frontend/src/ToggleDarkModeButton.tsx b/subprojects/frontend/src/ToggleDarkModeButton.tsx
index 59714f20..7a835e61 100644
--- a/subprojects/frontend/src/ToggleDarkModeButton.tsx
+++ b/subprojects/frontend/src/ToggleDarkModeButton.tsx
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import DarkModeIcon from '@mui/icons-material/DarkMode'; 7import DarkModeIcon from '@mui/icons-material/DarkMode';
2import LightModeIcon from '@mui/icons-material/LightMode'; 8import LightModeIcon from '@mui/icons-material/LightMode';
3import IconButton from '@mui/material/IconButton'; 9import IconButton from '@mui/material/IconButton';
diff --git a/subprojects/frontend/src/TopBar.tsx b/subprojects/frontend/src/TopBar.tsx
index 5a825512..f2542b14 100644
--- a/subprojects/frontend/src/TopBar.tsx
+++ b/subprojects/frontend/src/TopBar.tsx
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import GitHubIcon from '@mui/icons-material/GitHub'; 7import GitHubIcon from '@mui/icons-material/GitHub';
2import AppBar from '@mui/material/AppBar'; 8import AppBar from '@mui/material/AppBar';
3import Button from '@mui/material/Button'; 9import Button from '@mui/material/Button';
diff --git a/subprojects/frontend/src/UpdateNotification.tsx b/subprojects/frontend/src/UpdateNotification.tsx
index 5c8c2d01..d86c0703 100644
--- a/subprojects/frontend/src/UpdateNotification.tsx
+++ b/subprojects/frontend/src/UpdateNotification.tsx
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import Button from '@mui/material/Button'; 7import Button from '@mui/material/Button';
2import { observer } from 'mobx-react-lite'; 8import { observer } from 'mobx-react-lite';
3import { useEffect } from 'react'; 9import { useEffect } from 'react';
diff --git a/subprojects/frontend/src/WindowControlsOverlayColor.tsx b/subprojects/frontend/src/WindowControlsOverlayColor.tsx
index 14eda566..cfa468ea 100644
--- a/subprojects/frontend/src/WindowControlsOverlayColor.tsx
+++ b/subprojects/frontend/src/WindowControlsOverlayColor.tsx
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import { useTheme } from '@mui/material/styles'; 7import { useTheme } from '@mui/material/styles';
2import { useEffect } from 'react'; 8import { useEffect } from 'react';
3 9
diff --git a/subprojects/frontend/src/editor/AnimatedButton.tsx b/subprojects/frontend/src/editor/AnimatedButton.tsx
index f75d4617..dbbda618 100644
--- a/subprojects/frontend/src/editor/AnimatedButton.tsx
+++ b/subprojects/frontend/src/editor/AnimatedButton.tsx
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import Box from '@mui/material/Box'; 7import Box from '@mui/material/Box';
2import Button from '@mui/material/Button'; 8import Button from '@mui/material/Button';
3import { styled, type SxProps, type Theme } from '@mui/material/styles'; 9import { styled, type SxProps, type Theme } from '@mui/material/styles';
diff --git a/subprojects/frontend/src/editor/ConnectButton.tsx b/subprojects/frontend/src/editor/ConnectButton.tsx
index e2d251f3..eed6fbc7 100644
--- a/subprojects/frontend/src/editor/ConnectButton.tsx
+++ b/subprojects/frontend/src/editor/ConnectButton.tsx
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import CloudIcon from '@mui/icons-material/Cloud'; 7import CloudIcon from '@mui/icons-material/Cloud';
2import CloudOffIcon from '@mui/icons-material/CloudOff'; 8import CloudOffIcon from '@mui/icons-material/CloudOff';
3import SyncIcon from '@mui/icons-material/Sync'; 9import SyncIcon from '@mui/icons-material/Sync';
diff --git a/subprojects/frontend/src/editor/ConnectionStatusNotification.tsx b/subprojects/frontend/src/editor/ConnectionStatusNotification.tsx
index 9b27f45c..b7b962ab 100644
--- a/subprojects/frontend/src/editor/ConnectionStatusNotification.tsx
+++ b/subprojects/frontend/src/editor/ConnectionStatusNotification.tsx
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import Button from '@mui/material/Button'; 7import Button from '@mui/material/Button';
2import { observer } from 'mobx-react-lite'; 8import { observer } from 'mobx-react-lite';
3import { useEffect } from 'react'; 9import { useEffect } from 'react';
diff --git a/subprojects/frontend/src/editor/DiagnosticValue.ts b/subprojects/frontend/src/editor/DiagnosticValue.ts
index b4e0b165..20478262 100644
--- a/subprojects/frontend/src/editor/DiagnosticValue.ts
+++ b/subprojects/frontend/src/editor/DiagnosticValue.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import type { Diagnostic } from '@codemirror/lint'; 7import type { Diagnostic } from '@codemirror/lint';
2import { RangeValue } from '@codemirror/state'; 8import { RangeValue } from '@codemirror/state';
3 9
diff --git a/subprojects/frontend/src/editor/EditorArea.tsx b/subprojects/frontend/src/editor/EditorArea.tsx
index cfb988b2..905fa2ec 100644
--- a/subprojects/frontend/src/editor/EditorArea.tsx
+++ b/subprojects/frontend/src/editor/EditorArea.tsx
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import Box from '@mui/material/Box'; 7import Box from '@mui/material/Box';
2import { useTheme } from '@mui/material/styles'; 8import { useTheme } from '@mui/material/styles';
3import { observer } from 'mobx-react-lite'; 9import { observer } from 'mobx-react-lite';
diff --git a/subprojects/frontend/src/editor/EditorButtons.tsx b/subprojects/frontend/src/editor/EditorButtons.tsx
index 53b06e23..9b187e5c 100644
--- a/subprojects/frontend/src/editor/EditorButtons.tsx
+++ b/subprojects/frontend/src/editor/EditorButtons.tsx
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import type { Diagnostic } from '@codemirror/lint'; 7import type { Diagnostic } from '@codemirror/lint';
2import CheckIcon from '@mui/icons-material/Check'; 8import CheckIcon from '@mui/icons-material/Check';
3import ErrorIcon from '@mui/icons-material/Error'; 9import ErrorIcon from '@mui/icons-material/Error';
diff --git a/subprojects/frontend/src/editor/EditorPane.tsx b/subprojects/frontend/src/editor/EditorPane.tsx
index f7f8241a..87f408fe 100644
--- a/subprojects/frontend/src/editor/EditorPane.tsx
+++ b/subprojects/frontend/src/editor/EditorPane.tsx
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import Box from '@mui/material/Box'; 7import Box from '@mui/material/Box';
2import Skeleton from '@mui/material/Skeleton'; 8import Skeleton from '@mui/material/Skeleton';
3import Stack from '@mui/material/Stack'; 9import Stack from '@mui/material/Stack';
diff --git a/subprojects/frontend/src/editor/EditorStore.ts b/subprojects/frontend/src/editor/EditorStore.ts
index 0a0d885d..b98f085e 100644
--- a/subprojects/frontend/src/editor/EditorStore.ts
+++ b/subprojects/frontend/src/editor/EditorStore.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import type { 7import type {
2 CompletionContext, 8 CompletionContext,
3 CompletionResult, 9 CompletionResult,
diff --git a/subprojects/frontend/src/editor/EditorTheme.ts b/subprojects/frontend/src/editor/EditorTheme.ts
index 01b65a7e..e057ce18 100644
--- a/subprojects/frontend/src/editor/EditorTheme.ts
+++ b/subprojects/frontend/src/editor/EditorTheme.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import errorSVG from '@material-icons/svg/svg/error/baseline.svg?raw'; 7import errorSVG from '@material-icons/svg/svg/error/baseline.svg?raw';
2import expandMoreSVG from '@material-icons/svg/svg/expand_more/baseline.svg?raw'; 8import expandMoreSVG from '@material-icons/svg/svg/expand_more/baseline.svg?raw';
3import infoSVG from '@material-icons/svg/svg/info/baseline.svg?raw'; 9import infoSVG from '@material-icons/svg/svg/info/baseline.svg?raw';
@@ -8,32 +14,6 @@ function svgURL(svg: string): string {
8 return `url('data:image/svg+xml;utf8,${svg}')`; 14 return `url('data:image/svg+xml;utf8,${svg}')`;
9} 15}
10 16
11function radialShadowTheme(
12 origin: string,
13 scaleX: boolean,
14 scaleY: boolean,
15): CSSObject {
16 function radialGradient(opacity: number, scale: string): string {
17 return `radial-gradient(
18 farthest-side at ${origin},
19 rgba(0, 0, 0, ${opacity}),
20 rgba(0, 0, 0, 0)
21 )
22 ${origin} /
23 ${scaleX ? scale : '100%'}
24 ${scaleY ? scale : '100%'}
25 no-repeat`;
26 }
27
28 return {
29 background: `
30 ${radialGradient(0.2, '40%')},
31 ${radialGradient(0.14, '50%')},
32 ${radialGradient(0.12, '100%')}
33 `,
34 };
35}
36
37export default styled('div', { 17export default styled('div', {
38 name: 'EditorTheme', 18 name: 'EditorTheme',
39 shouldForwardProp: (propName) => 19 shouldForwardProp: (propName) =>
@@ -52,98 +32,13 @@ export default styled('div', {
52 }, 32 },
53 }; 33 };
54 34
55 const scrollerThumbOpacity = theme.palette.mode === 'dark' ? 0.16 : 0.28;
56
57 const generalStyle: CSSObject = { 35 const generalStyle: CSSObject = {
58 background: theme.palette.background.default, 36 background: theme.palette.background.default,
59 '&, .cm-editor': { 37 '&, .cm-editor': {
60 height: '100%', 38 height: '100%',
61 }, 39 },
62 '.cm-scroller-holder': {
63 display: 'flex',
64 position: 'relative',
65 flexDirection: 'column',
66 overflow: 'hidden',
67 flex: '1 1',
68 },
69 '.cm-scroller-spacer': {
70 position: 'sticky',
71 flexShrink: 0,
72 zIndex: 300,
73 width: 1,
74 marginRight: -1,
75 pointerEvents: 'none',
76 },
77 '.cm-scroller': { 40 '.cm-scroller': {
78 color: theme.palette.text.secondary, 41 color: theme.palette.text.secondary,
79 scrollbarWidth: 'none',
80 MsOverflowStyle: 'none',
81 '&::-webkit-scrollbar': {
82 width: 0,
83 height: 0,
84 background: 'transparent',
85 },
86 },
87 '.cm-scroller-track': {
88 position: 'absolute',
89 zIndex: 300,
90 touchAction: 'none',
91 },
92 '.cm-scroller-thumb': {
93 position: 'absolute',
94 background: theme.palette.text.secondary,
95 opacity: scrollerThumbOpacity,
96 transition: theme.transitions.create('opacity', {
97 duration: theme.transitions.duration.shortest,
98 }),
99 touchAction: 'none',
100 WebkitTapHighlightColor: 'transparent',
101 '&:hover': {
102 opacity: 0.75,
103 '@media (hover: none)': {
104 opacity: scrollerThumbOpacity,
105 },
106 },
107 '&.active': {
108 opacity: 1,
109 pointerEvents: 'none',
110 userSelect: 'none',
111 },
112 },
113 '.cm-scroller-track-y, .cm-scroller-thumb-y': {
114 top: 0,
115 right: 0,
116 width: 12,
117 },
118 '.cm-scroller-track-x, .cm-scroller-thumb-x': {
119 left: 0,
120 bottom: 0,
121 height: 12,
122 },
123 '.cm-scroller-track-x': {
124 right: 12,
125 },
126 '.cm-scroller-gutter-decoration': {
127 position: 'absolute',
128 top: 0,
129 bottom: 0,
130 left: 0,
131 width: 0,
132 transition: theme.transitions.create('width', {
133 duration: theme.transitions.duration.shortest,
134 }),
135 ...radialShadowTheme('0 50%', true, false),
136 },
137 '.cm-scroller-top-decoration': {
138 position: 'absolute',
139 top: 0,
140 left: 0,
141 right: 0,
142 height: 0,
143 transition: theme.transitions.create('height', {
144 duration: theme.transitions.duration.shortest,
145 }),
146 ...radialShadowTheme('50% 0', false, true),
147 }, 42 },
148 '.cm-gutters': { 43 '.cm-gutters': {
149 background: theme.palette.background.default, 44 background: theme.palette.background.default,
@@ -162,7 +57,6 @@ export default styled('div', {
162 background: 'transparent', 57 background: 'transparent',
163 }, 58 },
164 '.cm-cursor, .cm-cursor-primary': { 59 '.cm-cursor, .cm-cursor-primary': {
165 marginLeft: -1,
166 borderLeft: `2px solid ${theme.palette.info.main}`, 60 borderLeft: `2px solid ${theme.palette.info.main}`,
167 }, 61 },
168 '.cm-selectionBackground': { 62 '.cm-selectionBackground': {
@@ -175,7 +69,6 @@ export default styled('div', {
175 }, 69 },
176 }, 70 },
177 '.cm-line': { 71 '.cm-line': {
178 position: 'relative', // For indentation highlights
179 padding: '0 12px 0 0px', 72 padding: '0 12px 0 0px',
180 }, 73 },
181 }; 74 };
@@ -265,13 +158,6 @@ export default styled('div', {
265 '.cm-searchMatch-selected': { 158 '.cm-searchMatch-selected': {
266 background: theme.palette.highlight.search.selected, 159 background: theme.palette.highlight.search.selected,
267 }, 160 },
268 '.cm-indentation-marker': {
269 display: 'inline-block',
270 boxShadow: `1px 0 0 ${theme.palette.text.disabled} inset`,
271 '&.active': {
272 boxShadow: `1px 0 0 ${theme.palette.text.primary} inset`,
273 },
274 },
275 '.cm-scroller-selection': { 161 '.cm-scroller-selection': {
276 position: 'absolute', 162 position: 'absolute',
277 right: 0, 163 right: 0,
@@ -452,11 +338,11 @@ export default styled('div', {
452 338
453 const foldStyle = { 339 const foldStyle = {
454 '.cm-foldGutter': { 340 '.cm-foldGutter': {
455 width: 17, 341 width: 16,
456 }, 342 },
457 '.problem-editor-foldMarker': { 343 '.problem-editor-foldMarker': {
458 display: 'block', 344 display: 'block',
459 margin: '4px 1px 4px 0', 345 margin: '4px 0 4px 0',
460 padding: 0, 346 padding: 0,
461 maskImage: svgURL(expandMoreSVG), 347 maskImage: svgURL(expandMoreSVG),
462 maskSize: '16px 16px', 348 maskSize: '16px 16px',
@@ -467,7 +353,7 @@ export default styled('div', {
467 cursor: 'pointer', 353 cursor: 'pointer',
468 WebkitTapHighlightColor: 'transparent', 354 WebkitTapHighlightColor: 'transparent',
469 [theme.breakpoints.down('sm')]: { 355 [theme.breakpoints.down('sm')]: {
470 margin: '2px 1px 2px 0', 356 margin: '2px 0 2px 0',
471 }, 357 },
472 }, 358 },
473 '.problem-editor-foldMarker-open': { 359 '.problem-editor-foldMarker-open': {
diff --git a/subprojects/frontend/src/editor/GenerateButton.tsx b/subprojects/frontend/src/editor/GenerateButton.tsx
index 2036fc28..3837ef8e 100644
--- a/subprojects/frontend/src/editor/GenerateButton.tsx
+++ b/subprojects/frontend/src/editor/GenerateButton.tsx
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import DangerousOutlinedIcon from '@mui/icons-material/DangerousOutlined'; 7import DangerousOutlinedIcon from '@mui/icons-material/DangerousOutlined';
2import PlayArrowIcon from '@mui/icons-material/PlayArrow'; 8import PlayArrowIcon from '@mui/icons-material/PlayArrow';
3import Button from '@mui/material/Button'; 9import Button from '@mui/material/Button';
diff --git a/subprojects/frontend/src/editor/LintPanelStore.ts b/subprojects/frontend/src/editor/LintPanelStore.ts
index 502f9c59..f81587fa 100644
--- a/subprojects/frontend/src/editor/LintPanelStore.ts
+++ b/subprojects/frontend/src/editor/LintPanelStore.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import { closeLintPanel, openLintPanel } from '@codemirror/lint'; 7import { closeLintPanel, openLintPanel } from '@codemirror/lint';
2 8
3import type EditorStore from './EditorStore'; 9import type EditorStore from './EditorStore';
diff --git a/subprojects/frontend/src/editor/PanelStore.ts b/subprojects/frontend/src/editor/PanelStore.ts
index 4f827280..25ef8b6c 100644
--- a/subprojects/frontend/src/editor/PanelStore.ts
+++ b/subprojects/frontend/src/editor/PanelStore.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import type { Command } from '@codemirror/view'; 7import type { Command } from '@codemirror/view';
2import { action, makeObservable, observable } from 'mobx'; 8import { action, makeObservable, observable } from 'mobx';
3 9
diff --git a/subprojects/frontend/src/editor/SearchPanel.ts b/subprojects/frontend/src/editor/SearchPanel.ts
index c9df41b7..b63d5eed 100644
--- a/subprojects/frontend/src/editor/SearchPanel.ts
+++ b/subprojects/frontend/src/editor/SearchPanel.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import { 7import {
2 type EditorView, 8 type EditorView,
3 type Panel, 9 type Panel,
diff --git a/subprojects/frontend/src/editor/SearchPanelPortal.tsx b/subprojects/frontend/src/editor/SearchPanelPortal.tsx
index 5cf1c90e..b4b07c74 100644
--- a/subprojects/frontend/src/editor/SearchPanelPortal.tsx
+++ b/subprojects/frontend/src/editor/SearchPanelPortal.tsx
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import Portal from '@mui/material/Portal'; 7import Portal from '@mui/material/Portal';
2import { observer } from 'mobx-react-lite'; 8import { observer } from 'mobx-react-lite';
3 9
diff --git a/subprojects/frontend/src/editor/SearchPanelStore.ts b/subprojects/frontend/src/editor/SearchPanelStore.ts
index 65d595a8..6a97baf1 100644
--- a/subprojects/frontend/src/editor/SearchPanelStore.ts
+++ b/subprojects/frontend/src/editor/SearchPanelStore.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import { 7import {
2 closeSearchPanel, 8 closeSearchPanel,
3 findNext, 9 findNext,
diff --git a/subprojects/frontend/src/editor/SearchToolbar.tsx b/subprojects/frontend/src/editor/SearchToolbar.tsx
index 54f3dba7..4ae7e893 100644
--- a/subprojects/frontend/src/editor/SearchToolbar.tsx
+++ b/subprojects/frontend/src/editor/SearchToolbar.tsx
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import CloseIcon from '@mui/icons-material/Close'; 7import CloseIcon from '@mui/icons-material/Close';
2import FindReplaceIcon from '@mui/icons-material/FindReplace'; 8import FindReplaceIcon from '@mui/icons-material/FindReplace';
3import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; 9import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
diff --git a/subprojects/frontend/src/editor/createEditorState.ts b/subprojects/frontend/src/editor/createEditorState.ts
index ce1efa4f..67b8fb9e 100644
--- a/subprojects/frontend/src/editor/createEditorState.ts
+++ b/subprojects/frontend/src/editor/createEditorState.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import { 7import {
2 closeBrackets, 8 closeBrackets,
3 closeBracketsKeymap, 9 closeBracketsKeymap,
@@ -38,8 +44,6 @@ import type EditorStore from './EditorStore';
38import SearchPanel from './SearchPanel'; 44import SearchPanel from './SearchPanel';
39import exposeDiagnostics from './exposeDiagnostics'; 45import exposeDiagnostics from './exposeDiagnostics';
40import findOccurrences from './findOccurrences'; 46import findOccurrences from './findOccurrences';
41import indentationMarkerViewPlugin from './indentationMarkerViewPlugin';
42import scrollbarViewPlugin from './scrollbarViewPlugin';
43import semanticHighlighting from './semanticHighlighting'; 47import semanticHighlighting from './semanticHighlighting';
44 48
45export default function createEditorState( 49export default function createEditorState(
@@ -64,7 +68,6 @@ export default function createEditorState(
64 highlightSpecialChars(), 68 highlightSpecialChars(),
65 history(), 69 history(),
66 indentOnInput(), 70 indentOnInput(),
67 indentationMarkerViewPlugin(),
68 rectangularSelection(), 71 rectangularSelection(),
69 search({ 72 search({
70 createPanel(view) { 73 createPanel(view) {
@@ -123,7 +126,6 @@ export default function createEditorState(
123 ...defaultKeymap, 126 ...defaultKeymap,
124 ]), 127 ]),
125 problemLanguageSupport(), 128 problemLanguageSupport(),
126 scrollbarViewPlugin(store),
127 ], 129 ],
128 }); 130 });
129} 131}
diff --git a/subprojects/frontend/src/editor/defineDecorationSetExtension.ts b/subprojects/frontend/src/editor/defineDecorationSetExtension.ts
index d9c7bc7d..0887c92e 100644
--- a/subprojects/frontend/src/editor/defineDecorationSetExtension.ts
+++ b/subprojects/frontend/src/editor/defineDecorationSetExtension.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import { StateEffect, StateField, TransactionSpec } from '@codemirror/state'; 7import { StateEffect, StateField, TransactionSpec } from '@codemirror/state';
2import { EditorView, Decoration, DecorationSet } from '@codemirror/view'; 8import { EditorView, Decoration, DecorationSet } from '@codemirror/view';
3 9
diff --git a/subprojects/frontend/src/editor/exposeDiagnostics.ts b/subprojects/frontend/src/editor/exposeDiagnostics.ts
index 82f24c93..c4dcbb87 100644
--- a/subprojects/frontend/src/editor/exposeDiagnostics.ts
+++ b/subprojects/frontend/src/editor/exposeDiagnostics.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import { setDiagnosticsEffect } from '@codemirror/lint'; 7import { setDiagnosticsEffect } from '@codemirror/lint';
2import { 8import {
3 StateField, 9 StateField,
diff --git a/subprojects/frontend/src/editor/findOccurrences.ts b/subprojects/frontend/src/editor/findOccurrences.ts
index 08c078c2..00dffc96 100644
--- a/subprojects/frontend/src/editor/findOccurrences.ts
+++ b/subprojects/frontend/src/editor/findOccurrences.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import { 7import {
2 type Range, 8 type Range,
3 RangeSet, 9 RangeSet,
diff --git a/subprojects/frontend/src/editor/indentationMarkerViewPlugin.ts b/subprojects/frontend/src/editor/indentationMarkerViewPlugin.ts
deleted file mode 100644
index 730fa6e3..00000000
--- a/subprojects/frontend/src/editor/indentationMarkerViewPlugin.ts
+++ /dev/null
@@ -1,341 +0,0 @@
1/**
2 * @file CodeMirror plugin to highlight indentation
3 *
4 * This file is based on the
5 * [@replit/codemirror-indentation-markers](https://github.com/replit/codemirror-indentation-markers)
6 * package, which is available under the
7 * [MIT License](https://github.com/replit/codemirror-indentation-markers/blob/543cc508ca5cef5d8350af23973eb1425e31525c/LICENSE).
8 *
9 * The highlighting heuristics were adjusted to make them more suitable
10 * for logic programming.
11 *
12 * @see https://github.com/replit/codemirror-indentation-markers/blob/543cc508ca5cef5d8350af23973eb1425e31525c/src/index.ts
13 */
14
15import { getIndentUnit } from '@codemirror/language';
16import { Text, RangeSet, EditorState } from '@codemirror/state';
17import {
18 ViewPlugin,
19 Decoration,
20 EditorView,
21 WidgetType,
22 PluginValue,
23} from '@codemirror/view';
24
25export const INDENTATION_MARKER_CLASS = 'cm-indentation-marker';
26
27export const INDENTATION_MARKER_ACTIVE_CLASS = 'active';
28
29const indentationMark = Decoration.mark({
30 class: INDENTATION_MARKER_CLASS,
31 tagName: 'span',
32});
33
34const activeIndentationMark = Decoration.mark({
35 class: `${INDENTATION_MARKER_CLASS} ${INDENTATION_MARKER_ACTIVE_CLASS}`,
36 tagName: 'span',
37});
38
39/**
40 * Widget used to simulate N indentation markers on empty lines.
41 */
42class IndentationWidget extends WidgetType {
43 constructor(
44 readonly numIndent: number,
45 readonly indentSize: number,
46 readonly activeIndent?: number,
47 ) {
48 super();
49 }
50
51 override eq(other: IndentationWidget) {
52 return (
53 this.numIndent === other.numIndent &&
54 this.indentSize === other.indentSize &&
55 this.activeIndent === other.activeIndent
56 );
57 }
58
59 override toDOM(view: EditorView) {
60 const indentSize = getIndentUnit(view.state);
61
62 const wrapper = document.createElement('span');
63 wrapper.style.top = '0';
64 wrapper.style.left = '0';
65 wrapper.style.position = 'absolute';
66 wrapper.style.pointerEvents = 'none';
67
68 for (let indent = 0; indent < this.numIndent; indent += 1) {
69 const element = document.createElement('span');
70 element.className = INDENTATION_MARKER_CLASS;
71 element.classList.toggle(
72 INDENTATION_MARKER_ACTIVE_CLASS,
73 indent === this.activeIndent,
74 );
75 element.innerHTML = ' '.repeat(indentSize);
76 wrapper.appendChild(element);
77 }
78
79 return wrapper;
80 }
81}
82
83/**
84 * Returns the number of indentation markers a non-empty line should have
85 * based on the text in the line and the size of the indent.
86 */
87function getNumIndentMarkersForNonEmptyLine(
88 text: string,
89 indentSize: number,
90 onIndentMarker?: (pos: number) => void,
91) {
92 let numIndents = 0;
93 let numConsecutiveSpaces = 0;
94 let prevChar: string | undefined;
95
96 for (let char = 0; char < text.length; char += 1) {
97 // Bail if we encounter a non-whitespace character
98 if (text[char] !== ' ' && text[char] !== '\t') {
99 // We still increment the indentation level if we would
100 // have added a marker here had this been a space or tab.
101 if (numConsecutiveSpaces % indentSize === 0 && char !== 0) {
102 numIndents += 1;
103 }
104
105 return numIndents;
106 }
107
108 // Every tab and N space has an indentation marker
109 const shouldAddIndent =
110 prevChar === '\t' || numConsecutiveSpaces % indentSize === 0;
111
112 if (shouldAddIndent) {
113 numIndents += 1;
114
115 if (onIndentMarker) {
116 onIndentMarker(char);
117 }
118 }
119
120 if (text[char] === ' ') {
121 numConsecutiveSpaces += 1;
122 } else {
123 numConsecutiveSpaces = 0;
124 }
125
126 prevChar = text[char];
127 }
128
129 return numIndents;
130}
131
132/**
133 * Returns the number of indent markers an empty line should have
134 * based on the number of indent markers of the previous
135 * and next non-empty lines.
136 */
137function getNumIndentMarkersForEmptyLine(prev: number, next: number) {
138 const min = Math.min(prev, next);
139 const max = Math.max(prev, next);
140
141 // If only one side is non-zero, we omit markers,
142 // because in logic programming, a block often ends with an empty line.
143 if (min === 0 && max > 0) {
144 return 0;
145 }
146
147 // Else, default to the minimum of the two
148 return min;
149}
150
151/**
152 * Returns the next non-empty line and its indent level.
153 */
154function findNextNonEmptyLineAndIndentLevel(
155 doc: Text,
156 startLine: number,
157 indentSize: number,
158): [number, number] {
159 const numLines = doc.lines;
160 let lineNo = startLine;
161
162 while (lineNo <= numLines) {
163 const { text } = doc.line(lineNo);
164
165 if (text.trim().length === 0) {
166 lineNo += 1;
167 } else {
168 const indent = getNumIndentMarkersForNonEmptyLine(text, indentSize);
169 return [lineNo, indent];
170 }
171 }
172
173 // Reached the end of the doc
174 return [numLines + 1, 0];
175}
176
177interface IndentationMarkerDesc {
178 lineNumber: number;
179 from: number;
180 to: number;
181 create(activeIndentIndex?: number): Decoration;
182}
183
184/**
185 * Returns a range of lines with an active indent marker.
186 */
187function getLinesWithActiveIndentMarker(
188 state: EditorState,
189 indentMap: Map<number, number>,
190): { start: number; end: number; activeIndent: number } {
191 const currentLine = state.doc.lineAt(state.selection.main.head);
192 const currentIndent = indentMap.get(currentLine.number);
193 const currentLineNo = currentLine.number;
194
195 if (!currentIndent) {
196 return { start: -1, end: -1, activeIndent: NaN };
197 }
198
199 let start: number;
200 let end: number;
201
202 for (start = currentLineNo; start >= 0; start -= 1) {
203 const indent = indentMap.get(start - 1);
204 if (!indent || indent < currentIndent) {
205 break;
206 }
207 }
208
209 for (end = currentLineNo; ; end += 1) {
210 const indent = indentMap.get(end + 1);
211 if (!indent || indent < currentIndent) {
212 break;
213 }
214 }
215
216 return { start, end, activeIndent: currentIndent };
217}
218/**
219 * Adds indentation markers to all lines within view.
220 */
221function addIndentationMarkers(view: EditorView) {
222 const indentSize = getIndentUnit(view.state);
223 const indentSizeMap = new Map</* lineNumber */ number, number>();
224 const decorations: Array<IndentationMarkerDesc> = [];
225
226 view.visibleRanges.forEach(({ from, to }) => {
227 let pos = from;
228
229 let prevIndentMarkers = 0;
230 let nextIndentMarkers = 0;
231 let nextNonEmptyLine = 0;
232
233 while (pos <= to) {
234 const line = view.state.doc.lineAt(pos);
235 const { text } = line;
236
237 // If a line is empty, we match the indentation according
238 // to a heuristic based on the indentations of the
239 // previous and next non-empty lines.
240 if (text.trim().length === 0) {
241 // To retrieve the next non-empty indentation level,
242 // we perform a lookahead and cache the result.
243 if (nextNonEmptyLine < line.number) {
244 const [nextLine, nextIndent] = findNextNonEmptyLineAndIndentLevel(
245 view.state.doc,
246 line.number + 1,
247 indentSize,
248 );
249
250 nextNonEmptyLine = nextLine;
251 nextIndentMarkers = nextIndent;
252 }
253
254 const numIndentMarkers = getNumIndentMarkersForEmptyLine(
255 prevIndentMarkers,
256 nextIndentMarkers,
257 );
258
259 // Add the indent widget and move on to next line
260 indentSizeMap.set(line.number, numIndentMarkers);
261 decorations.push({
262 from: pos,
263 to: pos,
264 lineNumber: line.number,
265 create: (activeIndentIndex) =>
266 Decoration.widget({
267 widget: new IndentationWidget(
268 numIndentMarkers,
269 indentSize,
270 activeIndentIndex,
271 ),
272 }),
273 });
274 } else {
275 const indices: Array<number> = [];
276
277 prevIndentMarkers = getNumIndentMarkersForNonEmptyLine(
278 text,
279 indentSize,
280 (char) => indices.push(char),
281 );
282
283 indentSizeMap.set(line.number, indices.length);
284 decorations.push(
285 ...indices.map(
286 (char, i): IndentationMarkerDesc => ({
287 from: line.from + char,
288 to: line.from + char + 1,
289 lineNumber: line.number,
290 create: (activeIndentIndex) =>
291 activeIndentIndex === i
292 ? activeIndentationMark
293 : indentationMark,
294 }),
295 ),
296 );
297 }
298
299 // Move on to the next line
300 pos = line.to + 1;
301 }
302 });
303
304 const activeBlockRange = getLinesWithActiveIndentMarker(
305 view.state,
306 indentSizeMap,
307 );
308
309 return RangeSet.of<Decoration>(
310 Array.from(decorations).map(({ lineNumber, from, to, create }) => {
311 const activeIndent =
312 lineNumber >= activeBlockRange.start &&
313 lineNumber <= activeBlockRange.end
314 ? activeBlockRange.activeIndent - 1
315 : undefined;
316
317 return { from, to, value: create(activeIndent) };
318 }),
319 true,
320 );
321}
322
323export default function indentationMarkerViewPlugin() {
324 return ViewPlugin.define<PluginValue & { decorations: RangeSet<Decoration> }>(
325 (view) => ({
326 decorations: addIndentationMarkers(view),
327 update(update) {
328 if (
329 update.docChanged ||
330 update.viewportChanged ||
331 update.selectionSet
332 ) {
333 this.decorations = addIndentationMarkers(update.view);
334 }
335 },
336 }),
337 {
338 decorations: (v) => v.decorations,
339 },
340 );
341}
diff --git a/subprojects/frontend/src/editor/scrollbarViewPlugin.ts b/subprojects/frontend/src/editor/scrollbarViewPlugin.ts
deleted file mode 100644
index f44034fd..00000000
--- a/subprojects/frontend/src/editor/scrollbarViewPlugin.ts
+++ /dev/null
@@ -1,357 +0,0 @@
1import { EditorSelection } from '@codemirror/state';
2import {
3 type EditorView,
4 type PluginValue,
5 ViewPlugin,
6} from '@codemirror/view';
7import { reaction } from 'mobx';
8
9import type EditorStore from './EditorStore';
10import { getDiagnostics } from './exposeDiagnostics';
11import findOccurrences from './findOccurrences';
12
13export const HOLDER_CLASS = 'cm-scroller-holder';
14export const SPACER_CLASS = 'cm-scroller-spacer';
15export const TRACK_CLASS = 'cm-scroller-track';
16export const THUMB_CLASS = 'cm-scroller-thumb';
17export const THUMB_ACTIVE_CLASS = 'active';
18export const GUTTER_DECORATION_CLASS = 'cm-scroller-gutter-decoration';
19export const TOP_DECORATION_CLASS = 'cm-scroller-top-decoration';
20export const ANNOTATION_SELECTION_CLASS = 'cm-scroller-selection';
21export const ANNOTATION_DIAGNOSTIC_CLASS = 'cm-scroller-diagnostic';
22export const ANNOTATION_OCCURRENCE_CLASS = 'cm-scroller-occurrence';
23export const SHADOW_WIDTH = 10;
24export const SCROLLBAR_WIDTH = 12;
25export const ANNOTATION_WIDTH = SCROLLBAR_WIDTH / 2;
26export const MIN_ANNOTATION_HEIGHT = 1;
27
28function createScrollbar(
29 holder: HTMLElement,
30 direction: 'x' | 'y',
31 touchCallback: (offsetX: number, offsetY: number) => void,
32 moveCallback: (movementX: number, movementY: number) => void,
33): { track: HTMLElement; thumb: HTMLElement } {
34 const track = holder.ownerDocument.createElement('div');
35 track.className = `${TRACK_CLASS} ${TRACK_CLASS}-${direction}`;
36 holder.appendChild(track);
37
38 const thumb = holder.ownerDocument.createElement('div');
39 thumb.className = `${THUMB_CLASS} ${THUMB_CLASS}-${direction}`;
40 track.appendChild(thumb);
41
42 let pointerId: number | undefined;
43 track.addEventListener('pointerdown', (event) => {
44 if (pointerId !== undefined) {
45 event.preventDefault();
46 return;
47 }
48 ({ pointerId } = event);
49 thumb.classList.add(THUMB_ACTIVE_CLASS);
50 if (event.target === thumb) {
51 // Prevent implicit pointer capture on mobile.
52 thumb.releasePointerCapture(pointerId);
53 } else {
54 touchCallback(event.offsetX, event.offsetY);
55 }
56 track.setPointerCapture(pointerId);
57 });
58
59 track.addEventListener('pointermove', (event) => {
60 if (event.pointerId !== pointerId) {
61 return;
62 }
63 moveCallback(event.movementX, event.movementY);
64 event.preventDefault();
65 });
66
67 function scrollEnd(event: PointerEvent) {
68 if (event.pointerId !== pointerId) {
69 return;
70 }
71 pointerId = undefined;
72 thumb.classList.remove(THUMB_ACTIVE_CLASS);
73 }
74
75 track.addEventListener('pointerup', scrollEnd, { passive: true });
76 track.addEventListener('pointercancel', scrollEnd, { passive: true });
77
78 return { track, thumb };
79}
80
81function rebuildAnnotations(
82 view: EditorView,
83 scrollHeight: number,
84 trackYHeight: number,
85 holder: HTMLElement,
86 annotations: HTMLDivElement[],
87) {
88 const { state } = view;
89 const overlayAnnotationsHeight =
90 (view.contentHeight / scrollHeight) * trackYHeight;
91 const lineHeight = overlayAnnotationsHeight / state.doc.lines;
92
93 let i = 0;
94
95 function getOrCreateAnnotation(from: number, to?: number): HTMLDivElement {
96 const startLine = state.doc.lineAt(from).number;
97 const endLine = to === undefined ? startLine : state.doc.lineAt(to).number;
98 const top = (startLine - 1) * lineHeight;
99 const height = Math.max(
100 MIN_ANNOTATION_HEIGHT,
101 Math.max(1, endLine - startLine) * lineHeight,
102 );
103
104 let annotation: HTMLDivElement | undefined;
105 if (i < annotations.length) {
106 annotation = annotations[i];
107 }
108 if (annotation === undefined) {
109 annotation = holder.ownerDocument.createElement('div');
110 annotations.push(annotation);
111 holder.appendChild(annotation);
112 }
113 i += 1;
114
115 annotation.style.top = `${top}px`;
116 annotation.style.height = `${height}px`;
117
118 return annotation;
119 }
120
121 state.selection.ranges.forEach(({ head }) => {
122 const selectionAnnotation = getOrCreateAnnotation(head);
123 selectionAnnotation.className = ANNOTATION_SELECTION_CLASS;
124 selectionAnnotation.style.width = `${SCROLLBAR_WIDTH}px`;
125 });
126
127 const diagnosticsIter = getDiagnostics(state).iter();
128 while (diagnosticsIter.value !== null) {
129 const diagnosticAnnotation = getOrCreateAnnotation(
130 diagnosticsIter.from,
131 diagnosticsIter.to,
132 );
133 diagnosticAnnotation.className = `${ANNOTATION_DIAGNOSTIC_CLASS} ${ANNOTATION_DIAGNOSTIC_CLASS}-${diagnosticsIter.value.severity}`;
134 diagnosticAnnotation.style.width = `${ANNOTATION_WIDTH}px`;
135 diagnosticsIter.next();
136 }
137
138 const occurrences = view.state.field(findOccurrences);
139 const occurrencesIter = occurrences.iter();
140 while (occurrencesIter.value !== null) {
141 const occurrenceAnnotation = getOrCreateAnnotation(
142 occurrencesIter.from,
143 occurrencesIter.to,
144 );
145 occurrenceAnnotation.className = ANNOTATION_OCCURRENCE_CLASS;
146 occurrenceAnnotation.style.width = `${ANNOTATION_WIDTH}px`;
147 occurrenceAnnotation.style.right = `${ANNOTATION_WIDTH}px`;
148 occurrencesIter.next();
149 }
150
151 annotations
152 .splice(i)
153 .forEach((staleAnnotation) => holder.removeChild(staleAnnotation));
154}
155
156export default function scrollbarViewPlugin(
157 editorStore: EditorStore,
158): ViewPlugin<PluginValue> {
159 return ViewPlugin.define((view) => {
160 const { scrollDOM } = view;
161 const { ownerDocument, parentElement: parentDOM } = scrollDOM;
162 if (parentDOM === null) {
163 return {};
164 }
165
166 const holder = ownerDocument.createElement('div');
167 holder.className = HOLDER_CLASS;
168 parentDOM.replaceChild(holder, scrollDOM);
169 holder.appendChild(scrollDOM);
170
171 const spacer = ownerDocument.createElement('div');
172 spacer.className = SPACER_CLASS;
173 scrollDOM.insertBefore(spacer, scrollDOM.firstChild);
174
175 let gutterWidth = 0;
176
177 scrollDOM.addEventListener('click', (event) => {
178 const scrollX = scrollDOM.scrollLeft + event.offsetX;
179 const scrollY = scrollDOM.scrollTop + event.offsetY;
180 if (scrollX > gutterWidth && scrollY > view.contentHeight) {
181 event.preventDefault();
182 view.focus();
183 editorStore.dispatch({
184 scrollIntoView: true,
185 selection: EditorSelection.create([
186 EditorSelection.cursor(view.state.doc.length),
187 ]),
188 });
189 }
190 });
191
192 let factorY = 1;
193 let factorX = 1;
194
195 const { track: trackY, thumb: thumbY } = createScrollbar(
196 holder,
197 'y',
198 (_offsetX, offsetY) => {
199 const scaledOffset = offsetY / factorY;
200 const { height: scrollerHeight } = scrollDOM.getBoundingClientRect();
201 const target = Math.max(0, scaledOffset - scrollerHeight / 2);
202 scrollDOM.scrollTo({ top: target });
203 },
204 (_movementX, movementY) => {
205 scrollDOM.scrollBy({ top: movementY / factorY });
206 },
207 );
208
209 const { track: trackX, thumb: thumbX } = createScrollbar(
210 holder,
211 'x',
212 (offsetX) => {
213 const scaledOffset = offsetX / factorX;
214 const { width: scrollerWidth } = scrollDOM.getBoundingClientRect();
215 const target = Math.max(0, scaledOffset - scrollerWidth / 2);
216 scrollDOM.scrollTo({ left: target });
217 },
218 (movementX) => {
219 scrollDOM.scrollBy({ left: movementX / factorX });
220 },
221 );
222
223 const gutterDecoration = ownerDocument.createElement('div');
224 gutterDecoration.className = GUTTER_DECORATION_CLASS;
225 holder.appendChild(gutterDecoration);
226
227 const topDecoration = ownerDocument.createElement('div');
228 topDecoration.className = TOP_DECORATION_CLASS;
229 holder.appendChild(topDecoration);
230
231 const disposePanelReaction = reaction(
232 () => editorStore.searchPanel.state,
233 (panelOpen) => {
234 topDecoration.style.display = panelOpen ? 'none' : 'block';
235 },
236 { fireImmediately: true },
237 );
238
239 let gutters: Element | undefined;
240
241 let firstRun = true;
242 let firstRunTimeout: number | undefined;
243 let requested = false;
244 let rebuildRequested = false;
245
246 const annotations: HTMLDivElement[] = [];
247
248 let observer: ResizeObserver | undefined;
249
250 function update() {
251 requested = false;
252
253 if (gutters === undefined) {
254 gutters = scrollDOM.querySelector('.cm-gutters') ?? undefined;
255 if (gutters !== undefined && observer !== undefined) {
256 observer.observe(gutters);
257 }
258 }
259
260 const { height: scrollerHeight, width: scrollerWidth } =
261 scrollDOM.getBoundingClientRect();
262 const { scrollTop, scrollLeft, scrollWidth } = scrollDOM;
263 const scrollHeight =
264 view.contentHeight + scrollerHeight - view.defaultLineHeight;
265 if (firstRun) {
266 if (firstRunTimeout !== undefined) {
267 clearTimeout(firstRunTimeout);
268 }
269 firstRunTimeout = setTimeout(() => {
270 spacer.style.minHeight = `${scrollHeight}px`;
271 firstRun = false;
272 }, 0);
273 } else {
274 spacer.style.minHeight = `${scrollHeight}px`;
275 }
276 gutterWidth = gutters?.clientWidth ?? 0;
277 let trackYHeight = scrollerHeight;
278
279 // Prevent spurious horizontal scrollbar by rounding up to the nearest pixel.
280 if (scrollWidth > Math.ceil(scrollerWidth)) {
281 // Leave space for horizontal scrollbar.
282 trackYHeight -= SCROLLBAR_WIDTH;
283 // Alwalys leave space for annotation in the vertical scrollbar.
284 const trackXWidth = scrollerWidth - gutterWidth - SCROLLBAR_WIDTH;
285 const thumbWidth = trackXWidth * (scrollerWidth / scrollWidth);
286 factorX = (trackXWidth - thumbWidth) / (scrollWidth - scrollerWidth);
287 trackY.style.bottom = `${SCROLLBAR_WIDTH}px`;
288 trackX.style.display = 'block';
289 trackX.style.left = `${gutterWidth}px`;
290 thumbX.style.width = `${thumbWidth}px`;
291 thumbX.style.left = `${scrollLeft * factorX}px`;
292 scrollDOM.style.overflowX = 'scroll';
293 } else {
294 trackY.style.bottom = '0px';
295 trackX.style.display = 'none';
296 scrollDOM.style.overflowX = 'hidden';
297 }
298
299 const thumbHeight = trackYHeight * (scrollerHeight / scrollHeight);
300 factorY = (trackYHeight - thumbHeight) / (scrollHeight - scrollerHeight);
301 thumbY.style.display = 'block';
302 thumbY.style.height = `${thumbHeight}px`;
303 thumbY.style.top = `${scrollTop * factorY}px`;
304
305 gutterDecoration.style.left = `${gutterWidth}px`;
306 gutterDecoration.style.width = `${Math.max(
307 0,
308 Math.min(scrollLeft, SHADOW_WIDTH),
309 )}px`;
310
311 topDecoration.style.height = `${Math.max(
312 0,
313 Math.min(scrollTop, SHADOW_WIDTH),
314 )}px`;
315
316 if (rebuildRequested) {
317 rebuildAnnotations(
318 view,
319 scrollHeight,
320 trackYHeight,
321 holder,
322 annotations,
323 );
324 rebuildRequested = false;
325 }
326 }
327
328 function requestUpdate() {
329 if (!requested) {
330 requested = true;
331 view.requestMeasure({ read: update });
332 }
333 }
334
335 function requestRebuild() {
336 requestUpdate();
337 rebuildRequested = true;
338 }
339
340 observer = new ResizeObserver(requestRebuild);
341 observer.observe(holder);
342
343 scrollDOM.addEventListener('scroll', requestUpdate);
344
345 requestRebuild();
346
347 return {
348 update: requestRebuild,
349 destroy() {
350 disposePanelReaction();
351 observer?.disconnect();
352 scrollDOM.removeEventListener('scroll', requestUpdate);
353 parentDOM.replaceChild(holder, holder);
354 },
355 };
356 });
357}
diff --git a/subprojects/frontend/src/editor/semanticHighlighting.ts b/subprojects/frontend/src/editor/semanticHighlighting.ts
index 2c1bd67d..1f2e564c 100644
--- a/subprojects/frontend/src/editor/semanticHighlighting.ts
+++ b/subprojects/frontend/src/editor/semanticHighlighting.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import { RangeSet, type TransactionSpec } from '@codemirror/state'; 7import { RangeSet, type TransactionSpec } from '@codemirror/state';
2import { Decoration } from '@codemirror/view'; 8import { Decoration } from '@codemirror/view';
3 9
diff --git a/subprojects/frontend/src/index.tsx b/subprojects/frontend/src/index.tsx
index 29b2b196..cb11e6c3 100644
--- a/subprojects/frontend/src/index.tsx
+++ b/subprojects/frontend/src/index.tsx
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import { configure } from 'mobx'; 7import { configure } from 'mobx';
2import { type Root, createRoot } from 'react-dom/client'; 8import { type Root, createRoot } from 'react-dom/client';
3 9
diff --git a/subprojects/frontend/src/language/folding.ts b/subprojects/frontend/src/language/folding.ts
index 4dabfa27..b4d4ca22 100644
--- a/subprojects/frontend/src/language/folding.ts
+++ b/subprojects/frontend/src/language/folding.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import type { EditorState } from '@codemirror/state'; 7import type { EditorState } from '@codemirror/state';
2import type { SyntaxNode } from '@lezer/common'; 8import type { SyntaxNode } from '@lezer/common';
3 9
diff --git a/subprojects/frontend/src/language/indentation.ts b/subprojects/frontend/src/language/indentation.ts
index a0f7032d..8446d7fa 100644
--- a/subprojects/frontend/src/language/indentation.ts
+++ b/subprojects/frontend/src/language/indentation.ts
@@ -1,3 +1,10 @@
1/*
2 * Copyright (C) 2018-2021 by Marijn Haverbeke <marijnh@gmail.com> and others
3 * Copyright (C) 2021-2023 The Refinery Authors <https://refinery.tools/>
4 *
5 * SPDX-License-Identifier: MIT OR EPL-2.0
6 */
7
1import type { TreeIndentContext } from '@codemirror/language'; 8import type { TreeIndentContext } from '@codemirror/language';
2 9
3/** 10/**
diff --git a/subprojects/frontend/src/language/problem.grammar b/subprojects/frontend/src/language/problem.grammar
index 704badab..a7b1fb0a 100644
--- a/subprojects/frontend/src/language/problem.grammar
+++ b/subprojects/frontend/src/language/problem.grammar
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1@detectDelim 7@detectDelim
2 8
3@external prop implicitCompletion from './props' 9@external prop implicitCompletion from './props'
diff --git a/subprojects/frontend/src/language/problemLanguageSupport.ts b/subprojects/frontend/src/language/problemLanguageSupport.ts
index c3ae7ed9..2121e05f 100644
--- a/subprojects/frontend/src/language/problemLanguageSupport.ts
+++ b/subprojects/frontend/src/language/problemLanguageSupport.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import { 7import {
2 foldInside, 8 foldInside,
3 foldNodeProp, 9 foldNodeProp,
diff --git a/subprojects/frontend/src/language/props.ts b/subprojects/frontend/src/language/props.ts
index 65392e75..aa67145a 100644
--- a/subprojects/frontend/src/language/props.ts
+++ b/subprojects/frontend/src/language/props.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1/* eslint-disable import/prefer-default-export -- Lezer needs non-default exports */ 7/* eslint-disable import/prefer-default-export -- Lezer needs non-default exports */
2 8
3import { NodeProp } from '@lezer/common'; 9import { NodeProp } from '@lezer/common';
diff --git a/subprojects/frontend/src/theme/ThemeProvider.tsx b/subprojects/frontend/src/theme/ThemeProvider.tsx
index ff97d524..78146f25 100644
--- a/subprojects/frontend/src/theme/ThemeProvider.tsx
+++ b/subprojects/frontend/src/theme/ThemeProvider.tsx
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import { 7import {
2 alpha, 8 alpha,
3 createTheme, 9 createTheme,
@@ -69,7 +75,7 @@ function createResponsiveTheme(
69 ...options, 75 ...options,
70 typography: { 76 typography: {
71 fontFamily: 77 fontFamily:
72 '"InterVariable", "Inter", "Roboto", "Helvetica", "Arial", sans-serif', 78 '"Inter Variable", "Inter", "Roboto", "Helvetica", "Arial", sans-serif',
73 fontWeightMedium: 600, 79 fontWeightMedium: 600,
74 fontWeightEditorNormal: 400, 80 fontWeightEditorNormal: 400,
75 fontWeightEditorBold: 700, 81 fontWeightEditorBold: 700,
@@ -79,7 +85,7 @@ function createResponsiveTheme(
79 }, 85 },
80 editor: { 86 editor: {
81 fontFamily: 87 fontFamily:
82 '"JetBrains MonoVariable", "JetBrains Mono", "Cascadia Code", "Fira Code", monospace', 88 '"JetBrains Mono Variable", "JetBrains Mono", "Cascadia Code", "Fira Code", monospace',
83 fontFeatureSettings: '"liga", "calt"', 89 fontFeatureSettings: '"liga", "calt"',
84 // `rem` for JetBrains MonoVariable make the text too large in Safari. 90 // `rem` for JetBrains MonoVariable make the text too large in Safari.
85 fontSize: '16px', 91 fontSize: '16px',
diff --git a/subprojects/frontend/src/theme/ThemeStore.ts b/subprojects/frontend/src/theme/ThemeStore.ts
index e09d8d99..7c657449 100644
--- a/subprojects/frontend/src/theme/ThemeStore.ts
+++ b/subprojects/frontend/src/theme/ThemeStore.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import { makeAutoObservable } from 'mobx'; 7import { makeAutoObservable } from 'mobx';
2 8
3export enum ThemePreference { 9export enum ThemePreference {
diff --git a/subprojects/frontend/src/utils/CancelledError.ts b/subprojects/frontend/src/utils/CancelledError.ts
index ee23676f..96b67af7 100644
--- a/subprojects/frontend/src/utils/CancelledError.ts
+++ b/subprojects/frontend/src/utils/CancelledError.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1export default class CancelledError extends Error { 7export default class CancelledError extends Error {
2 constructor(message = 'Operation cancelled') { 8 constructor(message = 'Operation cancelled') {
3 super(message); 9 super(message);
diff --git a/subprojects/frontend/src/utils/PendingTask.ts b/subprojects/frontend/src/utils/PendingTask.ts
index d0b24c1f..80d1a346 100644
--- a/subprojects/frontend/src/utils/PendingTask.ts
+++ b/subprojects/frontend/src/utils/PendingTask.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import TimeoutError from './TimeoutError'; 7import TimeoutError from './TimeoutError';
2import getLogger from './getLogger'; 8import getLogger from './getLogger';
3 9
diff --git a/subprojects/frontend/src/utils/PriorityMutex.ts b/subprojects/frontend/src/utils/PriorityMutex.ts
index 78736141..c1215c76 100644
--- a/subprojects/frontend/src/utils/PriorityMutex.ts
+++ b/subprojects/frontend/src/utils/PriorityMutex.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import CancelledError from './CancelledError'; 7import CancelledError from './CancelledError';
2import PendingTask from './PendingTask'; 8import PendingTask from './PendingTask';
3import getLogger from './getLogger'; 9import getLogger from './getLogger';
diff --git a/subprojects/frontend/src/utils/TimeoutError.ts b/subprojects/frontend/src/utils/TimeoutError.ts
index eb800f40..21365502 100644
--- a/subprojects/frontend/src/utils/TimeoutError.ts
+++ b/subprojects/frontend/src/utils/TimeoutError.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1export default class TimeoutError extends Error { 7export default class TimeoutError extends Error {
2 constructor() { 8 constructor() {
3 super('Operation timed out'); 9 super('Operation timed out');
diff --git a/subprojects/frontend/src/utils/getLogger.ts b/subprojects/frontend/src/utils/getLogger.ts
index 301fd76d..09b0b17f 100644
--- a/subprojects/frontend/src/utils/getLogger.ts
+++ b/subprojects/frontend/src/utils/getLogger.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import styles, { type CSPair } from 'ansi-styles'; 7import styles, { type CSPair } from 'ansi-styles';
2import log from 'loglevel'; 8import log from 'loglevel';
3import prefix from 'loglevel-plugin-prefix'; 9import prefix from 'loglevel-plugin-prefix';
diff --git a/subprojects/frontend/src/utils/useDelayedSnackbar.ts b/subprojects/frontend/src/utils/useDelayedSnackbar.ts
index 03ad6caa..3d6df3e3 100644
--- a/subprojects/frontend/src/utils/useDelayedSnackbar.ts
+++ b/subprojects/frontend/src/utils/useDelayedSnackbar.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import { 7import {
2 useSnackbar, 8 useSnackbar,
3 type SnackbarKey, 9 type SnackbarKey,
diff --git a/subprojects/frontend/src/xtext/BackendConfig.ts b/subprojects/frontend/src/xtext/BackendConfig.ts
index 41737c0b..4c7eac5f 100644
--- a/subprojects/frontend/src/xtext/BackendConfig.ts
+++ b/subprojects/frontend/src/xtext/BackendConfig.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1/* eslint-disable @typescript-eslint/no-redeclare -- Declare types with their companion objects */ 7/* eslint-disable @typescript-eslint/no-redeclare -- Declare types with their companion objects */
2 8
3import { z } from 'zod'; 9import { z } from 'zod';
diff --git a/subprojects/frontend/src/xtext/ContentAssistService.ts b/subprojects/frontend/src/xtext/ContentAssistService.ts
index 78f61c06..fd30c4f9 100644
--- a/subprojects/frontend/src/xtext/ContentAssistService.ts
+++ b/subprojects/frontend/src/xtext/ContentAssistService.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import type { 7import type {
2 Completion, 8 Completion,
3 CompletionContext, 9 CompletionContext,
diff --git a/subprojects/frontend/src/xtext/HighlightingService.ts b/subprojects/frontend/src/xtext/HighlightingService.ts
index a126ee40..447f1401 100644
--- a/subprojects/frontend/src/xtext/HighlightingService.ts
+++ b/subprojects/frontend/src/xtext/HighlightingService.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import type EditorStore from '../editor/EditorStore'; 7import type EditorStore from '../editor/EditorStore';
2import type { IHighlightRange } from '../editor/semanticHighlighting'; 8import type { IHighlightRange } from '../editor/semanticHighlighting';
3 9
diff --git a/subprojects/frontend/src/xtext/OccurrencesService.ts b/subprojects/frontend/src/xtext/OccurrencesService.ts
index fc72ead2..c9c6c699 100644
--- a/subprojects/frontend/src/xtext/OccurrencesService.ts
+++ b/subprojects/frontend/src/xtext/OccurrencesService.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import type { Transaction } from '@codemirror/state'; 7import type { Transaction } from '@codemirror/state';
2import { debounce } from 'lodash-es'; 8import { debounce } from 'lodash-es';
3import ms from 'ms'; 9import ms from 'ms';
diff --git a/subprojects/frontend/src/xtext/UpdateService.ts b/subprojects/frontend/src/xtext/UpdateService.ts
index 63e28652..ee5ebde2 100644
--- a/subprojects/frontend/src/xtext/UpdateService.ts
+++ b/subprojects/frontend/src/xtext/UpdateService.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import type { ChangeDesc, Transaction } from '@codemirror/state'; 7import type { ChangeDesc, Transaction } from '@codemirror/state';
2import { debounce } from 'lodash-es'; 8import { debounce } from 'lodash-es';
3import { nanoid } from 'nanoid'; 9import { nanoid } from 'nanoid';
diff --git a/subprojects/frontend/src/xtext/UpdateStateTracker.ts b/subprojects/frontend/src/xtext/UpdateStateTracker.ts
index 5d4ce49e..4ce93ed6 100644
--- a/subprojects/frontend/src/xtext/UpdateStateTracker.ts
+++ b/subprojects/frontend/src/xtext/UpdateStateTracker.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import { 7import {
2 type ChangeDesc, 8 type ChangeDesc,
3 ChangeSet, 9 ChangeSet,
diff --git a/subprojects/frontend/src/xtext/ValidationService.ts b/subprojects/frontend/src/xtext/ValidationService.ts
index 72414590..64fb63eb 100644
--- a/subprojects/frontend/src/xtext/ValidationService.ts
+++ b/subprojects/frontend/src/xtext/ValidationService.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import type { Diagnostic } from '@codemirror/lint'; 7import type { Diagnostic } from '@codemirror/lint';
2 8
3import type EditorStore from '../editor/EditorStore'; 9import type EditorStore from '../editor/EditorStore';
diff --git a/subprojects/frontend/src/xtext/XtextClient.ts b/subprojects/frontend/src/xtext/XtextClient.ts
index 14fb2430..e8181af0 100644
--- a/subprojects/frontend/src/xtext/XtextClient.ts
+++ b/subprojects/frontend/src/xtext/XtextClient.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import type { 7import type {
2 CompletionContext, 8 CompletionContext,
3 CompletionResult, 9 CompletionResult,
diff --git a/subprojects/frontend/src/xtext/XtextWebSocketClient.ts b/subprojects/frontend/src/xtext/XtextWebSocketClient.ts
index 6b734546..6bb7eec8 100644
--- a/subprojects/frontend/src/xtext/XtextWebSocketClient.ts
+++ b/subprojects/frontend/src/xtext/XtextWebSocketClient.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import { createAtom, makeAutoObservable, observable } from 'mobx'; 7import { createAtom, makeAutoObservable, observable } from 'mobx';
2import ms from 'ms'; 8import ms from 'ms';
3import { nanoid } from 'nanoid'; 9import { nanoid } from 'nanoid';
diff --git a/subprojects/frontend/src/xtext/fetchBackendConfig.ts b/subprojects/frontend/src/xtext/fetchBackendConfig.ts
index 15e976d8..71ff2e63 100644
--- a/subprojects/frontend/src/xtext/fetchBackendConfig.ts
+++ b/subprojects/frontend/src/xtext/fetchBackendConfig.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import BackendConfig, { ENDPOINT } from './BackendConfig'; 7import BackendConfig, { ENDPOINT } from './BackendConfig';
2 8
3export default async function fetchBackendConfig(): Promise<BackendConfig> { 9export default async function fetchBackendConfig(): Promise<BackendConfig> {
diff --git a/subprojects/frontend/src/xtext/webSocketMachine.ts b/subprojects/frontend/src/xtext/webSocketMachine.ts
index fc53fef3..2fb1f52f 100644
--- a/subprojects/frontend/src/xtext/webSocketMachine.ts
+++ b/subprojects/frontend/src/xtext/webSocketMachine.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1import ms from 'ms'; 7import ms from 'ms';
2import { actions, assign, createMachine } from 'xstate'; 8import { actions, assign, createMachine } from 'xstate';
3 9
diff --git a/subprojects/frontend/src/xtext/xtextMessages.ts b/subprojects/frontend/src/xtext/xtextMessages.ts
index ec7a2a31..bbbff064 100644
--- a/subprojects/frontend/src/xtext/xtextMessages.ts
+++ b/subprojects/frontend/src/xtext/xtextMessages.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1/* eslint-disable @typescript-eslint/no-redeclare -- Declare types with their companion objects */ 7/* eslint-disable @typescript-eslint/no-redeclare -- Declare types with their companion objects */
2 8
3import { z } from 'zod'; 9import { z } from 'zod';
diff --git a/subprojects/frontend/src/xtext/xtextServiceResults.ts b/subprojects/frontend/src/xtext/xtextServiceResults.ts
index e93c6714..d3b467ad 100644
--- a/subprojects/frontend/src/xtext/xtextServiceResults.ts
+++ b/subprojects/frontend/src/xtext/xtextServiceResults.ts
@@ -1,3 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
1/* eslint-disable @typescript-eslint/no-redeclare -- Declare types with their companion objects */ 7/* eslint-disable @typescript-eslint/no-redeclare -- Declare types with their companion objects */
2 8
3import { z } from 'zod'; 9import { z } from 'zod';