diff --git a/playground/knot/src/Editor/Chrome.tsx b/playground/knot/src/Editor/Chrome.tsx index 6f4420c05c..2884762d30 100644 --- a/playground/knot/src/Editor/Chrome.tsx +++ b/playground/knot/src/Editor/Chrome.tsx @@ -13,7 +13,7 @@ import { Theme, VerticalResizeHandle, } from "shared"; -import type { Diagnostic, Workspace } from "red_knot_wasm"; +import type { Workspace } from "red_knot_wasm"; import { Panel, PanelGroup } from "react-resizable-panels"; import { Files, isPythonFile } from "./Files"; import SecondarySideBar from "./SecondarySideBar"; @@ -21,7 +21,7 @@ import SecondaryPanel, { SecondaryPanelResult, SecondaryTool, } from "./SecondaryPanel"; -import Diagnostics from "./Diagnostics"; +import Diagnostics, { Diagnostic } from "./Diagnostics"; import { FileId, ReadonlyFiles } from "../Playground"; import type { editor } from "monaco-editor"; import type { Monaco } from "@monaco-editor/react"; @@ -168,7 +168,7 @@ export default function Chrome({ workspace={workspace} onMount={handleEditorMount} onChange={(content) => onFileChanged(workspace, content)} - onOpenFile={onFileSelected} + onFileOpened={onFileSelected} /> {checkResult.error ? (
@@ -290,8 +289,18 @@ function useCheckResult( }; } + // Eagerly convert the diagnostic to avoid out of bound errors + // when the diagnostics are "deferred". + const serializedDiagnostics = diagnostics.map((diagnostic) => ({ + id: diagnostic.id(), + message: diagnostic.message(), + severity: diagnostic.severity(), + range: diagnostic.toRange(workspace) ?? null, + textRange: diagnostic.textRange() ?? null, + })); + return { - diagnostics, + diagnostics: serializedDiagnostics, error: null, secondary, }; diff --git a/playground/knot/src/Editor/Diagnostics.tsx b/playground/knot/src/Editor/Diagnostics.tsx index e9ae8c21ba..2a087c33ed 100644 --- a/playground/knot/src/Editor/Diagnostics.tsx +++ b/playground/knot/src/Editor/Diagnostics.tsx @@ -1,11 +1,10 @@ -import { Diagnostic, Workspace } from "red_knot_wasm"; +import type { Severity, Range, TextRange } from "red_knot_wasm"; import classNames from "classnames"; import { Theme } from "shared"; import { useMemo } from "react"; interface Props { diagnostics: Diagnostic[]; - workspace: Workspace; theme: Theme; onGoTo(line: number, column: number): void; @@ -13,14 +12,13 @@ interface Props { export default function Diagnostics({ diagnostics: unsorted, - workspace, theme, onGoTo, }: Props) { const diagnostics = useMemo(() => { const sorted = [...unsorted]; sorted.sort((a, b) => { - return (a.textRange()?.start ?? 0) - (b.textRange()?.start ?? 0); + return (a.textRange?.start ?? 0) - (b.textRange?.start ?? 0); }); return sorted; @@ -43,11 +41,7 @@ export default function Diagnostics({
- +
); @@ -56,10 +50,8 @@ export default function Diagnostics({ function Items({ diagnostics, onGoTo, - workspace, }: { diagnostics: Array; - workspace: Workspace; onGoTo(line: number, column: number): void; }) { if (diagnostics.length === 0) { @@ -73,9 +65,9 @@ function Items({ return ( ); } + +export interface Diagnostic { + id: string; + message: string; + severity: Severity; + range: Range | null; + textRange: TextRange | null; +} diff --git a/playground/knot/src/Editor/Editor.tsx b/playground/knot/src/Editor/Editor.tsx index c6dced6631..e03da98a00 100644 --- a/playground/knot/src/Editor/Editor.tsx +++ b/playground/knot/src/Editor/Editor.tsx @@ -17,9 +17,8 @@ import { import { RefObject, useCallback, useEffect, useRef } from "react"; import { Theme } from "shared"; import { - Diagnostic, Severity, - Workspace, + type Workspace, Position as KnotPosition, type Range as KnotRange, } from "red_knot_wasm"; @@ -27,6 +26,7 @@ import { import IStandaloneCodeEditor = editor.IStandaloneCodeEditor; import { FileId, ReadonlyFiles } from "../Playground"; import { isPythonFile } from "./Files"; +import { Diagnostic } from "./Diagnostics"; type Props = { visible: boolean; @@ -38,7 +38,7 @@ type Props = { workspace: Workspace; onChange(content: string): void; onMount(editor: IStandaloneCodeEditor, monaco: Monaco): void; - onOpenFile(file: FileId): void; + onFileOpened(file: FileId): void; }; export default function Editor({ @@ -51,7 +51,7 @@ export default function Editor({ workspace, onChange, onMount, - onOpenFile, + onFileOpened, }: Props) { const disposable = useRef<{ typeDefinition: IDisposable; @@ -61,14 +61,14 @@ export default function Editor({ monaco: null, files, workspace, - onOpenFile, + onFileOpened, }); playgroundState.current = { monaco: playgroundState.current.monaco, files, workspace, - onOpenFile, + onFileOpened, }; // Update the diagnostics in the editor. @@ -79,8 +79,8 @@ export default function Editor({ return; } - updateMarkers(monaco, workspace, diagnostics); - }, [workspace, diagnostics]); + updateMarkers(monaco, diagnostics); + }, [diagnostics]); const handleChange = useCallback( (value: string | undefined) => { @@ -98,7 +98,7 @@ export default function Editor({ const handleMount: OnMount = useCallback( (editor, instance) => { - updateMarkers(instance, workspace, diagnostics); + updateMarkers(instance, diagnostics); const server = new PlaygroundServer(playgroundState); const typeDefinitionDisposable = @@ -116,7 +116,7 @@ export default function Editor({ onMount(editor, instance); }, - [onMount, workspace, diagnostics], + [onMount, diagnostics], ); return ( @@ -141,11 +141,7 @@ export default function Editor({ ); } -function updateMarkers( - monaco: Monaco, - workspace: Workspace, - diagnostics: Array, -) { +function updateMarkers(monaco: Monaco, diagnostics: Array) { const editor = monaco.editor; const model = editor?.getModels()[0]; @@ -170,16 +166,16 @@ function updateMarkers( } }; - const range = diagnostic.toRange(workspace); + const range = diagnostic.range; return { - code: diagnostic.id(), + code: diagnostic.id, startLineNumber: range?.start?.line ?? 0, startColumn: range?.start?.column ?? 0, endLineNumber: range?.end?.line ?? 0, endColumn: range?.end?.column ?? 0, - message: diagnostic.message(), - severity: mapSeverity(diagnostic.severity()), + message: diagnostic.message, + severity: mapSeverity(diagnostic.severity), tags: [], }; }), @@ -191,7 +187,7 @@ interface PlaygroundServerProps { workspace: Workspace; files: ReadonlyFiles; - onOpenFile: (file: FileId) => void; + onFileOpened: (file: FileId) => void; } class PlaygroundServer @@ -223,26 +219,29 @@ class PlaygroundServer new KnotPosition(position.lineNumber, position.column), ); - const locations = links.map((link) => { - const targetSelection = - link.selection_range == null - ? undefined - : knotRangeToIRange(link.selection_range); + return ( + links + .map((link) => { + const targetSelection = + link.selection_range == null + ? undefined + : knotRangeToIRange(link.selection_range); - const originSelection = - link.origin_selection_range == null - ? undefined - : knotRangeToIRange(link.origin_selection_range); + const originSelection = + link.origin_selection_range == null + ? undefined + : knotRangeToIRange(link.origin_selection_range); - return { - uri: Uri.parse(link.path), - range: knotRangeToIRange(link.full_range), - targetSelectionRange: targetSelection, - originSelectionRange: originSelection, - } as languages.LocationLink; - }); - - return locations; + return { + uri: Uri.parse(link.path), + range: knotRangeToIRange(link.full_range), + targetSelectionRange: targetSelection, + originSelectionRange: originSelection, + } as languages.LocationLink; + }) + // Filter out vendored files because they aren't open in the editor. + .filter((link) => link.uri.scheme !== "vendored") + ); } openCodeEditor( @@ -284,7 +283,7 @@ class PlaygroundServer if (files.selected !== fileId) { source.setModel(model); - this.props.current.onOpenFile(fileId); + this.props.current.onFileOpened(fileId); } if (selectionOrPosition != null) {