mirror of https://github.com/astral-sh/ruff
playground: Merge `Editor` state variables (#5831)
<!-- Thank you for contributing to Ruff! To help us out with reviewing, please consider the following: - Does this pull request include a summary of the change? (See below.) - Does this pull request include a descriptive title? - Does this pull request include references to any relevant issues? --> ## Summary This PR removes state variables that can be derived, merges related variables into a single state, and generally avoids `null` states. ## Test Plan I clicked through the playground locally <!-- How was it tested? -->
This commit is contained in:
parent
9ddf40455d
commit
ef58287c16
|
|
@ -4,7 +4,7 @@ In-browser playground for Ruff. Available [https://play.ruff.rs/](https://play.r
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
- To build the WASM module, run `wasm-pack build ../crates/ruff_wasm --target web --out-dir ../../playground/src/pkg`
|
- To build the WASM module, run `npm run build:wasm`
|
||||||
from the `./playground` directory.
|
from the `./playground` directory.
|
||||||
- Install TypeScript dependencies with: `npm install`.
|
- Install TypeScript dependencies with: `npm install`.
|
||||||
- Start the development server with: `npm run dev`.
|
- Start the development server with: `npm run dev`.
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"build:wasm": "wasm-pack build ../crates/ruff_wasm --target web --out-dir ../../playground/src/pkg",
|
||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
"check": "npm run lint && npm run tsc",
|
"check": "npm run lint && npm run tsc",
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { DEFAULT_PYTHON_SOURCE } from "../constants";
|
import { DEFAULT_PYTHON_SOURCE } from "../constants";
|
||||||
import init, {
|
import init, {
|
||||||
check,
|
check,
|
||||||
|
|
@ -16,90 +16,98 @@ import MonacoThemes from "./MonacoThemes";
|
||||||
|
|
||||||
type Tab = "Source" | "Settings";
|
type Tab = "Source" | "Settings";
|
||||||
|
|
||||||
|
interface Source {
|
||||||
|
pythonSource: string;
|
||||||
|
settingsSource: string;
|
||||||
|
revision: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CheckResult {
|
||||||
|
diagnostics: Diagnostic[];
|
||||||
|
error: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
export default function Editor() {
|
export default function Editor() {
|
||||||
const [initialized, setInitialized] = useState<boolean>(false);
|
const [ruffVersion, setRuffVersion] = useState<string | null>(null);
|
||||||
const [version, setVersion] = useState<string | null>(null);
|
const [checkResult, setCheckResult] = useState<CheckResult>({
|
||||||
|
diagnostics: [],
|
||||||
|
error: null,
|
||||||
|
});
|
||||||
|
const [source, setSource] = useState<Source>({
|
||||||
|
pythonSource: "",
|
||||||
|
settingsSource: "",
|
||||||
|
revision: 0,
|
||||||
|
});
|
||||||
|
|
||||||
const [tab, setTab] = useState<Tab>("Source");
|
const [tab, setTab] = useState<Tab>("Source");
|
||||||
const [edit, setEdit] = useState<number>(0);
|
|
||||||
const [settingsSource, setSettingsSource] = useState<string | null>(null);
|
|
||||||
const [pythonSource, setPythonSource] = useState<string | null>(null);
|
|
||||||
const [diagnostics, setDiagnostics] = useState<Diagnostic[]>([]);
|
|
||||||
const [error, setError] = useState<string | null>(null);
|
|
||||||
const [theme, setTheme] = useTheme();
|
const [theme, setTheme] = useTheme();
|
||||||
|
|
||||||
|
const initialized = ruffVersion != null;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
init().then(() => setInitialized(true));
|
init().then(() => {
|
||||||
|
setRuffVersion(currentVersion());
|
||||||
|
|
||||||
|
const [settingsSource, pythonSource] = restore() ?? [
|
||||||
|
stringify(defaultSettings()),
|
||||||
|
DEFAULT_PYTHON_SOURCE,
|
||||||
|
];
|
||||||
|
|
||||||
|
setSource({
|
||||||
|
pythonSource,
|
||||||
|
revision: 0,
|
||||||
|
settingsSource,
|
||||||
|
});
|
||||||
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!initialized || settingsSource == null || pythonSource == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let config: any;
|
|
||||||
let diagnostics: Diagnostic[];
|
|
||||||
|
|
||||||
try {
|
|
||||||
config = JSON.parse(settingsSource);
|
|
||||||
} catch (e) {
|
|
||||||
setDiagnostics([]);
|
|
||||||
setError((e as Error).message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
diagnostics = check(pythonSource, config);
|
|
||||||
} catch (e) {
|
|
||||||
setError(e as string);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setError(null);
|
|
||||||
setDiagnostics(diagnostics);
|
|
||||||
}, [initialized, settingsSource, pythonSource]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!initialized) {
|
if (!initialized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settingsSource == null || pythonSource == null) {
|
const { settingsSource, pythonSource } = source;
|
||||||
const payload = restore();
|
|
||||||
if (payload) {
|
|
||||||
const [settingsSource, pythonSource] = payload;
|
|
||||||
setSettingsSource(settingsSource);
|
|
||||||
setPythonSource(pythonSource);
|
|
||||||
} else {
|
|
||||||
setSettingsSource(stringify(defaultSettings()));
|
|
||||||
setPythonSource(DEFAULT_PYTHON_SOURCE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [initialized, settingsSource, pythonSource]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
try {
|
||||||
|
const config = JSON.parse(settingsSource);
|
||||||
|
const diagnostics = check(pythonSource, config);
|
||||||
|
|
||||||
|
setCheckResult({
|
||||||
|
diagnostics,
|
||||||
|
error: null,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
setCheckResult({
|
||||||
|
diagnostics: [],
|
||||||
|
error: (e as Error).message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [initialized, source]);
|
||||||
|
|
||||||
|
const handleShare = useMemo(() => {
|
||||||
if (!initialized) {
|
if (!initialized) {
|
||||||
return;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
setVersion(currentVersion());
|
return () => {
|
||||||
}, [initialized]);
|
persist(source.settingsSource, source.pythonSource);
|
||||||
|
};
|
||||||
const handleShare = useCallback(() => {
|
}, [source, initialized]);
|
||||||
if (!initialized || settingsSource == null || pythonSource == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
persist(settingsSource, pythonSource);
|
|
||||||
}, [initialized, settingsSource, pythonSource]);
|
|
||||||
|
|
||||||
const handlePythonSourceChange = useCallback((pythonSource: string) => {
|
const handlePythonSourceChange = useCallback((pythonSource: string) => {
|
||||||
setEdit((edit) => edit + 1);
|
setSource((state) => ({
|
||||||
setPythonSource(pythonSource);
|
...state,
|
||||||
|
pythonSource,
|
||||||
|
revision: state.revision + 1,
|
||||||
|
}));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleSettingsSourceChange = useCallback((settingsSource: string) => {
|
const handleSettingsSourceChange = useCallback((settingsSource: string) => {
|
||||||
setEdit((edit) => edit + 1);
|
setSource((state) => ({
|
||||||
setSettingsSource(settingsSource);
|
...state,
|
||||||
|
settingsSource,
|
||||||
|
revision: state.revision + 1,
|
||||||
|
}));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -109,37 +117,37 @@ export default function Editor() {
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Header
|
<Header
|
||||||
edit={edit}
|
edit={source.revision}
|
||||||
tab={tab}
|
tab={tab}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
version={version}
|
version={ruffVersion}
|
||||||
onChangeTab={setTab}
|
onChangeTab={setTab}
|
||||||
onChangeTheme={setTheme}
|
onChangeTheme={setTheme}
|
||||||
onShare={initialized ? handleShare : undefined}
|
onShare={handleShare}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<MonacoThemes />
|
<MonacoThemes />
|
||||||
|
|
||||||
<div className={"mt-12 relative flex-auto"}>
|
<div className={"mt-12 relative flex-auto"}>
|
||||||
{initialized && settingsSource != null && pythonSource != null ? (
|
{initialized ? (
|
||||||
<>
|
<>
|
||||||
<SourceEditor
|
<SourceEditor
|
||||||
visible={tab === "Source"}
|
visible={tab === "Source"}
|
||||||
source={pythonSource}
|
source={source.pythonSource}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
diagnostics={diagnostics}
|
diagnostics={checkResult.diagnostics}
|
||||||
onChange={handlePythonSourceChange}
|
onChange={handlePythonSourceChange}
|
||||||
/>
|
/>
|
||||||
<SettingsEditor
|
<SettingsEditor
|
||||||
visible={tab === "Settings"}
|
visible={tab === "Settings"}
|
||||||
source={settingsSource}
|
source={source.settingsSource}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
onChange={handleSettingsSourceChange}
|
onChange={handleSettingsSourceChange}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
{error && tab === "Source" ? (
|
{checkResult.error && tab === "Source" ? (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
position: "fixed",
|
position: "fixed",
|
||||||
|
|
@ -148,7 +156,7 @@ export default function Editor() {
|
||||||
bottom: "10%",
|
bottom: "10%",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ErrorMessage>{error}</ErrorMessage>
|
<ErrorMessage>{checkResult.error}</ErrorMessage>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</main>
|
</main>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue