diff --git a/Cargo.lock b/Cargo.lock index 60c292b8f..01617bb5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2607,6 +2607,7 @@ dependencies = [ "salsa", "serde", "serde-wasm-bindgen", + "squawk-fmt", "squawk-ide", "squawk-lexer", "squawk-line-index", diff --git a/crates/squawk_wasm/Cargo.toml b/crates/squawk_wasm/Cargo.toml index 546e23150..521dfc6b3 100644 --- a/crates/squawk_wasm/Cargo.toml +++ b/crates/squawk_wasm/Cargo.toml @@ -23,6 +23,7 @@ squawk-syntax.workspace = true squawk-linter.workspace = true squawk-lexer.workspace = true squawk-ide.workspace = true +squawk-fmt.workspace = true rowan.workspace = true salsa.workspace = true diff --git a/crates/squawk_wasm/src/lib.rs b/crates/squawk_wasm/src/lib.rs index b49413e5a..971f8b632 100644 --- a/crates/squawk_wasm/src/lib.rs +++ b/crates/squawk_wasm/src/lib.rs @@ -172,6 +172,16 @@ impl SquawkDatabase { Ok(out) } + pub fn format(&self) -> Result { + let file = self.file()?; + let content = file.content(&self.db); + let line_ending = find_newline(content) + .map(|(_, ending)| ending) + .unwrap_or_default(); + let parse = db::parse(&self.db, file); + squawk_fmt::fmt(&parse.tree(), line_ending).map_err(into_error) + } + pub fn lint(&self) -> Result { let file = self.file()?; let content = file.content(&self.db); diff --git a/playground/src/App.tsx b/playground/src/App.tsx index 6ec8d83ac..a6065ca95 100644 --- a/playground/src/App.tsx +++ b/playground/src/App.tsx @@ -6,7 +6,14 @@ import { useEffectEvent, } from "react" import * as monaco from "monaco-editor" -import { LintError, Fix, useDumpCst, useDumpTokens, useErrors } from "./squawk" +import { + LintError, + Fix, + useDumpCst, + useDumpTokens, + useErrors, + useFormat, +} from "./squawk" import { compress, compressToEncodedURIComponent, @@ -28,7 +35,7 @@ import { import { language as pgsqlMonarchLanguage } from "./pgsql" import BUILTINS_SQL from "./builtins.sql?raw" -const modes = ["Lint", "Syntax Tree", "Tokens"] as const +const modes = ["Lint", "Format", "Syntax Tree", "Tokens"] as const const STORAGE_KEY = "playground-history-v3" type Mode = (typeof modes)[number] @@ -210,6 +217,8 @@ export function App() { ) : mode === "Lint" ? ( + ) : mode === "Format" ? ( + ) : mode == null ? null : ( assertNever(mode) )} @@ -251,6 +260,29 @@ function TokenPanel({ text, version }: { text: string; version: number }) { ) } +function FormatPanel({ text, version }: { text: string; version: number }) { + const result = useFormat(text, version) + const value = result.ok ? result.text : result.error + return ( +
+ {result.ok ? null : ( +
+ can't format this file +
+ )} + +
+ ) +} + function Controls({ activeMode, onModeChange, @@ -383,6 +415,12 @@ function registerMonacoProvidersOnce() { pgsqlMonarchLanguage, ) + monaco.languages.register({ id: "pgsql-formatted" }) + const pgsqlFormattedTokenProvider = monaco.languages.setMonarchTokensProvider( + "pgsql-formatted", + pgsqlMonarchLanguage, + ) + monaco.languages.register({ id: "rast" }) const tokenProvider = monaco.languages.setMonarchTokensProvider("rast", { tokenizer: { @@ -510,6 +548,7 @@ function registerMonacoProvidersOnce() { return () => { languageConfig.dispose() pgsqlTokenProvider.dispose() + pgsqlFormattedTokenProvider.dispose() codeActionProvider.dispose() hoverProvider.dispose() definitionProvider.dispose() diff --git a/playground/src/squawk.tsx b/playground/src/squawk.tsx index f09e61e40..138b42b5c 100644 --- a/playground/src/squawk.tsx +++ b/playground/src/squawk.tsx @@ -139,11 +139,21 @@ export function semantic_tokens_legend(): SemanticTokensLegend { return SquawkDatabase.semantic_tokens_legend() } -export function dump_cst(content: string, version: number): string { +type FormatResult = { ok: true; text: string } | { ok: false; error: string } + +function format(content: string, version: number): FormatResult { + try { + return { ok: true, text: getDb(content, version).format() } + } catch (e) { + return { ok: false, error: e instanceof Error ? e.message : String(e) } + } +} + +function dump_cst(content: string, version: number): string { return getDb(content, version).dump_cst() } -export function dump_tokens(content: string, version: number): string { +function dump_tokens(content: string, version: number): string { return getDb(content, version).dump_tokens() } @@ -162,6 +172,11 @@ export function useDumpTokens(text: string, version: number): string { return isReady ? dump_tokens(text, version) : "" } +export function useFormat(text: string, version: number): FormatResult { + const isReady = useWasmStatus() + return isReady ? format(text, version) : { ok: true, text: "" } +} + let isStartingAlready: { promise: Promise; start: number } | null = null