diff --git a/Cargo.lock b/Cargo.lock index bd78f8fc12..aefa5cb5ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2252,6 +2252,7 @@ dependencies = [ "ruff", "ruff_diagnostics", "ruff_python_ast", + "ruff_python_formatter", "ruff_rustpython", "rustpython-parser", "serde", diff --git a/crates/ruff_formatter/src/lib.rs b/crates/ruff_formatter/src/lib.rs index 5c99ebba87..9034e271d9 100644 --- a/crates/ruff_formatter/src/lib.rs +++ b/crates/ruff_formatter/src/lib.rs @@ -38,7 +38,7 @@ mod source_code; use crate::formatter::Formatter; use crate::group_id::UniqueGroupIdBuilder; use crate::prelude::TagKind; -use std::fmt::Debug; +use std::fmt::{Debug, Display}; use std::marker::PhantomData; use crate::format_element::document::Document; @@ -331,6 +331,16 @@ where Ok(printed) } } + +impl Display for Formatted +where + Context: FormatContext, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(&self.document.display(self.context.source_code()), f) + } +} + pub type PrintResult = Result; #[derive(Debug, Clone, Eq, PartialEq)] diff --git a/crates/ruff_wasm/Cargo.toml b/crates/ruff_wasm/Cargo.toml index 5383edfa78..bf1fb2e08a 100644 --- a/crates/ruff_wasm/Cargo.toml +++ b/crates/ruff_wasm/Cargo.toml @@ -21,6 +21,7 @@ default = ["console_error_panic_hook"] ruff = { path = "../ruff" } ruff_diagnostics = { path = "../ruff_diagnostics" } ruff_python_ast = { path = "../ruff_python_ast" } +ruff_python_formatter = { path = "../ruff_python_formatter" } ruff_rustpython = { path = "../ruff_rustpython" } console_error_panic_hook = { version = "0.1.7", optional = true } @@ -30,7 +31,7 @@ rustpython-parser = { workspace = true } serde = { workspace = true } serde-wasm-bindgen = { version = "0.5.0" } wasm-bindgen = { version = "0.2.84" } +js-sys = { version = "0.3.61" } [dev-dependencies] -js-sys = { version = "0.3.61" } wasm-bindgen-test = { version = "0.3.34" } diff --git a/crates/ruff_wasm/src/lib.rs b/crates/ruff_wasm/src/lib.rs index 27e3de73c0..1bd5f67bef 100644 --- a/crates/ruff_wasm/src/lib.rs +++ b/crates/ruff_wasm/src/lib.rs @@ -1,6 +1,9 @@ use std::path::Path; +use js_sys::Error; + use rustpython_parser::lexer::LexResult; +use rustpython_parser::{parse_tokens, Mode}; use serde::{Deserialize, Serialize}; use wasm_bindgen::prelude::*; @@ -18,7 +21,10 @@ use ruff::rules::{ use ruff::settings::configuration::Configuration; use ruff::settings::options::Options; use ruff::settings::{defaults, flags, Settings}; -use ruff_python_ast::source_code::{Indexer, Locator, SourceLocation, Stylist}; +use ruff_python_ast::source_code::{ + CommentRangesBuilder, Indexer, Locator, SourceLocation, Stylist, +}; +use ruff_python_formatter::{format_module, format_node, PyFormatOptions}; #[wasm_bindgen(typescript_custom_section)] const TYPES: &'static str = r#" @@ -89,157 +95,213 @@ pub fn run() { } #[wasm_bindgen] -#[allow(non_snake_case)] -pub fn currentVersion() -> JsValue { - JsValue::from(ruff::VERSION) +pub struct Workspace { + settings: Settings, } #[wasm_bindgen] -#[allow(non_snake_case)] -pub fn defaultSettings() -> Result { - Ok(serde_wasm_bindgen::to_value(&Options { - // Propagate defaults. - allowed_confusables: Some(Vec::default()), - builtins: Some(Vec::default()), - dummy_variable_rgx: Some(defaults::DUMMY_VARIABLE_RGX.as_str().to_string()), - extend_fixable: Some(Vec::default()), - extend_ignore: Some(Vec::default()), - extend_select: Some(Vec::default()), - extend_unfixable: Some(Vec::default()), - external: Some(Vec::default()), - ignore: Some(Vec::default()), - line_length: Some(LineLength::default()), - tab_size: Some(TabSize::default()), - select: Some(defaults::PREFIXES.to_vec()), - target_version: Some(defaults::TARGET_VERSION), - // Ignore a bunch of options that don't make sense in a single-file editor. - cache_dir: None, - exclude: None, - extend: None, - extend_exclude: None, - extend_include: None, - extend_per_file_ignores: None, - fix: None, - fix_only: None, - fixable: None, - force_exclude: None, - format: None, - include: None, - ignore_init_module_imports: None, - namespace_packages: None, - per_file_ignores: None, - required_version: None, - respect_gitignore: None, - show_fixes: None, - show_source: None, - src: None, - task_tags: None, - typing_modules: None, - unfixable: None, - // Use default options for all plugins. - flake8_annotations: Some(flake8_annotations::settings::Settings::default().into()), - flake8_bandit: Some(flake8_bandit::settings::Settings::default().into()), - flake8_bugbear: Some(flake8_bugbear::settings::Settings::default().into()), - flake8_builtins: Some(flake8_builtins::settings::Settings::default().into()), - flake8_comprehensions: Some(flake8_comprehensions::settings::Settings::default().into()), - flake8_copyright: Some(flake8_copyright::settings::Settings::default().into()), - flake8_errmsg: Some(flake8_errmsg::settings::Settings::default().into()), - flake8_gettext: Some(flake8_gettext::settings::Settings::default().into()), - flake8_implicit_str_concat: Some( - flake8_implicit_str_concat::settings::Settings::default().into(), - ), - flake8_import_conventions: Some( - flake8_import_conventions::settings::Settings::default().into(), - ), - flake8_pytest_style: Some(flake8_pytest_style::settings::Settings::default().into()), - flake8_quotes: Some(flake8_quotes::settings::Settings::default().into()), - flake8_self: Some(flake8_self::settings::Settings::default().into()), - flake8_tidy_imports: Some(flake8_tidy_imports::settings::Settings::default().into()), - flake8_type_checking: Some(flake8_type_checking::settings::Settings::default().into()), - flake8_unused_arguments: Some( - flake8_unused_arguments::settings::Settings::default().into(), - ), - isort: Some(isort::settings::Settings::default().into()), - mccabe: Some(mccabe::settings::Settings::default().into()), - pep8_naming: Some(pep8_naming::settings::Settings::default().into()), - pycodestyle: Some(pycodestyle::settings::Settings::default().into()), - pydocstyle: Some(pydocstyle::settings::Settings::default().into()), - pyflakes: Some(pyflakes::settings::Settings::default().into()), - pylint: Some(pylint::settings::Settings::default().into()), - pyupgrade: Some(pyupgrade::settings::Settings::default().into()), - })?) -} +impl Workspace { + pub fn version() -> String { + ruff::VERSION.to_string() + } -#[wasm_bindgen] -#[allow(non_snake_case)] -pub fn check(contents: &str, options: JsValue) -> Result { - let options: Options = serde_wasm_bindgen::from_value(options).map_err(|e| e.to_string())?; - let configuration = - Configuration::from_options(options, Path::new(".")).map_err(|e| e.to_string())?; - let settings = - Settings::from_configuration(configuration, Path::new(".")).map_err(|e| e.to_string())?; + #[wasm_bindgen(constructor)] + pub fn new(options: JsValue) -> Result { + let options: Options = serde_wasm_bindgen::from_value(options).map_err(into_error)?; + let configuration = + Configuration::from_options(options, Path::new(".")).map_err(into_error)?; + let settings = + Settings::from_configuration(configuration, Path::new(".")).map_err(into_error)?; - // Tokenize once. - let tokens: Vec = ruff_rustpython::tokenize(contents); + Ok(Workspace { settings }) + } - // Map row and column locations to byte slices (lazily). - let locator = Locator::new(contents); - - // Detect the current code style (lazily). - let stylist = Stylist::from_tokens(&tokens, &locator); - - // Extra indices from the code. - let indexer = Indexer::from_tokens(&tokens, &locator); - - // Extract the `# noqa` and `# isort: skip` directives from the source. - let directives = - directives::extract_directives(&tokens, directives::Flags::empty(), &locator, &indexer); - - // Generate checks. - let LinterResult { - data: (diagnostics, _imports), - .. - } = check_path( - Path::new(""), - None, - tokens, - &locator, - &stylist, - &indexer, - &directives, - &settings, - flags::Noqa::Enabled, - None, - ); - - let source_code = locator.to_source_code(); - - let messages: Vec = diagnostics - .into_iter() - .map(|message| { - let start_location = source_code.source_location(message.start()); - let end_location = source_code.source_location(message.end()); - - ExpandedMessage { - code: message.kind.rule().noqa_code().to_string(), - message: message.kind.body, - location: start_location, - end_location, - fix: message.fix.map(|fix| ExpandedFix { - message: message.kind.suggestion, - edits: fix - .into_edits() - .into_iter() - .map(|edit| ExpandedEdit { - location: source_code.source_location(edit.start()), - end_location: source_code.source_location(edit.end()), - content: edit.content().map(ToString::to_string), - }) - .collect(), - }), - } + #[wasm_bindgen(js_name=defaultSettings)] + pub fn default_settings() -> Result { + serde_wasm_bindgen::to_value(&Options { + // Propagate defaults. + allowed_confusables: Some(Vec::default()), + builtins: Some(Vec::default()), + dummy_variable_rgx: Some(defaults::DUMMY_VARIABLE_RGX.as_str().to_string()), + extend_fixable: Some(Vec::default()), + extend_ignore: Some(Vec::default()), + extend_select: Some(Vec::default()), + extend_unfixable: Some(Vec::default()), + external: Some(Vec::default()), + ignore: Some(Vec::default()), + line_length: Some(LineLength::default()), + tab_size: Some(TabSize::default()), + select: Some(defaults::PREFIXES.to_vec()), + target_version: Some(defaults::TARGET_VERSION), + // Ignore a bunch of options that don't make sense in a single-file editor. + cache_dir: None, + exclude: None, + extend: None, + extend_exclude: None, + extend_include: None, + extend_per_file_ignores: None, + fix: None, + fix_only: None, + fixable: None, + force_exclude: None, + format: None, + include: None, + ignore_init_module_imports: None, + namespace_packages: None, + per_file_ignores: None, + required_version: None, + respect_gitignore: None, + show_fixes: None, + show_source: None, + src: None, + task_tags: None, + typing_modules: None, + unfixable: None, + // Use default options for all plugins. + flake8_annotations: Some(flake8_annotations::settings::Settings::default().into()), + flake8_bandit: Some(flake8_bandit::settings::Settings::default().into()), + flake8_bugbear: Some(flake8_bugbear::settings::Settings::default().into()), + flake8_builtins: Some(flake8_builtins::settings::Settings::default().into()), + flake8_comprehensions: Some( + flake8_comprehensions::settings::Settings::default().into(), + ), + flake8_copyright: Some(flake8_copyright::settings::Settings::default().into()), + flake8_errmsg: Some(flake8_errmsg::settings::Settings::default().into()), + flake8_gettext: Some(flake8_gettext::settings::Settings::default().into()), + flake8_implicit_str_concat: Some( + flake8_implicit_str_concat::settings::Settings::default().into(), + ), + flake8_import_conventions: Some( + flake8_import_conventions::settings::Settings::default().into(), + ), + flake8_pytest_style: Some(flake8_pytest_style::settings::Settings::default().into()), + flake8_quotes: Some(flake8_quotes::settings::Settings::default().into()), + flake8_self: Some(flake8_self::settings::Settings::default().into()), + flake8_tidy_imports: Some(flake8_tidy_imports::settings::Settings::default().into()), + flake8_type_checking: Some(flake8_type_checking::settings::Settings::default().into()), + flake8_unused_arguments: Some( + flake8_unused_arguments::settings::Settings::default().into(), + ), + isort: Some(isort::settings::Settings::default().into()), + mccabe: Some(mccabe::settings::Settings::default().into()), + pep8_naming: Some(pep8_naming::settings::Settings::default().into()), + pycodestyle: Some(pycodestyle::settings::Settings::default().into()), + pydocstyle: Some(pydocstyle::settings::Settings::default().into()), + pyflakes: Some(pyflakes::settings::Settings::default().into()), + pylint: Some(pylint::settings::Settings::default().into()), + pyupgrade: Some(pyupgrade::settings::Settings::default().into()), }) - .collect(); + .map_err(into_error) + } - Ok(serde_wasm_bindgen::to_value(&messages)?) + pub fn check(&self, contents: &str) -> Result { + // Tokenize once. + let tokens: Vec = ruff_rustpython::tokenize(contents); + + // Map row and column locations to byte slices (lazily). + let locator = Locator::new(contents); + + // Detect the current code style (lazily). + let stylist = Stylist::from_tokens(&tokens, &locator); + + // Extra indices from the code. + let indexer = Indexer::from_tokens(&tokens, &locator); + + // Extract the `# noqa` and `# isort: skip` directives from the source. + let directives = + directives::extract_directives(&tokens, directives::Flags::empty(), &locator, &indexer); + + // Generate checks. + let LinterResult { + data: (diagnostics, _imports), + .. + } = check_path( + Path::new(""), + None, + tokens, + &locator, + &stylist, + &indexer, + &directives, + &self.settings, + flags::Noqa::Enabled, + None, + ); + + let source_code = locator.to_source_code(); + + let messages: Vec = diagnostics + .into_iter() + .map(|message| { + let start_location = source_code.source_location(message.start()); + let end_location = source_code.source_location(message.end()); + + ExpandedMessage { + code: message.kind.rule().noqa_code().to_string(), + message: message.kind.body, + location: start_location, + end_location, + fix: message.fix.map(|fix| ExpandedFix { + message: message.kind.suggestion, + edits: fix + .into_edits() + .into_iter() + .map(|edit| ExpandedEdit { + location: source_code.source_location(edit.start()), + end_location: source_code.source_location(edit.end()), + content: edit.content().map(ToString::to_string), + }) + .collect(), + }), + } + }) + .collect(); + + serde_wasm_bindgen::to_value(&messages).map_err(into_error) + } + + pub fn format(&self, contents: &str) -> Result { + let printed = format_module(contents, PyFormatOptions::default()).map_err(into_error)?; + + Ok(printed.into_code()) + } + + pub fn format_ir(&self, contents: &str) -> Result { + let tokens: Vec<_> = rustpython_parser::lexer::lex(contents, Mode::Module).collect(); + let mut comment_ranges = CommentRangesBuilder::default(); + + for (token, range) in tokens.iter().flatten() { + comment_ranges.visit_token(token, *range); + } + + let comment_ranges = comment_ranges.finish(); + let module = parse_tokens(tokens, Mode::Module, ".").map_err(into_error)?; + + let formatted = format_node( + &module, + &comment_ranges, + contents, + PyFormatOptions::default(), + ) + .map_err(into_error)?; + + Ok(format!("{formatted}")) + } + + /// Parses the content and returns its AST + pub fn parse(&self, contents: &str) -> Result { + let parsed = rustpython_parser::parse(contents, Mode::Module, ".").map_err(into_error)?; + + Ok(format!("{parsed:#?}")) + } + + pub fn tokens(&self, contents: &str) -> Result { + let tokens: Vec<_> = rustpython_parser::lexer::lex(contents, Mode::Module).collect(); + + Ok(format!("{tokens:#?}")) + } +} + +pub(crate) fn into_error(err: E) -> Error { + Error::new(&err.to_string()) } diff --git a/crates/ruff_wasm/tests/api.rs b/crates/ruff_wasm/tests/api.rs index 35c72dfc10..9900f4243a 100644 --- a/crates/ruff_wasm/tests/api.rs +++ b/crates/ruff_wasm/tests/api.rs @@ -4,12 +4,12 @@ use wasm_bindgen_test::wasm_bindgen_test; use ruff::registry::Rule; use ruff_python_ast::source_code::{OneIndexed, SourceLocation}; -use ruff_wasm::{check, ExpandedMessage}; +use ruff_wasm::{ExpandedMessage, Workspace}; macro_rules! check { ($source:expr, $config:expr, $expected:expr) => {{ - let foo = js_sys::JSON::parse($config).unwrap(); - match check($source, foo) { + let config = js_sys::JSON::parse($config).unwrap(); + match Workspace::new(config).unwrap().check($source) { Ok(output) => { let result: Vec = serde_wasm_bindgen::from_value(output).unwrap(); assert_eq!(result, $expected); diff --git a/playground/package-lock.json b/playground/package-lock.json index ae72b506f4..80222151a8 100644 --- a/playground/package-lock.json +++ b/playground/package-lock.json @@ -13,7 +13,8 @@ "lz-string": "^1.4.4", "monaco-editor": "^0.40.0", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-resizable-panels": "^0.0.54" }, "devDependencies": { "@types/react": "^18.0.26", @@ -3964,6 +3965,15 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, + "node_modules/react-resizable-panels": { + "version": "0.0.54", + "resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-0.0.54.tgz", + "integrity": "sha512-f8hHdQrqvXoiZGdRNuoOi/C2cdYT2nEpaOb1KIWVWorSTPZmnE+ZQiamGeu+AMx3iZ/tqBtlAkBOpKXzTnDCoA==", + "peerDependencies": { + "react": "^16.14.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", diff --git a/playground/package.json b/playground/package.json index a27b063a44..82d08904b6 100644 --- a/playground/package.json +++ b/playground/package.json @@ -20,7 +20,8 @@ "lz-string": "^1.4.4", "monaco-editor": "^0.40.0", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-resizable-panels": "^0.0.54" }, "devDependencies": { "@types/react": "^18.0.26", diff --git a/playground/src/Editor/Editor.tsx b/playground/src/Editor/Editor.tsx index 421b5f1f53..60c91e626f 100644 --- a/playground/src/Editor/Editor.tsx +++ b/playground/src/Editor/Editor.tsx @@ -1,19 +1,26 @@ -import { useCallback, useEffect, useMemo, useState } from "react"; +import { + useCallback, + useDeferredValue, + useEffect, + useMemo, + useState, +} from "react"; import { DEFAULT_PYTHON_SOURCE } from "../constants"; -import init, { - check, - Diagnostic, - currentVersion, - defaultSettings, -} from "../pkg"; +import init, { Diagnostic, Workspace } from "../pkg"; import { ErrorMessage } from "./ErrorMessage"; import Header from "./Header"; import { useTheme } from "./theme"; import { persist, restore, stringify } from "./settings"; import SettingsEditor from "./SettingsEditor"; import SourceEditor from "./SourceEditor"; -import MonacoThemes from "./MonacoThemes"; -import SideBar from "./SideBar"; +import { Panel, PanelGroup } from "react-resizable-panels"; +import PrimarySideBar from "./PrimarySideBar"; +import SecondarySideBar from "./SecondarySideBar"; +import { HorizontalResizeHandle } from "./ResizeHandle"; +import SecondaryPanel, { + SecondaryPanelResult, + SecondaryTool, +} from "./SecondaryPanel"; type Tab = "Source" | "Settings"; @@ -26,6 +33,7 @@ interface Source { interface CheckResult { diagnostics: Diagnostic[]; error: string | null; + secondary: SecondaryPanelResult; } export default function Editor() { @@ -33,6 +41,7 @@ export default function Editor() { const [checkResult, setCheckResult] = useState({ diagnostics: [], error: null, + secondary: null, }); const [source, setSource] = useState({ pythonSource: "", @@ -42,15 +51,18 @@ export default function Editor() { const [tab, setTab] = useState("Source"); const [theme, setTheme] = useTheme(); + const [secondaryTool, setSecondaryTool] = useState( + null, + ); const initialized = ruffVersion != null; useEffect(() => { init().then(() => { - setRuffVersion(currentVersion()); + setRuffVersion(Workspace.version()); const [settingsSource, pythonSource] = restore() ?? [ - stringify(defaultSettings()), + stringify(Workspace.defaultSettings()), DEFAULT_PYTHON_SOURCE, ]; @@ -62,28 +74,72 @@ export default function Editor() { }); }, []); + const deferredSource = useDeferredValue(source); + useEffect(() => { if (!initialized) { return; } - const { settingsSource, pythonSource } = source; + const { settingsSource, pythonSource } = deferredSource; try { const config = JSON.parse(settingsSource); - const diagnostics = check(pythonSource, config); + const workspace = new Workspace(config); + const diagnostics = workspace.check(pythonSource); + + let secondary: SecondaryPanelResult = null; + + try { + switch (secondaryTool) { + case "AST": + secondary = { + status: "ok", + content: workspace.parse(pythonSource), + }; + break; + + case "Format": + secondary = { + status: "ok", + content: workspace.format(pythonSource), + }; + break; + + case "FIR": + secondary = { + status: "ok", + content: workspace.format_ir(pythonSource), + }; + break; + + case "Tokens": + secondary = { + status: "ok", + content: workspace.tokens(pythonSource), + }; + break; + } + } catch (error: unknown) { + secondary = { + status: "error", + error: error instanceof Error ? error.message : error + "", + }; + } setCheckResult({ diagnostics, error: null, + secondary, }); } catch (e) { setCheckResult({ diagnostics: [], error: (e as Error).message, + secondary: null, }); } - }, [initialized, source]); + }, [initialized, deferredSource, secondaryTool]); const handleShare = useMemo(() => { if (!initialized) { @@ -111,6 +167,8 @@ export default function Editor() { })); }, []); + // useMonacoTheme(); + return (
- -
{initialized ? ( - <> - setTab(tool)} selected={tab} /> - + setTab(tool)} + selected={tab} /> - + + + + {secondaryTool != null && ( + <> + + + + + + )} + { + if (secondaryTool === tool) { + setSecondaryTool(null); + } else { + setSecondaryTool(tool); + } + }} /> - + ) : null}
{checkResult.error && tab === "Source" ? ( diff --git a/playground/src/Editor/Icons.tsx b/playground/src/Editor/Icons.tsx index a1625c4c04..13f4188a5c 100644 --- a/playground/src/Editor/Icons.tsx +++ b/playground/src/Editor/Icons.tsx @@ -25,8 +25,17 @@ export function FileIcon() { return ( - - + + ); } @@ -47,3 +56,54 @@ export function SettingsIcon() { ); } + +export function StructureIcon() { + return ( + + + + ); +} + +export function FormatIcon() { + return ( + + + + ); +} + +export function TokensIcon() { + return ( + + + + ); +} diff --git a/playground/src/Editor/MonacoThemes.tsx b/playground/src/Editor/MonacoThemes.tsx deleted file mode 100644 index 5d2080e55a..0000000000 --- a/playground/src/Editor/MonacoThemes.tsx +++ /dev/null @@ -1,565 +0,0 @@ -/** - * Non-rendering component that loads the Monaco editor themes. - */ - -import { useMonaco } from "@monaco-editor/react"; -import { useEffect } from "react"; - -export const WHITE = "#ffffff"; -export const RADIATE = "#d7ff64"; -export const FLARE = "#6340ac"; -export const ROCK = "#78876e"; -export const GALAXY = "#261230"; -export const SPACE = "#30173d"; -export const COMET = "#6f5d6f"; -export const COSMIC = "#de5fe9"; -export const SUN = "#ffac2f"; -export const ELECTRON = "#46ebe1"; -export const AURORA = "#46eb74"; -export const CONSTELLATION = "#5f6de9"; -export const NEUTRON = "#cff3cf"; -export const PROTON = "#f6afbc"; -export const NEBULA = "#cdcbfb"; -export const SUPERNOVA = "#f1aff6"; -export const STARLIGHT = "#f4f4f1"; -export const LUNAR = "#fbf2fc"; -export const ASTEROID = "#e3cee3"; -export const CRATER = "#f0dfdf"; - -export default function MonacoThemes() { - const monaco = useMonaco(); - - useEffect(() => { - // Generated via `monaco-vscode-textmate-theme-converter`. - // See: https://github.com/ayu-theme/vscode-ayu/blob/91839e8a9dfa78d61e58dbcf9b52272a01fee66a/ayu-light.json. - monaco?.editor.defineTheme("Ayu-Light", { - inherit: false, - base: "vs-dark", - colors: { - focusBorder: "#ffaa33b3", - foreground: "#8a9199", - "widget.shadow": "#00000026", - "selection.background": "#035bd626", - "icon.foreground": "#8a9199", - errorForeground: "#e65050", - descriptionForeground: "#8a9199", - "textBlockQuote.background": "#f3f4f5", - "textLink.foreground": "#ffaa33", - "textLink.activeForeground": "#ffaa33", - "textPreformat.foreground": "#5c6166", - "button.background": "#ffaa33", - "button.foreground": "#f8f9fa", - "button.hoverBackground": "#f9a52e", - "button.secondaryBackground": "#8a919933", - "button.secondaryForeground": "#5c6166", - "button.secondaryHoverBackground": "#8a919980", - "dropdown.background": "#fcfcfc", - "dropdown.foreground": "#8a9199", - "dropdown.border": "#8a919945", - "input.background": "#fcfcfc", - "input.border": "#8a919945", - "input.foreground": "#5c6166", - "input.placeholderForeground": "#8a919980", - "inputOption.activeBorder": "#f4a0284d", - "inputOption.activeBackground": "#ffaa3333", - "inputOption.activeForeground": "#f4a028", - "inputValidation.errorBackground": "#fcfcfc", - "inputValidation.errorBorder": "#e65050", - "inputValidation.infoBackground": "#f8f9fa", - "inputValidation.infoBorder": "#55b4d4", - "inputValidation.warningBackground": "#f8f9fa", - "inputValidation.warningBorder": "#f2ae49", - "scrollbar.shadow": "#6b7d8f00", - "scrollbarSlider.background": "#8a919966", - "scrollbarSlider.hoverBackground": "#8a919999", - "scrollbarSlider.activeBackground": "#8a9199b3", - "badge.background": "#ffaa3333", - "badge.foreground": "#f4a028", - "progressBar.background": "#ffaa33", - "list.activeSelectionBackground": "#56728f1f", - "list.activeSelectionForeground": "#5c6166", - "list.focusBackground": "#56728f1f", - "list.focusForeground": "#5c6166", - "list.focusOutline": "#56728f1f", - "list.highlightForeground": "#ffaa33", - "list.deemphasizedForeground": "#e65050", - "list.hoverBackground": "#56728f1f", - "list.inactiveSelectionBackground": "#6b7d8f1f", - "list.inactiveSelectionForeground": "#8a9199", - "list.invalidItemForeground": "#8a91994d", - "list.errorForeground": "#e65050", - "tree.indentGuidesStroke": "#8a919959", - "listFilterWidget.background": "#f3f4f5", - "listFilterWidget.outline": "#ffaa33", - "listFilterWidget.noMatchesOutline": "#e65050", - "list.filterMatchBackground": "#8f30efcc", - "list.filterMatchBorder": "#9f40ffcc", - "activityBar.background": "#f8f9fa", - "activityBar.foreground": "#8a9199cc", - "activityBar.inactiveForeground": "#8a919999", - "activityBar.border": "#f8f9fa", - "activityBar.activeBorder": "#ffaa33b3", - "activityBarBadge.background": "#ffaa33", - "activityBarBadge.foreground": "#f8f9fa", - "sideBar.background": "#f8f9fa", - "sideBar.border": "#f8f9fa", - "sideBarTitle.foreground": "#8a9199", - "sideBarSectionHeader.background": "#f8f9fa", - "sideBarSectionHeader.foreground": "#8a9199", - "sideBarSectionHeader.border": "#f8f9fa", - "minimap.background": "#f8f9fa", - "minimap.selectionHighlight": "#035bd626", - "minimap.errorHighlight": "#e65050", - "minimap.findMatchHighlight": "#9f40ff2b", - "minimapGutter.addedBackground": "#6cbf43", - "minimapGutter.modifiedBackground": "#478acc", - "minimapGutter.deletedBackground": "#ff7383", - "editorGroup.border": "#6b7d8f1f", - "editorGroup.background": "#f3f4f5", - "editorGroupHeader.noTabsBackground": "#f8f9fa", - "editorGroupHeader.tabsBackground": "#f8f9fa", - "editorGroupHeader.tabsBorder": "#f8f9fa", - "tab.activeBackground": "#f8f9fa", - "tab.activeForeground": "#5c6166", - "tab.border": "#f8f9fa", - "tab.activeBorder": "#ffaa33", - "tab.unfocusedActiveBorder": "#8a9199", - "tab.inactiveBackground": "#f8f9fa", - "tab.inactiveForeground": "#8a9199", - "tab.unfocusedActiveForeground": "#8a9199", - "tab.unfocusedInactiveForeground": "#8a9199", - "editor.background": "#f8f9fa", - "editor.foreground": "#5c6166", - "editorLineNumber.foreground": "#8a919966", - "editorLineNumber.activeForeground": "#8a9199cc", - "editorCursor.foreground": "#ffaa33", - "editor.inactiveSelectionBackground": "#035bd612", - "editor.selectionBackground": "#035bd626", - "editor.selectionHighlightBackground": "#6cbf4326", - "editor.selectionHighlightBorder": "#6cbf4300", - "editor.wordHighlightBackground": "#478acc14", - "editor.wordHighlightStrongBackground": "#6cbf4314", - "editor.wordHighlightBorder": "#478acc80", - "editor.wordHighlightStrongBorder": "#6cbf4380", - "editor.findMatchBackground": "#9f40ff2b", - "editor.findMatchBorder": "#9f40ff2b", - "editor.findMatchHighlightBackground": "#9f40ffcc", - "editor.findMatchHighlightBorder": "#8f30efcc", - "editor.findRangeHighlightBackground": "#9f40ff40", - "editor.rangeHighlightBackground": "#9f40ff33", - "editor.lineHighlightBackground": "#8a91991a", - "editorLink.activeForeground": "#ffaa33", - "editorWhitespace.foreground": "#8a919966", - "editorIndentGuide.background": "#8a91992e", - "editorIndentGuide.activeBackground": "#8a919959", - "editorRuler.foreground": "#8a91992e", - "editorCodeLens.foreground": "#787b8099", - "editorBracketMatch.background": "#8a91994d", - "editorBracketMatch.border": "#8a91994d", - "editor.snippetTabstopHighlightBackground": "#6cbf4333", - "editorOverviewRuler.border": "#6b7d8f1f", - "editorOverviewRuler.modifiedForeground": "#478acc", - "editorOverviewRuler.addedForeground": "#6cbf43", - "editorOverviewRuler.deletedForeground": "#ff7383", - "editorOverviewRuler.errorForeground": "#e65050", - "editorOverviewRuler.warningForeground": "#ffaa33", - "editorOverviewRuler.bracketMatchForeground": "#8a9199b3", - "editorOverviewRuler.wordHighlightForeground": "#478acc66", - "editorOverviewRuler.wordHighlightStrongForeground": "#6cbf4366", - "editorOverviewRuler.findMatchForeground": "#9f40ff2b", - "editorError.foreground": "#e65050", - "editorWarning.foreground": "#ffaa33", - "editorGutter.modifiedBackground": "#478acccc", - "editorGutter.addedBackground": "#6cbf43cc", - "editorGutter.deletedBackground": "#ff7383cc", - "diffEditor.insertedTextBackground": "#6cbf431f", - "diffEditor.removedTextBackground": "#ff73831f", - "diffEditor.diagonalFill": "#6b7d8f1f", - "editorWidget.background": "#f3f4f5", - "editorWidget.border": "#6b7d8f1f", - "editorHoverWidget.background": "#f3f4f5", - "editorHoverWidget.border": "#6b7d8f1f", - "editorSuggestWidget.background": "#f3f4f5", - "editorSuggestWidget.border": "#6b7d8f1f", - "editorSuggestWidget.highlightForeground": "#ffaa33", - "editorSuggestWidget.selectedBackground": "#56728f1f", - "debugExceptionWidget.border": "#6b7d8f1f", - "debugExceptionWidget.background": "#f3f4f5", - "editorMarkerNavigation.background": "#f3f4f5", - "peekView.border": "#56728f1f", - "peekViewTitle.background": "#56728f1f", - "peekViewTitleDescription.foreground": "#8a9199", - "peekViewTitleLabel.foreground": "#5c6166", - "peekViewEditor.background": "#f3f4f5", - "peekViewEditor.matchHighlightBackground": "#9f40ffcc", - "peekViewEditor.matchHighlightBorder": "#8f30efcc", - "peekViewResult.background": "#f3f4f5", - "peekViewResult.fileForeground": "#5c6166", - "peekViewResult.lineForeground": "#8a9199", - "peekViewResult.matchHighlightBackground": "#9f40ffcc", - "peekViewResult.selectionBackground": "#56728f1f", - "panel.background": "#f8f9fa", - "panel.border": "#6b7d8f1f", - "panelTitle.activeBorder": "#ffaa33", - "panelTitle.activeForeground": "#5c6166", - "panelTitle.inactiveForeground": "#8a9199", - "statusBar.background": "#f8f9fa", - "statusBar.foreground": "#8a9199", - "statusBar.border": "#f8f9fa", - "statusBar.debuggingBackground": "#ed9366", - "statusBar.debuggingForeground": "#fcfcfc", - "statusBar.noFolderBackground": "#f3f4f5", - "statusBarItem.activeBackground": "#8a919933", - "statusBarItem.hoverBackground": "#8a919933", - "statusBarItem.prominentBackground": "#6b7d8f1f", - "statusBarItem.prominentHoverBackground": "#00000030", - "statusBarItem.remoteBackground": "#ffaa33", - "statusBarItem.remoteForeground": "#fcfcfc", - "titleBar.activeBackground": "#f8f9fa", - "titleBar.activeForeground": "#5c6166", - "titleBar.inactiveBackground": "#f8f9fa", - "titleBar.inactiveForeground": "#8a9199", - "titleBar.border": "#f8f9fa", - "extensionButton.prominentForeground": "#fcfcfc", - "extensionButton.prominentBackground": "#ffaa33", - "extensionButton.prominentHoverBackground": "#f9a52e", - "pickerGroup.border": "#6b7d8f1f", - "pickerGroup.foreground": "#8a919980", - "debugToolBar.background": "#f3f4f5", - "debugIcon.breakpointForeground": "#ed9366", - "debugIcon.breakpointDisabledForeground": "#ed936680", - "debugConsoleInputIcon.foreground": "#ffaa33", - "welcomePage.tileBackground": "#f8f9fa", - "welcomePage.tileShadow": "#00000026", - "welcomePage.progress.background": "#8a91991a", - "welcomePage.buttonBackground": "#ffaa3366", - "walkThrough.embeddedEditorBackground": "#f3f4f5", - "gitDecoration.modifiedResourceForeground": "#478accb3", - "gitDecoration.deletedResourceForeground": "#ff7383b3", - "gitDecoration.untrackedResourceForeground": "#6cbf43b3", - "gitDecoration.ignoredResourceForeground": "#8a919980", - "gitDecoration.conflictingResourceForeground": "", - "gitDecoration.submoduleResourceForeground": "#a37accb3", - "settings.headerForeground": "#5c6166", - "settings.modifiedItemIndicator": "#478acc", - "keybindingLabel.background": "#8a91991a", - "keybindingLabel.foreground": "#5c6166", - "keybindingLabel.border": "#5c61661a", - "keybindingLabel.bottomBorder": "#5c61661a", - "terminal.background": "#f8f9fa", - "terminal.foreground": "#5c6166", - "terminal.ansiBlack": "#000000", - "terminal.ansiRed": "#ea6c6d", - "terminal.ansiGreen": "#6cbf43", - "terminal.ansiYellow": "#eca944", - "terminal.ansiBlue": "#3199e1", - "terminal.ansiMagenta": "#9e75c7", - "terminal.ansiCyan": "#46ba94", - "terminal.ansiWhite": "#c7c7c7", - "terminal.ansiBrightBlack": "#686868", - "terminal.ansiBrightRed": "#f07171", - "terminal.ansiBrightGreen": "#86b300", - "terminal.ansiBrightYellow": "#f2ae49", - "terminal.ansiBrightBlue": "#399ee6", - "terminal.ansiBrightMagenta": "#a37acc", - "terminal.ansiBrightCyan": "#4cbf99", - "terminal.ansiBrightWhite": "#d1d1d1", - }, - rules: [ - { - fontStyle: "italic", - foreground: "#787b8099", - token: "comment", - }, - { - foreground: ROCK, - token: "string", - }, - { - foreground: SUN, - token: "keyword", - }, - { - foreground: CONSTELLATION, - token: "number", - }, - { - token: "tag", - foreground: ROCK, - }, - ], - encodedTokensColors: [], - }); - - // Generated via `monaco-vscode-textmate-theme-converter`. - // See: https://github.com/ayu-theme/vscode-ayu/blob/91839e8a9dfa78d61e58dbcf9b52272a01fee66a/ayu-dark.json. - monaco?.editor.defineTheme("Ayu-Dark", { - inherit: false, - base: "vs-dark", - colors: { - focusBorder: "#e6b450b3", - foreground: "#565b66", - "widget.shadow": "#00000080", - "selection.background": "#409fff4d", - "icon.foreground": "#565b66", - errorForeground: "#d95757", - descriptionForeground: "#565b66", - "textBlockQuote.background": "#0f131a", - "textLink.foreground": "#e6b450", - "textLink.activeForeground": "#e6b450", - "textPreformat.foreground": "#bfbdb6", - "button.background": "#e6b450", - "button.foreground": "#0b0e14", - "button.hoverBackground": "#e1af4b", - "button.secondaryBackground": "#565b6633", - "button.secondaryForeground": "#bfbdb6", - "button.secondaryHoverBackground": "#565b6680", - "dropdown.background": "#0d1017", - "dropdown.foreground": "#565b66", - "dropdown.border": "#565b6645", - "input.background": "#0d1017", - "input.border": "#565b6645", - "input.foreground": "#bfbdb6", - "input.placeholderForeground": "#565b6680", - "inputOption.activeBorder": "#e6b4504d", - "inputOption.activeBackground": "#e6b45033", - "inputOption.activeForeground": "#e6b450", - "inputValidation.errorBackground": "#0d1017", - "inputValidation.errorBorder": "#d95757", - "inputValidation.infoBackground": "#0b0e14", - "inputValidation.infoBorder": "#39bae6", - "inputValidation.warningBackground": "#0b0e14", - "inputValidation.warningBorder": "#ffb454", - "scrollbar.shadow": "#11151c00", - "scrollbarSlider.background": "#565b6666", - "scrollbarSlider.hoverBackground": "#565b6699", - "scrollbarSlider.activeBackground": "#565b66b3", - "badge.background": "#e6b45033", - "badge.foreground": "#e6b450", - "progressBar.background": "#e6b450", - "list.activeSelectionBackground": "#47526640", - "list.activeSelectionForeground": "#bfbdb6", - "list.focusBackground": "#47526640", - "list.focusForeground": "#bfbdb6", - "list.focusOutline": "#47526640", - "list.highlightForeground": "#e6b450", - "list.deemphasizedForeground": "#d95757", - "list.hoverBackground": "#47526640", - "list.inactiveSelectionBackground": "#47526633", - "list.inactiveSelectionForeground": "#565b66", - "list.invalidItemForeground": "#565b664d", - "list.errorForeground": "#d95757", - "tree.indentGuidesStroke": "#6c738080", - "listFilterWidget.background": "#0f131a", - "listFilterWidget.outline": "#e6b450", - "listFilterWidget.noMatchesOutline": "#d95757", - "list.filterMatchBackground": "#5f4c7266", - "list.filterMatchBorder": "#6c598066", - "activityBar.background": "#0b0e14", - "activityBar.foreground": "#565b66cc", - "activityBar.inactiveForeground": "#565b6699", - "activityBar.border": "#0b0e14", - "activityBar.activeBorder": "#e6b450b3", - "activityBarBadge.background": "#e6b450", - "activityBarBadge.foreground": "#0b0e14", - "sideBar.background": "#0b0e14", - "sideBar.border": "#0b0e14", - "sideBarTitle.foreground": "#565b66", - "sideBarSectionHeader.background": "#0b0e14", - "sideBarSectionHeader.foreground": "#565b66", - "sideBarSectionHeader.border": "#0b0e14", - "minimap.background": "#0b0e14", - "minimap.selectionHighlight": "#409fff4d", - "minimap.errorHighlight": "#d95757", - "minimap.findMatchHighlight": "#6c5980", - "minimapGutter.addedBackground": "#7fd962", - "minimapGutter.modifiedBackground": "#73b8ff", - "minimapGutter.deletedBackground": "#f26d78", - "editorGroup.border": "#11151c", - "editorGroup.background": "#0f131a", - "editorGroupHeader.noTabsBackground": "#0b0e14", - "editorGroupHeader.tabsBackground": "#0b0e14", - "editorGroupHeader.tabsBorder": "#0b0e14", - "tab.activeBackground": "#0b0e14", - "tab.activeForeground": "#bfbdb6", - "tab.border": "#0b0e14", - "tab.activeBorder": "#e6b450", - "tab.unfocusedActiveBorder": "#565b66", - "tab.inactiveBackground": "#0b0e14", - "tab.inactiveForeground": "#565b66", - "tab.unfocusedActiveForeground": "#565b66", - "tab.unfocusedInactiveForeground": "#565b66", - "editor.background": "#0b0e14", - "editor.foreground": "#bfbdb6", - "editorLineNumber.foreground": "#6c738099", - "editorLineNumber.activeForeground": "#6c7380e6", - "editorCursor.foreground": "#e6b450", - "editor.inactiveSelectionBackground": "#409fff21", - "editor.selectionBackground": "#409fff4d", - "editor.selectionHighlightBackground": "#7fd96226", - "editor.selectionHighlightBorder": "#7fd96200", - "editor.wordHighlightBackground": "#73b8ff14", - "editor.wordHighlightStrongBackground": "#7fd96214", - "editor.wordHighlightBorder": "#73b8ff80", - "editor.wordHighlightStrongBorder": "#7fd96280", - "editor.findMatchBackground": "#6c5980", - "editor.findMatchBorder": "#6c5980", - "editor.findMatchHighlightBackground": "#6c598066", - "editor.findMatchHighlightBorder": "#5f4c7266", - "editor.findRangeHighlightBackground": "#6c598040", - "editor.rangeHighlightBackground": "#6c598033", - "editor.lineHighlightBackground": "#131721", - "editorLink.activeForeground": "#e6b450", - "editorWhitespace.foreground": "#6c738099", - "editorIndentGuide.background": "#6c738033", - "editorIndentGuide.activeBackground": "#6c738080", - "editorRuler.foreground": "#6c738033", - "editorCodeLens.foreground": "#acb6bf8c", - "editorBracketMatch.background": "#6c73804d", - "editorBracketMatch.border": "#6c73804d", - "editor.snippetTabstopHighlightBackground": "#7fd96233", - "editorOverviewRuler.border": "#11151c", - "editorOverviewRuler.modifiedForeground": "#73b8ff", - "editorOverviewRuler.addedForeground": "#7fd962", - "editorOverviewRuler.deletedForeground": "#f26d78", - "editorOverviewRuler.errorForeground": "#d95757", - "editorOverviewRuler.warningForeground": "#e6b450", - "editorOverviewRuler.bracketMatchForeground": "#6c7380b3", - "editorOverviewRuler.wordHighlightForeground": "#73b8ff66", - "editorOverviewRuler.wordHighlightStrongForeground": "#7fd96266", - "editorOverviewRuler.findMatchForeground": "#6c5980", - "editorError.foreground": "#d95757", - "editorWarning.foreground": "#e6b450", - "editorGutter.modifiedBackground": "#73b8ffcc", - "editorGutter.addedBackground": "#7fd962cc", - "editorGutter.deletedBackground": "#f26d78cc", - "diffEditor.insertedTextBackground": "#7fd9621f", - "diffEditor.removedTextBackground": "#f26d781f", - "diffEditor.diagonalFill": "#11151c", - "editorWidget.background": "#0f131a", - "editorWidget.border": "#11151c", - "editorHoverWidget.background": "#0f131a", - "editorHoverWidget.border": "#11151c", - "editorSuggestWidget.background": "#0f131a", - "editorSuggestWidget.border": "#11151c", - "editorSuggestWidget.highlightForeground": "#e6b450", - "editorSuggestWidget.selectedBackground": "#47526640", - "debugExceptionWidget.border": "#11151c", - "debugExceptionWidget.background": "#0f131a", - "editorMarkerNavigation.background": "#0f131a", - "peekView.border": "#47526640", - "peekViewTitle.background": "#47526640", - "peekViewTitleDescription.foreground": "#565b66", - "peekViewTitleLabel.foreground": "#bfbdb6", - "peekViewEditor.background": "#0f131a", - "peekViewEditor.matchHighlightBackground": "#6c598066", - "peekViewEditor.matchHighlightBorder": "#5f4c7266", - "peekViewResult.background": "#0f131a", - "peekViewResult.fileForeground": "#bfbdb6", - "peekViewResult.lineForeground": "#565b66", - "peekViewResult.matchHighlightBackground": "#6c598066", - "peekViewResult.selectionBackground": "#47526640", - "panel.background": "#0b0e14", - "panel.border": "#11151c", - "panelTitle.activeBorder": "#e6b450", - "panelTitle.activeForeground": "#bfbdb6", - "panelTitle.inactiveForeground": "#565b66", - "statusBar.background": "#0b0e14", - "statusBar.foreground": "#565b66", - "statusBar.border": "#0b0e14", - "statusBar.debuggingBackground": "#f29668", - "statusBar.debuggingForeground": "#0d1017", - "statusBar.noFolderBackground": "#0f131a", - "statusBarItem.activeBackground": "#565b6633", - "statusBarItem.hoverBackground": "#565b6633", - "statusBarItem.prominentBackground": "#11151c", - "statusBarItem.prominentHoverBackground": "#00000030", - "statusBarItem.remoteBackground": "#e6b450", - "statusBarItem.remoteForeground": "#0d1017", - "titleBar.activeBackground": "#0b0e14", - "titleBar.activeForeground": "#bfbdb6", - "titleBar.inactiveBackground": "#0b0e14", - "titleBar.inactiveForeground": "#565b66", - "titleBar.border": "#0b0e14", - "extensionButton.prominentForeground": "#0d1017", - "extensionButton.prominentBackground": "#e6b450", - "extensionButton.prominentHoverBackground": "#e1af4b", - "pickerGroup.border": "#11151c", - "pickerGroup.foreground": "#565b6680", - "debugToolBar.background": "#0f131a", - "debugIcon.breakpointForeground": "#f29668", - "debugIcon.breakpointDisabledForeground": "#f2966880", - "debugConsoleInputIcon.foreground": "#e6b450", - "welcomePage.tileBackground": "#0b0e14", - "welcomePage.tileShadow": "#00000080", - "welcomePage.progress.background": "#131721", - "welcomePage.buttonBackground": "#e6b45066", - "walkThrough.embeddedEditorBackground": "#0f131a", - "gitDecoration.modifiedResourceForeground": "#73b8ffb3", - "gitDecoration.deletedResourceForeground": "#f26d78b3", - "gitDecoration.untrackedResourceForeground": "#7fd962b3", - "gitDecoration.ignoredResourceForeground": "#565b6680", - "gitDecoration.conflictingResourceForeground": "", - "gitDecoration.submoduleResourceForeground": "#d2a6ffb3", - "settings.headerForeground": "#bfbdb6", - "settings.modifiedItemIndicator": "#73b8ff", - "keybindingLabel.background": "#565b661a", - "keybindingLabel.foreground": "#bfbdb6", - "keybindingLabel.border": "#bfbdb61a", - "keybindingLabel.bottomBorder": "#bfbdb61a", - "terminal.background": "#0b0e14", - "terminal.foreground": "#bfbdb6", - "terminal.ansiBlack": "#11151c", - "terminal.ansiRed": "#ea6c73", - "terminal.ansiGreen": "#7fd962", - "terminal.ansiYellow": "#f9af4f", - "terminal.ansiBlue": "#53bdfa", - "terminal.ansiMagenta": "#cda1fa", - "terminal.ansiCyan": "#90e1c6", - "terminal.ansiWhite": "#c7c7c7", - "terminal.ansiBrightBlack": "#686868", - "terminal.ansiBrightRed": "#f07178", - "terminal.ansiBrightGreen": "#aad94c", - "terminal.ansiBrightYellow": "#ffb454", - "terminal.ansiBrightBlue": "#59c2ff", - "terminal.ansiBrightMagenta": "#d2a6ff", - "terminal.ansiBrightCyan": "#95e6cb", - "terminal.ansiBrightWhite": "#ffffff", - }, - rules: [ - { - fontStyle: "italic", - foreground: "#acb6bf8c", - token: "comment", - }, - { - foreground: RADIATE, - token: "string", - }, - { - foreground: ELECTRON, - token: "number", - }, - { - foreground: STARLIGHT, - token: "identifier", - }, - { - foreground: SUN, - token: "keyword", - }, - { - foreground: PROTON, - token: "tag", - }, - { - foreground: ASTEROID, - token: "delimiter", - }, - ], - encodedTokensColors: [], - }); - }, [monaco]); - - return null; -} diff --git a/playground/src/Editor/PrimarySideBar.tsx b/playground/src/Editor/PrimarySideBar.tsx new file mode 100644 index 0000000000..f4be0292d6 --- /dev/null +++ b/playground/src/Editor/PrimarySideBar.tsx @@ -0,0 +1,34 @@ +import { FileIcon, SettingsIcon } from "./Icons"; +import SideBar, { SideBarEntry } from "./SideBar"; + +type Tool = "Settings" | "Source"; + +type SideBarProps = { + selected: Tool; + onSelectTool(tool: Tool): void; +}; + +export default function PrimarySideBar({ + selected, + onSelectTool, +}: SideBarProps) { + return ( + + onSelectTool("Source")} + selected={selected == "Source"} + > + + + + onSelectTool("Settings")} + selected={selected == "Settings"} + > + + + + ); +} diff --git a/playground/src/Editor/ResizeHandle.tsx b/playground/src/Editor/ResizeHandle.tsx new file mode 100644 index 0000000000..6c983b93a2 --- /dev/null +++ b/playground/src/Editor/ResizeHandle.tsx @@ -0,0 +1,13 @@ +import { PanelResizeHandle } from "react-resizable-panels"; + +export function HorizontalResizeHandle() { + return ( + + ); +} + +export function VerticalResizeHandle() { + return ( + + ); +} diff --git a/playground/src/Editor/SecondaryPanel.tsx b/playground/src/Editor/SecondaryPanel.tsx new file mode 100644 index 0000000000..c4d2b11d3d --- /dev/null +++ b/playground/src/Editor/SecondaryPanel.tsx @@ -0,0 +1,83 @@ +import Editor from "@monaco-editor/react"; +import { Theme } from "./theme"; + +export type SecondaryTool = "Format" | "AST" | "Tokens" | "FIR"; + +export type SecondaryPanelResult = + | null + | { status: "ok"; content: string } + | { status: "error"; error: string }; + +export type SecondaryPanelProps = { + tool: SecondaryTool; + result: SecondaryPanelResult; + theme: Theme; +}; + +export default function SecondaryPanel({ + tool, + result, + theme, +}: SecondaryPanelProps) { + return ( +
+
+ +
+
+ ); +} + +function Content({ + tool, + result, + theme, +}: { + tool: SecondaryTool; + result: SecondaryPanelResult; + theme: Theme; +}) { + if (result == null) { + return ""; + } else { + let language; + switch (result.status) { + case "ok": + switch (tool) { + case "Format": + language = "python"; + break; + + case "AST": + language = "RustPythonAst"; + break; + + case "Tokens": + language = "RustPythonTokens"; + break; + + case "FIR": + language = "fir"; + break; + } + + return ( + + ); + case "error": + return {result.error}; + } + } +} diff --git a/playground/src/Editor/SecondarySideBar.tsx b/playground/src/Editor/SecondarySideBar.tsx new file mode 100644 index 0000000000..1abe6db6a3 --- /dev/null +++ b/playground/src/Editor/SecondarySideBar.tsx @@ -0,0 +1,49 @@ +import SideBar, { SideBarEntry } from "./SideBar"; +import { FormatIcon, StructureIcon, TokensIcon } from "./Icons"; +import { SecondaryTool } from "./SecondaryPanel"; + +interface RightSideBarProps { + selected: SecondaryTool | null; + onSelected(tool: SecondaryTool): void; +} + +export default function SecondarySideBar({ + selected, + onSelected, +}: RightSideBarProps) { + return ( + + onSelected("Format")} + > + + + + onSelected("AST")} + > + + + + onSelected("Tokens")} + > + + + + onSelected("FIR")} + > + FIR + + + ); +} diff --git a/playground/src/Editor/SettingsEditor.tsx b/playground/src/Editor/SettingsEditor.tsx index a754fa00f8..78406ec67f 100644 --- a/playground/src/Editor/SettingsEditor.tsx +++ b/playground/src/Editor/SettingsEditor.tsx @@ -46,6 +46,7 @@ export default function SettingsEditor({ fontSize: 14, roundedSelection: false, scrollBeyondLastLine: false, + contextmenu: false, }} wrapperProps={visible ? {} : { style: { display: "none" } }} language={"json"} diff --git a/playground/src/Editor/SideBar.tsx b/playground/src/Editor/SideBar.tsx index 2d40a485d9..db948d1fb3 100644 --- a/playground/src/Editor/SideBar.tsx +++ b/playground/src/Editor/SideBar.tsx @@ -1,43 +1,32 @@ -import { FileIcon, SettingsIcon } from "./Icons"; import { type ReactNode } from "react"; - -type Tool = "Settings" | "Source"; +import classNames from "classnames"; type SideBarProps = { - selected: Tool; - onSelectTool(tool: Tool): void; + children: ReactNode; + position: "right" | "left"; }; -export default function SideBar({ selected, onSelectTool }: SideBarProps) { +export default function SideBar({ children, position }: SideBarProps) { return ( -
    - onSelectTool("Source")} - selected={selected == "Source"} - > - - - - onSelectTool("Settings")} - selected={selected == "Settings"} - > - - +
      + {children}
    ); } -interface SideBarEntryProps { +export interface SideBarEntryProps { title: string; selected: boolean; - onClick?(): void; children: ReactNode; + onClick?(): void; } -function SideBarEntry({ +export function SideBarEntry({ title, onClick, children, @@ -49,12 +38,12 @@ function SideBarEntry({ onClick={onClick} role="button" className={`py-4 px-2 relative flex items-center flex-col ${ - selected ? "fill-white" : "fill-slate-500" + selected ? "fill-white text-white" : "fill-slate-500 text-slate-500" }`} > {children} {selected && ( - + )} ); diff --git a/playground/src/Editor/SourceEditor.tsx b/playground/src/Editor/SourceEditor.tsx index 492b0836cc..33b9b48473 100644 --- a/playground/src/Editor/SourceEditor.tsx +++ b/playground/src/Editor/SourceEditor.tsx @@ -2,9 +2,9 @@ * Editor for the Python source code. */ -import Editor, { useMonaco } from "@monaco-editor/react"; +import Editor, { BeforeMount, Monaco } from "@monaco-editor/react"; import { MarkerSeverity, MarkerTag } from "monaco-editor"; -import { useCallback, useEffect } from "react"; +import { useCallback, useEffect, useRef } from "react"; import { Diagnostic } from "../pkg"; import { Theme } from "./theme"; @@ -21,7 +21,8 @@ export default function SourceEditor({ theme: Theme; onChange: (pythonSource: string) => void; }) { - const monaco = useMonaco(); + const monacoRef = useRef(null); + const monaco = monacoRef.current; useEffect(() => { const editor = monaco?.editor; @@ -98,14 +99,21 @@ export default function SourceEditor({ [onChange], ); + const handleMount: BeforeMount = useCallback( + (instance) => (monacoRef.current = instance), + [], + ); + return ( ]/, "@brackets"], + + // Strings + [/"/, { token: "string.quote", bracket: "@open", next: "@string" }], + + [/\d+/, "number"], + ], + string: [ + [/[^\\"]+/, "string"], + [/"/, { token: "string.quote", bracket: "@close", next: "@pop" }], + ], + }, + brackets: [ + { + open: "(", + close: ")", + token: "delimiter.parenthesis", + }, + { + open: "[", + close: "]", + token: "delimiter.bracket", + }, + { + open: "<", + close: ">", + token: "delimiter.angle", + }, + ], + }); +} diff --git a/playground/src/main.tsx b/playground/src/main.tsx index 62fd07ae28..d62cb07f57 100644 --- a/playground/src/main.tsx +++ b/playground/src/main.tsx @@ -2,6 +2,10 @@ import React from "react"; import ReactDOM from "react-dom/client"; import Editor from "./Editor"; import "./index.css"; +import { loader } from "@monaco-editor/react"; +import { setupMonaco } from "./Editor/setupMonaco"; + +loader.init().then(setupMonaco); ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(