mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-27 20:42:10 +00:00
Playground: Add Copy as pyproject.toml/ruff.toml and paste from TOML (#13328)
This commit is contained in:
parent
43a5922f6f
commit
21bfab9b69
4 changed files with 153 additions and 21 deletions
15
playground/package-lock.json
generated
15
playground/package-lock.json
generated
|
@ -14,7 +14,8 @@
|
|||
"monaco-editor": "^0.51.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-resizable-panels": "^2.0.0"
|
||||
"react-resizable-panels": "^2.0.0",
|
||||
"smol-toml": "^1.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.0.26",
|
||||
|
@ -4560,6 +4561,18 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/smol-toml": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.3.0.tgz",
|
||||
"integrity": "sha512-tWpi2TsODPScmi48b/OQZGi2lgUmBCHy6SZrhi/FdnnHiU1GwebbCfuQuxsC3nHaLwtYeJGPrDZDIeodDOc4pA==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/cyyynthia"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
|
||||
|
|
|
@ -21,7 +21,8 @@
|
|||
"monaco-editor": "^0.51.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-resizable-panels": "^2.0.0"
|
||||
"react-resizable-panels": "^2.0.0",
|
||||
"smol-toml": "^1.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.0.26",
|
||||
|
|
|
@ -2,10 +2,11 @@
|
|||
* Editor for the settings JSON.
|
||||
*/
|
||||
|
||||
import MonacoEditor, { useMonaco } from "@monaco-editor/react";
|
||||
import { useCallback, useEffect } from "react";
|
||||
import schema from "../../../ruff.schema.json";
|
||||
import { useCallback } from "react";
|
||||
import { Theme } from "./theme";
|
||||
import MonacoEditor from "@monaco-editor/react";
|
||||
import { editor } from "monaco-editor";
|
||||
import IStandaloneCodeEditor = editor.IStandaloneCodeEditor;
|
||||
|
||||
export default function SettingsEditor({
|
||||
visible,
|
||||
|
@ -18,26 +19,86 @@ export default function SettingsEditor({
|
|||
theme: Theme;
|
||||
onChange: (source: string) => void;
|
||||
}) {
|
||||
const monaco = useMonaco();
|
||||
|
||||
useEffect(() => {
|
||||
monaco?.languages.json.jsonDefaults.setDiagnosticsOptions({
|
||||
schemas: [
|
||||
{
|
||||
uri: "https://raw.githubusercontent.com/astral-sh/ruff/main/ruff.schema.json",
|
||||
fileMatch: ["*"],
|
||||
schema,
|
||||
},
|
||||
],
|
||||
});
|
||||
}, [monaco]);
|
||||
|
||||
const handleChange = useCallback(
|
||||
(value: string | undefined) => {
|
||||
onChange(value ?? "");
|
||||
},
|
||||
[onChange],
|
||||
);
|
||||
|
||||
const handleMount = useCallback((editor: IStandaloneCodeEditor) => {
|
||||
editor.addAction({
|
||||
id: "copyAsRuffToml",
|
||||
label: "Copy as ruff.toml",
|
||||
contextMenuGroupId: "9_cutcopypaste",
|
||||
contextMenuOrder: 3,
|
||||
|
||||
async run(editor): Promise<undefined> {
|
||||
const model = editor.getModel();
|
||||
|
||||
if (model == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const toml = await import("smol-toml");
|
||||
const settings = model.getValue();
|
||||
const tomlSettings = toml.stringify(JSON.parse(settings));
|
||||
|
||||
await navigator.clipboard.writeText(tomlSettings);
|
||||
},
|
||||
});
|
||||
|
||||
editor.addAction({
|
||||
id: "copyAsPyproject.toml",
|
||||
label: "Copy as pyproject.toml",
|
||||
contextMenuGroupId: "9_cutcopypaste",
|
||||
contextMenuOrder: 4,
|
||||
|
||||
async run(editor): Promise<undefined> {
|
||||
const model = editor.getModel();
|
||||
|
||||
if (model == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const settings = model.getValue();
|
||||
const toml = await import("smol-toml");
|
||||
const tomlSettings = toml.stringify(
|
||||
prefixWithRuffToml(JSON.parse(settings)),
|
||||
);
|
||||
|
||||
await navigator.clipboard.writeText(tomlSettings);
|
||||
},
|
||||
});
|
||||
editor.onDidPaste((event) => {
|
||||
const model = editor.getModel();
|
||||
|
||||
if (model == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow pasting a TOML settings configuration if it replaces the entire settings.
|
||||
if (model.getFullModelRange().equalsRange(event.range)) {
|
||||
const pasted = model.getValueInRange(event.range);
|
||||
|
||||
// Text starting with a `{` must be JSON. Don't even try to parse as TOML.
|
||||
if (!pasted.trimStart().startsWith("{")) {
|
||||
import("smol-toml").then((toml) => {
|
||||
try {
|
||||
const parsed = toml.parse(pasted);
|
||||
const cleansed = stripToolRuff(parsed);
|
||||
|
||||
model.setValue(JSON.stringify(cleansed, null, 4));
|
||||
} catch (e) {
|
||||
// Turned out to not be TOML after all.
|
||||
console.warn("Failed to parse settings as TOML", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<MonacoEditor
|
||||
options={{
|
||||
|
@ -46,13 +107,59 @@ export default function SettingsEditor({
|
|||
fontSize: 14,
|
||||
roundedSelection: false,
|
||||
scrollBeyondLastLine: false,
|
||||
contextmenu: false,
|
||||
contextmenu: true,
|
||||
}}
|
||||
onMount={handleMount}
|
||||
wrapperProps={visible ? {} : { style: { display: "none" } }}
|
||||
language={"json"}
|
||||
language="json"
|
||||
value={source}
|
||||
theme={theme === "light" ? "Ayu-Light" : "Ayu-Dark"}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function stripToolRuff(settings: object) {
|
||||
const { tool, ...nonToolSettings } = settings as any;
|
||||
|
||||
// Flatten out `tool.ruff.x` to just `x`
|
||||
if (typeof tool == "object" && !Array.isArray(tool)) {
|
||||
if (tool.ruff != null) {
|
||||
return { ...nonToolSettings, ...tool.ruff };
|
||||
}
|
||||
}
|
||||
|
||||
return Object.fromEntries(
|
||||
Object.entries(settings).flatMap(([key, value]) => {
|
||||
if (key.startsWith("tool.ruff")) {
|
||||
const strippedKey = key.substring("tool.ruff".length);
|
||||
|
||||
if (strippedKey === "") {
|
||||
return Object.entries(value);
|
||||
}
|
||||
|
||||
return [[strippedKey.substring(1), value]];
|
||||
}
|
||||
|
||||
return [[key, value]];
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
function prefixWithRuffToml(settings: object) {
|
||||
const subTableEntries = [];
|
||||
const ruffTableEntries = [];
|
||||
|
||||
for (const [key, value] of Object.entries(settings)) {
|
||||
if (typeof value === "object" && !Array.isArray(value)) {
|
||||
subTableEntries.push([`tool.ruff.${key}`, value]);
|
||||
} else {
|
||||
ruffTableEntries.push([key, value]);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
["tool.ruff"]: Object.fromEntries(ruffTableEntries),
|
||||
...Object.fromEntries(subTableEntries),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*/
|
||||
|
||||
import { Monaco } from "@monaco-editor/react";
|
||||
import schema from "../../../ruff.schema.json";
|
||||
|
||||
export const WHITE = "#ffffff";
|
||||
export const RADIATE = "#d7ff64";
|
||||
|
@ -31,6 +32,16 @@ export function setupMonaco(monaco: Monaco) {
|
|||
defineRustPythonTokensLanguage(monaco);
|
||||
defineRustPythonAstLanguage(monaco);
|
||||
defineCommentsLanguage(monaco);
|
||||
|
||||
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
|
||||
schemas: [
|
||||
{
|
||||
uri: "https://raw.githubusercontent.com/astral-sh/ruff/main/ruff.schema.json",
|
||||
fileMatch: ["*"],
|
||||
schema,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
function defineAyuThemes(monaco: Monaco) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue