mirror of https://github.com/astral-sh/ruff
playground: Persist source and panel (#6071)
This commit is contained in:
parent
c8ee357613
commit
1fdadee59c
|
|
@ -10,7 +10,7 @@ import init, { Diagnostic, Workspace } from "../pkg";
|
||||||
import { ErrorMessage } from "./ErrorMessage";
|
import { ErrorMessage } from "./ErrorMessage";
|
||||||
import Header from "./Header";
|
import Header from "./Header";
|
||||||
import { useTheme } from "./theme";
|
import { useTheme } from "./theme";
|
||||||
import { persist, restore, stringify } from "./settings";
|
import { persist, persistLocal, restore, stringify } from "./settings";
|
||||||
import SettingsEditor from "./SettingsEditor";
|
import SettingsEditor from "./SettingsEditor";
|
||||||
import SourceEditor from "./SourceEditor";
|
import SourceEditor from "./SourceEditor";
|
||||||
import { Panel, PanelGroup } from "react-resizable-panels";
|
import { Panel, PanelGroup } from "react-resizable-panels";
|
||||||
|
|
@ -50,17 +50,45 @@ export default function Editor() {
|
||||||
});
|
});
|
||||||
|
|
||||||
const [tab, setTab] = useState<Tab>("Source");
|
const [tab, setTab] = useState<Tab>("Source");
|
||||||
const [theme, setTheme] = useTheme();
|
|
||||||
const [secondaryTool, setSecondaryTool] = useState<SecondaryTool | null>(
|
const [secondaryTool, setSecondaryTool] = useState<SecondaryTool | null>(
|
||||||
null,
|
() => {
|
||||||
|
const secondaryValue = new URLSearchParams(location.search).get(
|
||||||
|
"secondary",
|
||||||
|
);
|
||||||
|
if (secondaryValue == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return parseSecondaryTool(secondaryValue);
|
||||||
|
}
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
const [theme, setTheme] = useTheme();
|
||||||
|
|
||||||
const initialized = ruffVersion != null;
|
const initialized = ruffVersion != null;
|
||||||
|
|
||||||
|
// Ideally this would be retrieved right from the URl... but routing without a proper
|
||||||
|
// router is hard (there's no location changed event) and pulling in a router
|
||||||
|
// feels overkill.
|
||||||
|
const handleSecondaryToolSelected = (tool: SecondaryTool | null) => {
|
||||||
|
if (tool === secondaryTool) {
|
||||||
|
tool = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = new URL(location.href);
|
||||||
|
|
||||||
|
if (tool == null) {
|
||||||
|
url.searchParams.delete("secondary");
|
||||||
|
} else {
|
||||||
|
url.searchParams.set("secondary", tool);
|
||||||
|
}
|
||||||
|
|
||||||
|
history.replaceState(null, "", url);
|
||||||
|
|
||||||
|
setSecondaryTool(tool);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
init().then(() => {
|
init().then(() => {
|
||||||
setRuffVersion(Workspace.version());
|
|
||||||
|
|
||||||
const [settingsSource, pythonSource] = restore() ?? [
|
const [settingsSource, pythonSource] = restore() ?? [
|
||||||
stringify(Workspace.defaultSettings()),
|
stringify(Workspace.defaultSettings()),
|
||||||
DEFAULT_PYTHON_SOURCE,
|
DEFAULT_PYTHON_SOURCE,
|
||||||
|
|
@ -71,6 +99,7 @@ export default function Editor() {
|
||||||
revision: 0,
|
revision: 0,
|
||||||
settingsSource,
|
settingsSource,
|
||||||
});
|
});
|
||||||
|
setRuffVersion(Workspace.version());
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
@ -141,6 +170,12 @@ export default function Editor() {
|
||||||
}
|
}
|
||||||
}, [initialized, deferredSource, secondaryTool]);
|
}, [initialized, deferredSource, secondaryTool]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (initialized) {
|
||||||
|
persistLocal(source);
|
||||||
|
}
|
||||||
|
}, [initialized, source]);
|
||||||
|
|
||||||
const handleShare = useMemo(() => {
|
const handleShare = useMemo(() => {
|
||||||
if (!initialized) {
|
if (!initialized) {
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|
@ -215,13 +250,7 @@ export default function Editor() {
|
||||||
)}
|
)}
|
||||||
<SecondarySideBar
|
<SecondarySideBar
|
||||||
selected={secondaryTool}
|
selected={secondaryTool}
|
||||||
onSelected={(tool) => {
|
onSelected={handleSecondaryToolSelected}
|
||||||
if (secondaryTool === tool) {
|
|
||||||
setSecondaryTool(null);
|
|
||||||
} else {
|
|
||||||
setSecondaryTool(tool);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</PanelGroup>
|
</PanelGroup>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
@ -241,3 +270,11 @@ export default function Editor() {
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseSecondaryTool(tool: string): SecondaryTool | null {
|
||||||
|
if (Object.hasOwn(SecondaryTool, tool)) {
|
||||||
|
return tool as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,12 @@
|
||||||
import Editor from "@monaco-editor/react";
|
import Editor from "@monaco-editor/react";
|
||||||
import { Theme } from "./theme";
|
import { Theme } from "./theme";
|
||||||
|
|
||||||
export type SecondaryTool = "Format" | "AST" | "Tokens" | "FIR";
|
export enum SecondaryTool {
|
||||||
|
"Format" = "Format",
|
||||||
|
"AST" = "AST",
|
||||||
|
"Tokens" = "Tokens",
|
||||||
|
"FIR" = "FIR",
|
||||||
|
}
|
||||||
|
|
||||||
export type SecondaryPanelResult =
|
export type SecondaryPanelResult =
|
||||||
| null
|
| null
|
||||||
|
|
|
||||||
|
|
@ -15,32 +15,32 @@ export default function SecondarySideBar({
|
||||||
<SideBar position="right">
|
<SideBar position="right">
|
||||||
<SideBarEntry
|
<SideBarEntry
|
||||||
title="Format (alpha)"
|
title="Format (alpha)"
|
||||||
selected={selected === "Format"}
|
selected={selected === SecondaryTool.Format}
|
||||||
onClick={() => onSelected("Format")}
|
onClick={() => onSelected(SecondaryTool.Format)}
|
||||||
>
|
>
|
||||||
<FormatIcon />
|
<FormatIcon />
|
||||||
</SideBarEntry>
|
</SideBarEntry>
|
||||||
|
|
||||||
<SideBarEntry
|
<SideBarEntry
|
||||||
title="AST"
|
title="AST"
|
||||||
selected={selected === "AST"}
|
selected={selected === SecondaryTool.AST}
|
||||||
onClick={() => onSelected("AST")}
|
onClick={() => onSelected(SecondaryTool.AST)}
|
||||||
>
|
>
|
||||||
<StructureIcon />
|
<StructureIcon />
|
||||||
</SideBarEntry>
|
</SideBarEntry>
|
||||||
|
|
||||||
<SideBarEntry
|
<SideBarEntry
|
||||||
title="Tokens"
|
title="Tokens"
|
||||||
selected={selected === "Tokens"}
|
selected={selected === SecondaryTool.Tokens}
|
||||||
onClick={() => onSelected("Tokens")}
|
onClick={() => onSelected(SecondaryTool.Tokens)}
|
||||||
>
|
>
|
||||||
<TokensIcon />
|
<TokensIcon />
|
||||||
</SideBarEntry>
|
</SideBarEntry>
|
||||||
|
|
||||||
<SideBarEntry
|
<SideBarEntry
|
||||||
title="Formatter IR"
|
title="Formatter IR"
|
||||||
selected={selected === "FIR"}
|
selected={selected === SecondaryTool.FIR}
|
||||||
onClick={() => onSelected("FIR")}
|
onClick={() => onSelected(SecondaryTool.FIR)}
|
||||||
>
|
>
|
||||||
FIR
|
FIR
|
||||||
</SideBarEntry>
|
</SideBarEntry>
|
||||||
|
|
|
||||||
|
|
@ -39,10 +39,33 @@ export function restore(): [string, string] | null {
|
||||||
window.location.hash.slice(1),
|
window.location.hash.slice(1),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (value != null) {
|
if (value == null) {
|
||||||
|
return restoreLocal();
|
||||||
|
} else {
|
||||||
const [settingsSource, pythonSource] = value.split("$$$");
|
const [settingsSource, pythonSource] = value.split("$$$");
|
||||||
return [settingsSource.replaceAll("$$$$$$", "$$$"), pythonSource];
|
return [settingsSource.replaceAll("$$$$$$", "$$$"), pythonSource];
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function restoreLocal(): [string, string] | null {
|
||||||
|
const source = localStorage.getItem("source");
|
||||||
|
|
||||||
|
if (source == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return JSON.parse(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function persistLocal({
|
||||||
|
settingsSource,
|
||||||
|
pythonSource,
|
||||||
|
}: {
|
||||||
|
settingsSource: string;
|
||||||
|
pythonSource: string;
|
||||||
|
}) {
|
||||||
|
localStorage.setItem(
|
||||||
|
"source",
|
||||||
|
JSON.stringify([settingsSource, pythonSource]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,8 @@ html,
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "Alliance Text";
|
font-family: "Alliance Text";
|
||||||
src:
|
src:
|
||||||
url("../public/fonts/Alliance-TextRegular.woff2") format("woff2"),
|
url("../fonts/Alliance-TextRegular.woff2") format("woff2"),
|
||||||
url("../public/fonts/Alliance-TextRegular.woff") format("woff");
|
url("../fonts/Alliance-TextRegular.woff") format("woff");
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: block;
|
font-display: block;
|
||||||
|
|
@ -39,8 +39,8 @@ html,
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "Alliance Text";
|
font-family: "Alliance Text";
|
||||||
src:
|
src:
|
||||||
url("../public/fonts/Alliance-TextMedium.woff2") format("woff2"),
|
url("../fonts/Alliance-TextMedium.woff2") format("woff2"),
|
||||||
url("../public/fonts/Alliance-TextMedium.woff") format("woff");
|
url("../fonts/Alliance-TextMedium.woff") format("woff");
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: block;
|
font-display: block;
|
||||||
|
|
@ -49,8 +49,8 @@ html,
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "Alliance Platt";
|
font-family: "Alliance Platt";
|
||||||
src:
|
src:
|
||||||
url("../public/fonts/Alliance-PlattMedium.woff2") format("woff2"),
|
url("../fonts/Alliance-PlattMedium.woff2") format("woff2"),
|
||||||
url("../public/fonts/Alliance-PlattMedium.woff") format("woff");
|
url("../fonts/Alliance-PlattMedium.woff") format("woff");
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: block;
|
font-display: block;
|
||||||
|
|
@ -59,8 +59,8 @@ html,
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "Alliance Platt";
|
font-family: "Alliance Platt";
|
||||||
src:
|
src:
|
||||||
url("../public/fonts/Alliance-PlattRegular.woff2") format("woff2"),
|
url("../fonts/Alliance-PlattRegular.woff2") format("woff2"),
|
||||||
url("../public/fonts/Alliance-PlattRegular.woff") format("woff");
|
url("../fonts/Alliance-PlattRegular.woff") format("woff");
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: block;
|
font-display: block;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue