Skip to content

Commit c323187

Browse files
committed
refactor(formatter): Split oxc_formatter and oxc_formatter_core (#22611)
Split `oxc_formatter` into: - `oxc_formatter(_js)`: JS/TS specific formatter, comment handling, sortImports, jsdoc, etc - `oxc_formatter_core`: Language agnostic IR, printer, etc The naming convention is different from Biome or Ruff (they do not have `_core`, and `_formatter` is equivalent to the core), but this is because in Oxc, items without `_js` are the default. As a basic task, I have added `Context` as a generic to all relevant parts to ensure support for multiple languages. There might still be other components that could be refactored into the core, but I would like to finish them as I implement support for other languages. Also moved some shared utilities to the core in advance.
1 parent 9e496a7 commit c323187

151 files changed

Lines changed: 5362 additions & 6760 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.lock

Lines changed: 17 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ oxc_traverse = { version = "0.133.0", path = "crates/oxc_traverse" } # AST trave
143143

144144
# publish = false
145145
oxc_formatter = { path = "crates/oxc_formatter" } # Code formatting
146+
oxc_formatter_core = { path = "crates/oxc_formatter_core" } # Language-agnostic formatter core
146147
oxc_language_server = { path = "crates/oxc_language_server", default-features = false } # Language server
147148
oxc_linter = { path = "crates/oxc_linter" } # Linting engine
148149
oxc_macros = { path = "crates/oxc_macros" } # Proc macros

apps/oxfmt/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ oxc_config = { workspace = true }
3333
oxc_data_structures = { workspace = true, features = ["rope"] }
3434
oxc_diagnostics = { workspace = true }
3535
oxc_formatter = { workspace = true }
36+
oxc_formatter_core = { workspace = true }
3637
oxc_language_server = { workspace = true }
3738
oxc_napi = { workspace = true }
3839
oxc_parser = { workspace = true }

apps/oxfmt/src/api/text_to_doc_api.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use tracing::{debug, instrument};
77
use oxc_allocator::Allocator;
88
use oxc_ast::ast::*;
99
use oxc_formatter::{
10-
AstNode, AstNodes, FormatOptions, FormatVueBindingParams, FormatVueScriptGeneric, Formatter,
10+
AstNode, AstNodes, FormatVueBindingParams, FormatVueScriptGeneric, Formatter, JsFormatOptions,
1111
enable_jsx_source_type, get_parse_options,
1212
};
1313
use oxc_parser::{Parser, ParserReturn};
@@ -197,7 +197,7 @@ fn run_fragment(
197197

198198
let formatter = Formatter::new(
199199
&allocator,
200-
FormatOptions {
200+
JsFormatOptions {
201201
// TODO: Fragments inside of Vue attributes should always use single quotes,
202202
// since double quotes are used for the attribute value itself.
203203
//

apps/oxfmt/src/core/config/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use tracing::instrument;
2121

2222
use oxc_config::{ConfigDiscovery, ConfigFileNames, DiscoveredConfigFile, is_js_config_path};
2323
#[cfg(feature = "napi")]
24-
use oxc_formatter::FormatOptions;
24+
use oxc_formatter::JsFormatOptions;
2525

2626
use self::{
2727
editorconfig::{apply_editorconfig, has_editorconfig_overrides, load_editorconfig},
@@ -151,7 +151,7 @@ pub fn resolve_for_api(
151151
#[cfg(feature = "napi")]
152152
#[derive(Debug)]
153153
pub struct EmbeddedCallbackResolved {
154-
pub format_options: Box<FormatOptions>,
154+
pub format_options: Box<JsFormatOptions>,
155155
/// Retained so nested embedded callbacks can derive Prettier options on demand.
156156
/// (e.g., CSS-in-JS inside the embedded JS)
157157
pub config: Box<FormatConfig>,

apps/oxfmt/src/core/external_formatter.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use serde_json::Value;
1010
use tracing::{debug, debug_span};
1111

1212
use oxc_formatter::{
13-
EmbeddedDocFormatterCallback, EmbeddedFormatterCallback, ExternalCallbacks, FormatOptions,
13+
EmbeddedDocFormatterCallback, EmbeddedFormatterCallback, ExternalCallbacks, JsFormatOptions,
1414
TailwindCallback,
1515
};
1616

@@ -241,7 +241,7 @@ impl ExternalFormatter {
241241
/// The options (including `filepath`) are captured in the closures and passed to JS on each call.
242242
pub fn to_external_callbacks(
243243
&self,
244-
format_options: &FormatOptions,
244+
format_options: &JsFormatOptions,
245245
options: Value,
246246
) -> ExternalCallbacks {
247247
let needs_embedded = !format_options.embedded_language_formatting.is_off();

apps/oxfmt/src/core/format.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use tracing::instrument;
66

77
use oxc_allocator::AllocatorPool;
88
use oxc_diagnostics::OxcDiagnostic;
9-
use oxc_formatter::{FormatOptions, Formatter, enable_jsx_source_type, get_parse_options};
9+
use oxc_formatter::{Formatter, JsFormatOptions, enable_jsx_source_type, get_parse_options};
1010
use oxc_parser::Parser;
1111
use oxc_span::SourceType;
1212
use oxc_toml::Options as TomlFormatterOptions;
@@ -37,7 +37,7 @@ pub enum FormatStrategy {
3737
OxcFormatter {
3838
path: Arc<Path>,
3939
source_type: SourceType,
40-
format_options: Box<FormatOptions>,
40+
format_options: Box<JsFormatOptions>,
4141
#[cfg(feature = "napi")]
4242
config: Box<FormatConfig>,
4343
insert_final_newline: bool,
@@ -92,7 +92,7 @@ impl FormatStrategy {
9292
/// and `Box<FormatConfig>` is materially smaller per file than a fully-built `Value`.
9393
///
9494
/// # Errors
95-
/// Returns `Err` if the kind needs `FormatOptions`/`TomlFormatterOptions`
95+
/// Returns `Err` if the kind needs `JsFormatOptions`/`TomlFormatterOptions`
9696
/// and the config fails validation.
9797
// `config` is moved into the napi-only `ExternalFormatter*` variants;
9898
// when the `napi` feature is off, those branches are cfg-gated out and the
@@ -268,7 +268,7 @@ impl SourceFormatter {
268268
source_text: &str,
269269
path: &Path,
270270
source_type: SourceType,
271-
format_options: FormatOptions,
271+
format_options: JsFormatOptions,
272272
#[cfg(feature = "napi")] config: &FormatConfig,
273273
) -> Result<String, OxcDiagnostic> {
274274
let source_type = enable_jsx_source_type(source_type);
@@ -344,7 +344,7 @@ impl SourceFormatter {
344344
/// to fire based on user config.
345345
fn build_external_callbacks(
346346
&self,
347-
format_options: &FormatOptions,
347+
format_options: &JsFormatOptions,
348348
config: &FormatConfig,
349349
path: &Path,
350350
) -> oxc_formatter::ExternalCallbacks {

apps/oxfmt/src/core/options/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Converters from typed [`super::oxfmtrc::FormatConfig`] to each downstream
22
//! consumer's options shape.
33
//!
4-
//! - [`to_oxc_formatter()`]: `oxc_formatter::FormatOptions` for JS/TS formatting
4+
//! - [`to_oxc_formatter()`]: `oxc_formatter::JsFormatOptions` for JS/TS formatting
55
//! - [`to_toml_formatter()`]: `oxc_toml::Options` for TOML formatting
66
//! - `to_prettier`: Prettier-compatible JSON, plus `inject_*` helpers for
77
//! layering in `parser` / `filepath` / plugin payloads at the format step

apps/oxfmt/src/core/options/to_oxc_formatter.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ use rustc_hash::FxHashSet;
22

33
use oxc_formatter::{
44
ArrowParentheses, AttributePosition, BracketSameLine, BracketSpacing, CustomGroupDefinition,
5-
EmbeddedLanguageFormatting, Expand, FormatOptions, GroupEntry, ImportModifier, ImportSelector,
6-
IndentStyle, IndentWidth, LineEnding, LineWidth, QuoteProperties, QuoteStyle, Semicolons,
7-
SortImportsOptions, SortOrder, SortTailwindcssOptions, TrailingCommas,
5+
EmbeddedLanguageFormatting, Expand, GroupEntry, ImportModifier, ImportSelector,
6+
JsFormatOptions, QuoteProperties, QuoteStyle, Semicolons, SortImportsOptions, SortOrder,
7+
SortTailwindcssOptions, TrailingCommas,
88
};
9+
use oxc_formatter_core::{IndentStyle, IndentWidth, LineEnding, LineWidth};
910

1011
use super::super::oxfmtrc::{
1112
ArrowParensConfig, CustomGroupItemConfig, EmbeddedLanguageFormattingConfig, EndOfLineConfig,
@@ -14,18 +15,18 @@ use super::super::oxfmtrc::{
1415
SortTailwindcssUserConfig, TrailingCommaConfig,
1516
};
1617

17-
/// Convert `FormatConfig` into validated `FormatOptions` for `oxc_formatter`.
18+
/// Convert `FormatConfig` into validated `JsFormatOptions` for `oxc_formatter`.
1819
///
1920
/// # Errors
2021
/// Returns error if any option value is invalid
21-
pub fn to_oxc_formatter(config: &FormatConfig) -> Result<FormatOptions, String> {
22+
pub fn to_oxc_formatter(config: &FormatConfig) -> Result<JsFormatOptions, String> {
2223
// NOTE: Not yet supported options:
2324
// [Prettier] experimentalOperatorPosition: "start" | "end"
2425
// [Prettier] experimentalTernaries: boolean
2526
// These are rejected at deserialize time so they never reach here.
2627

27-
// All values are based on defaults from `FormatOptions::default()`
28-
let mut format_options = FormatOptions::default();
28+
// All values are based on defaults from `JsFormatOptions::default()`
29+
let mut format_options = JsFormatOptions::default();
2930

3031
// [Prettier] useTabs: boolean
3132
if let Some(use_tabs) = config.use_tabs {

apps/oxfmt/src/core/options/to_prettier.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::path::Path;
44
use serde::Serialize;
55
use serde_json::Value;
66

7-
use oxc_formatter::FormatOptions;
7+
use oxc_formatter::JsFormatOptions;
88

99
use super::super::oxfmtrc::{
1010
ArrowParensConfig, EmbeddedLanguageFormattingConfig, EndOfLineConfig, FormatConfig,
@@ -31,7 +31,7 @@ pub fn to_prettier(config: &FormatConfig) -> Value {
3131
// / `ResolvedFormatConfig`) instead of the JS formatter's typed enum once available.
3232
obj.insert(
3333
"printWidth".to_string(),
34-
Value::from(config.print_width.unwrap_or(FormatOptions::default().line_width.value())),
34+
Value::from(config.print_width.unwrap_or(JsFormatOptions::default().line_width.value())),
3535
);
3636

3737
// Other Prettier core options share defaults with oxfmt,

0 commit comments

Comments
 (0)