feat(rome_json_formatter): Enable JSON formatting for debug builds
MODIFY
Cargo.lock
Cargo.lock
@@ -1910,6 +1910,7 @@ dependencies = [
"rome_js_parser",
"rome_js_semantic",
"rome_js_syntax",
+ "rome_json_formatter",
"rome_json_parser",
"rome_json_syntax",
"rome_parser",

MODIFY
crates/rome_cli/src/execute.rs
crates/rome_cli/src/execute.rs
@@ -95,15 +95,15 @@ impl Execution {
}
}
- pub(crate) fn is_ci(&self) -> bool {
+ pub(crate) const fn is_ci(&self) -> bool {
matches!(self.traversal_mode, TraversalMode::CI { .. })
}
- pub(crate) fn is_check(&self) -> bool {
+ pub(crate) const fn is_check(&self) -> bool {
matches!(self.traversal_mode, TraversalMode::Check { .. })
}
- pub(crate) fn is_format(&self) -> bool {
+ pub(crate) const fn is_format(&self) -> bool {
matches!(self.traversal_mode, TraversalMode::Format { .. })
}

MODIFY
crates/rome_cli/tests/snapshots/main_commands_format/does_not_format_ignored_directories.snap
crates/rome_cli/tests/snapshots/main_commands_format/does_not_format_ignored_directories.snap
@@ -29,7 +29,7 @@ statement();
# Emitted Messages
```block
-Formatted 1 file(s) in <TIME>
+Formatted 2 file(s) in <TIME>
```

MODIFY
crates/rome_json_formatter/src/lib.rs
crates/rome_json_formatter/src/lib.rs
@@ -26,6 +26,8 @@ where
N: AstNode<Language = JsonLanguage>,
{
fn fmt(&self, node: &N, f: &mut JsonFormatter) -> FormatResult<()> {
+ f.comments().mark_suppression_checked(node.syntax());
+
self.fmt_fields(node, f)
}

MODIFY
crates/rome_json_parser/src/syntax.rs
crates/rome_json_parser/src/syntax.rs
@@ -311,7 +311,6 @@ fn parse_rest(p: &mut JsonParser, value: ParsedSyntax) {
let range = match parse_value(p) {
Present(value) => value.range(p),
Absent => ParseRecovery::new(JSON_BOGUS_VALUE, VALUE_START)
- .enable_recovery_on_line_break()
.recover(p)
.expect("Expect recovery to succeed because parser isn't at EOF nor at a value.")
.range(p),

MODIFY
crates/rome_service/Cargo.toml
crates/rome_service/Cargo.toml
@@ -26,6 +26,7 @@ rome_js_formatter = { path = "../rome_js_formatter", features = ["serde"] }
rome_js_semantic = { path = "../rome_js_semantic" }
rome_json_parser = { path = "../rome_json_parser" }
rome_json_syntax = { path = "../rome_json_syntax" }
+rome_json_formatter = { path = "../rome_json_formatter" }
rome_rowan = { path = "../rome_rowan", features = ["serde"] }
rome_text_edit = { path = "../rome_text_edit" }
indexmap = { workspace = true, features = ["serde"] }

MODIFY
crates/rome_service/src/file_handlers/javascript.rs
crates/rome_service/src/file_handlers/javascript.rs
@@ -53,8 +53,8 @@ pub struct JsLinterSettings {
impl Language for JsLanguage {
type FormatterSettings = JsFormatterSettings;
- type FormatOptions = JsFormatOptions;
type LinterSettings = JsLinterSettings;
+ type FormatOptions = JsFormatOptions;
fn lookup_settings(languages: &LanguagesSettings) -> &LanguageSettings<Self> {
&languages.javascript

MODIFY
crates/rome_service/src/file_handlers/json.rs
crates/rome_service/src/file_handlers/json.rs
use super::{ExtensionHandler, Mime};
-use crate::file_handlers::{Capabilities, ParserCapabilities};
+use crate::file_handlers::{Capabilities, FormatterCapabilities, ParserCapabilities};
use crate::file_handlers::{DebugCapabilities, Language as LanguageId};
+use crate::settings::{
+ FormatSettings, Language, LanguageSettings, LanguagesSettings, SettingsHandle,
+};
use crate::workspace::server::AnyParse;
use crate::workspace::GetSyntaxTreeResult;
+use crate::RomeError;
+#[cfg(debug_assertions)]
+use rome_formatter::FormatError;
+use rome_formatter::Printed;
use rome_fs::RomePath;
+use rome_json_formatter::context::JsonFormatOptions;
+use rome_json_formatter::format_node;
use rome_json_parser::JsonParse;
-use rome_json_syntax::{JsonRoot, JsonSyntaxNode};
+use rome_json_syntax::{JsonLanguage, JsonRoot, JsonSyntaxNode};
+#[cfg(debug_assertions)]
+use rome_rowan::{TextRange, TextSize, TokenAtOffset};
+use tracing::debug;
+
+impl Language for JsonLanguage {
+ type FormatterSettings = ();
+ type LinterSettings = ();
+ type FormatOptions = JsonFormatOptions;
+
+ fn lookup_settings(language: &LanguagesSettings) -> &LanguageSettings<Self> {
+ &language.json
+ }
+
+ fn resolve_format_options(
+ global: &FormatSettings,
+ _language: &Self::FormatterSettings,
+ _path: &RomePath,
+ ) -> Self::FormatOptions {
+ JsonFormatOptions::default()
+ .with_indent_style(global.indent_style.unwrap_or_default())
+ .with_line_width(global.line_width.unwrap_or_default())
+ }
+}
#[derive(Debug, Default, PartialEq, Eq)]
pub(crate) struct JsonFileHandler;
@@ -29,12 +61,26 @@ impl ExtensionHandler for JsonFileHandler {
debug: DebugCapabilities {
debug_syntax_tree: Some(debug_syntax_tree),
debug_control_flow: None,
- debug_formatter_ir: None,
+ debug_formatter_ir: Some(debug_formatter_ir),
},
analyzer: Default::default(),
- formatter: Default::default(),
+ formatter: formatter_capabilities(),
+ }
+ }
+}
+
+#[cfg(debug_assertions)]
+fn formatter_capabilities() -> FormatterCapabilities {
+ FormatterCapabilities {
+ format: Some(format),
+ format_range: Some(format_range),
+ format_on_type: Some(format_on_type),
}
}
+
+#[cfg(not(debug_assertions))]
+fn formatter_capabilities() -> FormatterCapabilities {
+ FormatterCapabilities::default()
}
fn parse(rome_path: &RomePath, _: LanguageId, text: &str) -> AnyParse {
@@ -53,6 +99,90 @@ fn debug_syntax_tree(_rome_path: &RomePath, parse: AnyParse) -> GetSyntaxTreeRes
}
}
+fn debug_formatter_ir(
+ rome_path: &RomePath,
+ parse: AnyParse,
+ settings: SettingsHandle,
+) -> Result<String, RomeError> {
+ let options = settings.format_options::<JsonLanguage>(rome_path);
+
+ let tree = parse.syntax();
+ let formatted = format_node(options, &tree)?;
+
+ let root_element = formatted.into_document();
+ Ok(root_element.to_string())
+}
+
+#[tracing::instrument(level = "debug", skip(parse))]
+fn format(
+ rome_path: &RomePath,
+ parse: AnyParse,
+ settings: SettingsHandle,
+) -> Result<Printed, RomeError> {
+ let options = settings.format_options::<JsonLanguage>(rome_path);
+
+ debug!("Format with the following options: \n{}", options);
+
+ let tree = parse.syntax();
+ let formatted = format_node(options, &tree)?;
+
+ match formatted.print() {
+ Ok(printed) => Ok(printed),
+ Err(error) => Err(RomeError::FormatError(error.into())),
+ }
+}
+
+#[cfg(debug_assertions)]
+fn format_range(
+ rome_path: &RomePath,
+ parse: AnyParse,
+ settings: SettingsHandle,
+ range: TextRange,
+) -> Result<Printed, RomeError> {
+ let options = settings.format_options::<JsonLanguage>(rome_path);
+
+ let tree = parse.syntax();
+ let printed = rome_json_formatter::format_range(options, &tree, range)?;
+ Ok(printed)
+}
+
+#[cfg(debug_assertions)]
+fn format_on_type(
+ rome_path: &RomePath,
+ parse: AnyParse,
+ settings: SettingsHandle,
+ offset: TextSize,
+) -> Result<Printed, RomeError> {
+ let options = settings.format_options::<JsonLanguage>(rome_path);
+
+ let tree = parse.syntax();
+
+ let range = tree.text_range();
+ if offset < range.start() || offset > range.end() {
+ return Err(RomeError::FormatError(FormatError::RangeError {
+ input: TextRange::at(offset, TextSize::from(0)),
+ tree: range,
+ }));
+ }
+
+ let token = match tree.token_at_offset(offset) {
+ // File is empty, do nothing
+ TokenAtOffset::None => panic!("empty file"),
+ TokenAtOffset::Single(token) => token,
+ // The cursor should be right after the closing character that was just typed,
+ // select the previous token as the correct one
+ TokenAtOffset::Between(token, _) => token,
+ };
+
+ let root_node = match token.parent() {
+ Some(node) => node,
+ None => panic!("found a token with no parent"),
+ };
+
+ let printed = rome_json_formatter::format_sub_tree(options, &root_node)?;
+ Ok(printed)
+}
+
impl From<JsonParse> for AnyParse {
fn from(parse: JsonParse) -> Self {
let root = parse.syntax();

MODIFY
crates/rome_service/src/settings.rs
crates/rome_service/src/settings.rs
@@ -7,6 +7,7 @@ use rome_diagnostics::Category;
use rome_formatter::{IndentStyle, LineWidth};
use rome_fs::RomePath;
use rome_js_syntax::JsLanguage;
+use rome_json_syntax::JsonLanguage;
use std::{
num::NonZeroU64,
sync::{RwLock, RwLockReadGuard},
@@ -152,6 +153,7 @@ impl Default for LinterSettings {
#[derive(Debug, Default)]
pub struct LanguagesSettings {
pub javascript: LanguageSettings<JsLanguage>,
+ pub json: LanguageSettings<JsonLanguage>,
}
pub trait Language: rome_rowan::Language {

MODIFY
crates/rome_service/src/workspace/server.rs
crates/rome_service/src/workspace/server.rs
@@ -15,7 +15,7 @@ use crate::{
use dashmap::{mapref::entry::Entry, DashMap};
use indexmap::IndexSet;
use rome_analyze::{AnalysisFilter, RuleFilter};
-use rome_diagnostics::{serde::Diagnostic, DiagnosticExt};
+use rome_diagnostics::{serde::Diagnostic as SerdeDiagnostic, Diagnostic, DiagnosticExt, Severity};
use rome_formatter::Printed;
use rome_fs::RomePath;
use rome_parser::diagnostic::ParseDiagnostic;
@@ -84,8 +84,11 @@ impl AnyParse {
}
/// This function transforms diagnostics coming from the parser into serializable diagnostics
- pub(crate) fn into_diagnostics(self) -> Vec<Diagnostic> {
- self.diagnostics.into_iter().map(Diagnostic::new).collect()
+ pub(crate) fn into_diagnostics(self) -> Vec<SerdeDiagnostic> {
+ self.diagnostics
+ .into_iter()
+ .map(SerdeDiagnostic::new)
+ .collect()
}
fn has_errors(&self) -> bool {
@@ -372,19 +375,18 @@ impl Workspace for WorkspaceServer {
&self,
params: PullDiagnosticsParams,
) -> Result<PullDiagnosticsResult, RomeError> {
- let capabilities = self.get_capabilities(&params.path);
- let lint = capabilities
- .analyzer
- .lint
- .ok_or_else(self.build_capability_error(&params.path))?;
-
- let settings = self.settings.read().unwrap();
let feature = if params.categories.is_syntax() {
FeatureName::Format
} else {
FeatureName::Lint
};
+
let parse = self.get_parse(params.path.clone(), Some(feature))?;
+ let settings = self.settings.read().unwrap();
+
+ let (diagnostics, errors, skipped_diagnostics) = if let Some(lint) =
+ self.get_capabilities(&params.path).analyzer.lint
+ {
let rules = settings.linter().rules.as_ref();
let rule_filter_list = self.build_rule_filter_list(rules);
let mut filter = AnalysisFilter::from_enabled_rules(Some(rule_filter_list.as_slice()));
@@ -399,17 +401,31 @@ impl Workspace for WorkspaceServer {
max_diagnostics: params.max_diagnostics,
});
+ (
+ results.diagnostics,
+ results.errors,
+ results.skipped_diagnostics,
+ )
+ } else {
+ let parse_diagnostics = parse.into_diagnostics();
+ let errors = parse_diagnostics
+ .iter()
+ .filter(|diag| diag.severity() <= Severity::Error)
+ .count();
+
+ (parse_diagnostics, errors, 0)
+ };
+
Ok(PullDiagnosticsResult {
- diagnostics: results
- .diagnostics
+ diagnostics: diagnostics
.into_iter()
.map(|diag| {
let diag = diag.with_file_path(params.path.as_path().display().to_string());
- Diagnostic::new(diag)
+ SerdeDiagnostic::new(diag)
})
.collect(),
- errors: results.errors,
- skipped_diagnostics: results.skipped_diagnostics,
+ errors,
+ skipped_diagnostics,
})
}

feat(rome_json_formatter): Enable JSON formatting for debug builds (#3859)