mirror of https://github.com/astral-sh/ruff
[red-knot] Simplify playground editor state (#17223)
## Summary Reduce the number of `useRef`s in `Editor`
This commit is contained in:
parent
1c652e6b98
commit
4571c5f56f
|
|
@ -14,7 +14,7 @@ import {
|
||||||
Position,
|
Position,
|
||||||
Uri,
|
Uri,
|
||||||
} from "monaco-editor";
|
} from "monaco-editor";
|
||||||
import { RefObject, useCallback, useEffect, useRef } from "react";
|
import { useCallback, useEffect, useRef } from "react";
|
||||||
import { Theme } from "shared";
|
import { Theme } from "shared";
|
||||||
import {
|
import {
|
||||||
Severity,
|
Severity,
|
||||||
|
|
@ -53,34 +53,25 @@ export default function Editor({
|
||||||
onMount,
|
onMount,
|
||||||
onFileOpened,
|
onFileOpened,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const disposable = useRef<{
|
const serverRef = useRef<PlaygroundServer | null>(null);
|
||||||
typeDefinition: IDisposable;
|
|
||||||
editorOpener: IDisposable;
|
|
||||||
hover: IDisposable;
|
|
||||||
} | null>(null);
|
|
||||||
const playgroundState = useRef<PlaygroundServerProps>({
|
|
||||||
monaco: null,
|
|
||||||
files,
|
|
||||||
workspace,
|
|
||||||
onFileOpened,
|
|
||||||
});
|
|
||||||
|
|
||||||
playgroundState.current = {
|
if (serverRef.current != null) {
|
||||||
monaco: playgroundState.current.monaco,
|
serverRef.current.update({
|
||||||
files,
|
files,
|
||||||
workspace,
|
workspace,
|
||||||
onFileOpened,
|
onFileOpened,
|
||||||
};
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Update the diagnostics in the editor.
|
// Update the diagnostics in the editor.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const monaco = playgroundState.current.monaco;
|
const server = serverRef.current;
|
||||||
|
|
||||||
if (monaco == null) {
|
if (server == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateMarkers(monaco, diagnostics);
|
server.updateDiagnostics(diagnostics);
|
||||||
}, [diagnostics]);
|
}, [diagnostics]);
|
||||||
|
|
||||||
const handleChange = useCallback(
|
const handleChange = useCallback(
|
||||||
|
|
@ -92,38 +83,29 @@ export default function Editor({
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
disposable.current?.typeDefinition.dispose();
|
const server = serverRef.current;
|
||||||
disposable.current?.editorOpener.dispose();
|
|
||||||
disposable.current?.hover.dispose();
|
if (server != null) {
|
||||||
|
server.dispose();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleMount: OnMount = useCallback(
|
const handleMount: OnMount = useCallback(
|
||||||
(editor, instance) => {
|
(editor, instance) => {
|
||||||
updateMarkers(instance, diagnostics);
|
const server = new PlaygroundServer(instance, {
|
||||||
|
workspace,
|
||||||
|
files,
|
||||||
|
onFileOpened,
|
||||||
|
});
|
||||||
|
|
||||||
const server = new PlaygroundServer(playgroundState);
|
server.updateDiagnostics(diagnostics);
|
||||||
const typeDefinitionDisposable =
|
serverRef.current = server;
|
||||||
instance.languages.registerTypeDefinitionProvider("python", server);
|
|
||||||
const hoverDisposable = instance.languages.registerHoverProvider(
|
|
||||||
"python",
|
|
||||||
server,
|
|
||||||
);
|
|
||||||
const editorOpenerDisposable =
|
|
||||||
instance.editor.registerEditorOpener(server);
|
|
||||||
|
|
||||||
disposable.current = {
|
|
||||||
typeDefinition: typeDefinitionDisposable,
|
|
||||||
editorOpener: editorOpenerDisposable,
|
|
||||||
hover: hoverDisposable,
|
|
||||||
};
|
|
||||||
|
|
||||||
playgroundState.current.monaco = instance;
|
|
||||||
|
|
||||||
onMount(editor, instance);
|
onMount(editor, instance);
|
||||||
},
|
},
|
||||||
|
|
||||||
[onMount, diagnostics],
|
[files, onFileOpened, workspace, onMount, diagnostics],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -148,52 +130,9 @@ export default function Editor({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateMarkers(monaco: Monaco, diagnostics: Array<Diagnostic>) {
|
|
||||||
const editor = monaco.editor;
|
|
||||||
const model = editor?.getModels()[0];
|
|
||||||
|
|
||||||
if (!model) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
editor.setModelMarkers(
|
|
||||||
model,
|
|
||||||
"owner",
|
|
||||||
diagnostics.map((diagnostic) => {
|
|
||||||
const mapSeverity = (severity: Severity) => {
|
|
||||||
switch (severity) {
|
|
||||||
case Severity.Info:
|
|
||||||
return MarkerSeverity.Info;
|
|
||||||
case Severity.Warning:
|
|
||||||
return MarkerSeverity.Warning;
|
|
||||||
case Severity.Error:
|
|
||||||
return MarkerSeverity.Error;
|
|
||||||
case Severity.Fatal:
|
|
||||||
return MarkerSeverity.Error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const range = diagnostic.range;
|
|
||||||
|
|
||||||
return {
|
|
||||||
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),
|
|
||||||
tags: [],
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface PlaygroundServerProps {
|
interface PlaygroundServerProps {
|
||||||
monaco: Monaco | null;
|
|
||||||
workspace: Workspace;
|
workspace: Workspace;
|
||||||
files: ReadonlyFiles;
|
files: ReadonlyFiles;
|
||||||
|
|
||||||
onFileOpened: (file: FileId) => void;
|
onFileOpened: (file: FileId) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -203,7 +142,77 @@ class PlaygroundServer
|
||||||
editor.ICodeEditorOpener,
|
editor.ICodeEditorOpener,
|
||||||
languages.HoverProvider
|
languages.HoverProvider
|
||||||
{
|
{
|
||||||
constructor(private props: RefObject<PlaygroundServerProps>) {}
|
private typeDefinitionProviderDisposable: IDisposable;
|
||||||
|
private editorOpenerDisposable: IDisposable;
|
||||||
|
private hoverDisposable: IDisposable;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private monaco: Monaco,
|
||||||
|
private props: PlaygroundServerProps,
|
||||||
|
) {
|
||||||
|
this.typeDefinitionProviderDisposable =
|
||||||
|
monaco.languages.registerTypeDefinitionProvider("python", this);
|
||||||
|
this.hoverDisposable = monaco.languages.registerHoverProvider(
|
||||||
|
"python",
|
||||||
|
this,
|
||||||
|
);
|
||||||
|
this.editorOpenerDisposable = monaco.editor.registerEditorOpener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(props: PlaygroundServerProps) {
|
||||||
|
this.props = props;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDiagnostics(diagnostics: Array<Diagnostic>) {
|
||||||
|
if (this.props.files.selected == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handle = this.props.files.handles[this.props.files.selected];
|
||||||
|
|
||||||
|
if (handle == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const editor = this.monaco.editor;
|
||||||
|
const model = editor.getModel(Uri.parse(handle.path()));
|
||||||
|
|
||||||
|
if (model == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.setModelMarkers(
|
||||||
|
model,
|
||||||
|
"owner",
|
||||||
|
diagnostics.map((diagnostic) => {
|
||||||
|
const mapSeverity = (severity: Severity) => {
|
||||||
|
switch (severity) {
|
||||||
|
case Severity.Info:
|
||||||
|
return MarkerSeverity.Info;
|
||||||
|
case Severity.Warning:
|
||||||
|
return MarkerSeverity.Warning;
|
||||||
|
case Severity.Error:
|
||||||
|
return MarkerSeverity.Error;
|
||||||
|
case Severity.Fatal:
|
||||||
|
return MarkerSeverity.Error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const range = diagnostic.range;
|
||||||
|
|
||||||
|
return {
|
||||||
|
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),
|
||||||
|
tags: [],
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
provideHover(
|
provideHover(
|
||||||
model: editor.ITextModel,
|
model: editor.ITextModel,
|
||||||
|
|
@ -213,14 +222,14 @@ class PlaygroundServer
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
context?: languages.HoverContext<languages.Hover> | undefined,
|
context?: languages.HoverContext<languages.Hover> | undefined,
|
||||||
): languages.ProviderResult<languages.Hover> {
|
): languages.ProviderResult<languages.Hover> {
|
||||||
const workspace = this.props.current.workspace;
|
const workspace = this.props.workspace;
|
||||||
|
|
||||||
const selectedFile = this.props.current.files.selected;
|
const selectedFile = this.props.files.selected;
|
||||||
if (selectedFile == null) {
|
if (selectedFile == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedHandle = this.props.current.files.handles[selectedFile];
|
const selectedHandle = this.props.files.handles[selectedFile];
|
||||||
|
|
||||||
if (selectedHandle == null) {
|
if (selectedHandle == null) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -247,14 +256,14 @@ class PlaygroundServer
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
_: CancellationToken,
|
_: CancellationToken,
|
||||||
): languages.ProviderResult<languages.Definition | languages.LocationLink[]> {
|
): languages.ProviderResult<languages.Definition | languages.LocationLink[]> {
|
||||||
const workspace = this.props.current.workspace;
|
const workspace = this.props.workspace;
|
||||||
|
|
||||||
const selectedFile = this.props.current.files.selected;
|
const selectedFile = this.props.files.selected;
|
||||||
if (selectedFile == null) {
|
if (selectedFile == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedHandle = this.props.current.files.handles[selectedFile];
|
const selectedHandle = this.props.files.handles[selectedFile];
|
||||||
|
|
||||||
if (selectedHandle == null) {
|
if (selectedHandle == null) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -295,12 +304,7 @@ class PlaygroundServer
|
||||||
resource: Uri,
|
resource: Uri,
|
||||||
selectionOrPosition?: IRange | IPosition,
|
selectionOrPosition?: IRange | IPosition,
|
||||||
): boolean {
|
): boolean {
|
||||||
const files = this.props.current.files;
|
const files = this.props.files;
|
||||||
const monaco = this.props.current.monaco;
|
|
||||||
|
|
||||||
if (monaco == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileId = files.index.find((file) => {
|
const fileId = files.index.find((file) => {
|
||||||
return Uri.file(file.name).toString() === resource.toString();
|
return Uri.file(file.name).toString() === resource.toString();
|
||||||
|
|
@ -312,11 +316,11 @@ class PlaygroundServer
|
||||||
|
|
||||||
const handle = files.handles[fileId];
|
const handle = files.handles[fileId];
|
||||||
|
|
||||||
let model = monaco.editor.getModel(resource);
|
let model = this.monaco.editor.getModel(resource);
|
||||||
if (model == null) {
|
if (model == null) {
|
||||||
const language =
|
const language =
|
||||||
handle != null && isPythonFile(handle) ? "python" : undefined;
|
handle != null && isPythonFile(handle) ? "python" : undefined;
|
||||||
model = monaco.editor.createModel(
|
model = this.monaco.editor.createModel(
|
||||||
files.contents[fileId],
|
files.contents[fileId],
|
||||||
language,
|
language,
|
||||||
resource,
|
resource,
|
||||||
|
|
@ -329,7 +333,7 @@ class PlaygroundServer
|
||||||
if (files.selected !== fileId) {
|
if (files.selected !== fileId) {
|
||||||
source.setModel(model);
|
source.setModel(model);
|
||||||
|
|
||||||
this.props.current.onFileOpened(fileId);
|
this.props.onFileOpened(fileId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectionOrPosition != null) {
|
if (selectionOrPosition != null) {
|
||||||
|
|
@ -347,6 +351,12 @@ class PlaygroundServer
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
this.hoverDisposable.dispose();
|
||||||
|
this.editorOpenerDisposable.dispose();
|
||||||
|
this.typeDefinitionProviderDisposable.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function knotRangeToIRange(range: KnotRange): IRange {
|
function knotRangeToIRange(range: KnotRange): IRange {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue