feat(rome_service, rome_cli): linter via configuration
MODIFY
crates/rome_cli/src/commands/check.rs
crates/rome_cli/src/commands/check.rs
+use crate::commands::format::parse_format_options;
use crate::{
traversal::{traverse, TraversalMode},
CliSession, Termination,
};
use rome_diagnostics::MAXIMUM_DISPLAYABLE_DIAGNOSTICS;
+use rome_service::configuration::Configuration;
+use rome_service::load_config;
+use rome_service::load_config::ConfigurationType;
+use rome_service::settings::{LinterSettings, WorkspaceSettings};
+use rome_service::workspace::UpdateSettingsParams;
/// Handler for the "check" command of the Rome CLI
pub(crate) fn check(mut session: CliSession) -> Result<(), Termination> {
- let mode = if session.args.contains("--apply") {
- TraversalMode::Fix
- } else {
+ let configuration = load_config(&session.app.fs, ConfigurationType::Root)?;
+ let mut workspace_settings = WorkspaceSettings::default();
+
let max_diagnostics: Option<u8> = session
.args
.opt_value_from_str("--max-diagnostics")
@@ -30,8 +36,33 @@ pub(crate) fn check(mut session: CliSession) -> Result<(), Termination> {
20
};
- TraversalMode::Check { max_diagnostics }
- };
+ parse_format_options(&mut session, &mut workspace_settings, &configuration)?;
+ parse_linter_options(&mut session, &mut workspace_settings, &configuration)?;
+
+ session
+ .app
+ .workspace
+ .update_settings(UpdateSettingsParams {
+ settings: workspace_settings,
+ })?;
+
+ traverse(
+ TraversalMode::Check {
+ max_diagnostics,
+ should_fix: session.args.contains("--apply"),
+ },
+ session,
+ )
+}
+
+pub(crate) fn parse_linter_options(
+ _session: &mut CliSession,
+ workspace_settings: &mut WorkspaceSettings,
+ configuration: &Option<Configuration>,
+) -> Result<(), Termination> {
+ if let Some(configuration) = configuration {
+ workspace_settings.linter = LinterSettings::from(&configuration.linter);
+ }
- traverse(mode, session)
+ Ok(())
}

MODIFY
crates/rome_cli/src/commands/ci.rs
crates/rome_cli/src/commands/ci.rs
+use crate::commands::check::parse_linter_options;
use crate::{
traversal::{traverse, TraversalMode},
CliSession, Termination,
};
use rome_service::load_config;
use rome_service::load_config::ConfigurationType;
+use rome_service::settings::WorkspaceSettings;
+use rome_service::workspace::UpdateSettingsParams;
use super::format::parse_format_options;
/// Handler for the "ci" command of the Rome CLI
pub(crate) fn ci(mut session: CliSession) -> Result<(), Termination> {
let configuration = load_config(&session.app.fs, ConfigurationType::Root)?;
+ let mut workspace_settings = WorkspaceSettings::default();
+
+ parse_format_options(&mut session, &mut workspace_settings, &configuration)?;
+ parse_linter_options(&mut session, &mut workspace_settings, &configuration)?;
+
+ session
+ .app
+ .workspace
+ .update_settings(UpdateSettingsParams {
+ settings: workspace_settings,
+ })?;
- parse_format_options(&mut session, configuration)?;
traverse(TraversalMode::CI, session)
}

MODIFY
crates/rome_cli/src/commands/format.rs
crates/rome_cli/src/commands/format.rs
@@ -12,17 +12,24 @@ use crate::{
/// Handler for the "format" command of the Rome CLI
pub(crate) fn format(mut session: CliSession) -> Result<(), Termination> {
let configuration = load_config(&session.app.fs, ConfigurationType::Root)?;
+ let mut settings = WorkspaceSettings::default();
if let Some(configuration) = &configuration {
if configuration.is_formatter_disabled() {
return Ok(());
}
}
- parse_format_options(&mut session, configuration)?;
+
+ parse_format_options(&mut session, &mut settings, &configuration)?;
let is_write = session.args.contains("--write");
let ignore_errors = session.args.contains("--skip-errors");
+ session
+ .app
+ .workspace
+ .update_settings(UpdateSettingsParams { settings })?;
+
traverse(
TraversalMode::Format {
ignore_errors,
@@ -36,13 +43,12 @@ pub(crate) fn format(mut session: CliSession) -> Result<(), Termination> {
/// into the workspace settings
pub(crate) fn parse_format_options(
session: &mut CliSession,
- configuration: Option<Configuration>,
+ workspace_settings: &mut WorkspaceSettings,
+ configuration: &Option<Configuration>,
) -> Result<(), Termination> {
- let mut settings = WorkspaceSettings::default();
-
if let Some(configuration) = configuration {
- settings.format = FormatSettings::from(&configuration.formatter);
- settings.languages.javascript.format.quote_style =
+ workspace_settings.format = FormatSettings::from(&configuration.formatter);
+ workspace_settings.languages.javascript.format.quote_style =
Some(configuration.javascript.formatter.quote_style);
}
@@ -64,10 +70,11 @@ pub(crate) fn parse_format_options(
match indent_style {
Some(IndentStyle::Tab) => {
- settings.format.indent_style = Some(IndentStyle::Tab);
+ workspace_settings.format.indent_style = Some(IndentStyle::Tab);
}
Some(IndentStyle::Space(default_size)) => {
- settings.format.indent_style = Some(IndentStyle::Space(size.unwrap_or(default_size)));
+ workspace_settings.format.indent_style =
+ Some(IndentStyle::Space(size.unwrap_or(default_size)));
}
None => {}
}
@@ -81,7 +88,7 @@ pub(crate) fn parse_format_options(
})?;
if let Some(quote_style) = quote_style {
- settings.languages.javascript.format.quote_style = Some(quote_style);
+ workspace_settings.languages.javascript.format.quote_style = Some(quote_style);
}
let line_width = session
@@ -93,13 +100,8 @@ pub(crate) fn parse_format_options(
})?;
if let Some(line_width) = line_width {
- settings.format.line_width = Some(line_width);
+ workspace_settings.format.line_width = Some(line_width);
}
- session
- .app
- .workspace
- .update_settings(UpdateSettingsParams { settings })?;
-
Ok(())
}

MODIFY
crates/rome_cli/src/traversal.rs
crates/rome_cli/src/traversal.rs
@@ -90,14 +90,20 @@ pub(crate) fn traverse(mode: TraversalMode, mut session: CliSession) -> Result<(
let skipped = skipped.load(Ordering::Relaxed);
match mode {
- TraversalMode::Check { .. } | TraversalMode::CI { .. } => {
+ TraversalMode::Check { should_fix, .. } => {
+ if should_fix {
+ console.log(rome_console::markup! {
+ <Info>"Fixed "{count}" files in "{duration}</Info>
+ });
+ } else {
console.log(rome_console::markup! {
<Info>"Checked "{count}" files in "{duration}</Info>
});
}
- TraversalMode::Fix => {
+ }
+ TraversalMode::CI { .. } => {
console.log(rome_console::markup! {
- <Info>"Fixed "{count}" files in "{duration}</Info>
+ <Info>"Checked "{count}" files in "{duration}</Info>
});
}
TraversalMode::Format { write: false, .. } => {
@@ -280,22 +286,39 @@ fn print_messages_to_console(
#[derive(Clone, Copy)]
pub(crate) enum TraversalMode {
- Check { max_diagnostics: u8 },
+ /// This mode is enabled when running the command `rome check`
+ Check {
+ max_diagnostics: u8,
+ /// `true` when running the command `check` with the `--apply` argument
+ should_fix: bool,
+ },
+ /// This mode is enabled when running the command `rome ci`
CI,
- Fix,
+ /// This mode is enabled when running the command `rome format`
Format { ignore_errors: bool, write: bool },
}
impl TraversalMode {
fn get_max_diagnostics(&self) -> Option<u8> {
match self {
- TraversalMode::Check { max_diagnostics } => Some(*max_diagnostics),
+ TraversalMode::Check {
+ max_diagnostics, ..
+ } => Some(*max_diagnostics),
_ => None,
}
}
+ /// `true` only when running the traversal in [TraversalMode::Check] and `should_fix` is `true`
+ fn should_fix(&self) -> bool {
+ if let TraversalMode::Check { should_fix, .. } = self {
+ *should_fix
+ } else {
+ false
+ }
+ }
+
fn is_ci(&self) -> bool {
- matches!(self, TraversalMode::CI)
+ matches!(self, TraversalMode::CI { .. })
}
}
@@ -354,7 +377,7 @@ impl<'ctx, 'app> TraversalContext for TraversalOptions<'ctx, 'app> {
fn can_handle(&self, rome_path: &RomePath) -> bool {
match self.mode {
- TraversalMode::Check { .. } | TraversalMode::Fix => self.can_lint(rome_path),
+ TraversalMode::Check { .. } => self.can_lint(rome_path),
TraversalMode::CI { .. } => self.can_lint(rome_path) || self.can_format(rome_path),
TraversalMode::Format { .. } => self.can_format(rome_path),
}
@@ -428,7 +451,7 @@ fn process_file(ctx: &TraversalOptions, path: &Path, file_id: FileId) -> FileRes
let rome_path = RomePath::new(path, file_id);
let can_format = ctx.can_format(&rome_path);
let can_handle = match ctx.mode {
- TraversalMode::Check { .. } | TraversalMode::Fix => ctx.can_lint(&rome_path),
+ TraversalMode::Check { .. } => ctx.can_lint(&rome_path),
TraversalMode::CI { .. } => ctx.can_lint(&rome_path) || can_format,
TraversalMode::Format { .. } => can_format,
};
@@ -457,7 +480,7 @@ fn process_file(ctx: &TraversalOptions, path: &Path, file_id: FileId) -> FileRes
)
.with_file_id_and_code(file_id, "IO")?;
- if let TraversalMode::Fix = ctx.mode {
+ if ctx.mode.should_fix() {
let fixed = file_guard
.fix_file()
.with_file_id_and_code(file_id, "Lint")?;
@@ -536,8 +559,8 @@ fn process_file(ctx: &TraversalOptions, path: &Path, file_id: FileId) -> FileRes
if can_format {
let write = match ctx.mode {
// In check mode do not run the formatter and return the result immediately
- TraversalMode::Check { .. } | TraversalMode::Fix => return Ok(result),
- TraversalMode::CI => false,
+ TraversalMode::Check { .. } => return Ok(result),
+ TraversalMode::CI { .. } => false,
TraversalMode::Format { write, .. } => write,
};

MODIFY
crates/rome_cli/tests/configs.rs
crates/rome_cli/tests/configs.rs
@@ -31,6 +31,9 @@ pub const CONFIG_ALL_FIELDS: &str = r#"{
"indentSize": 2,
"lineWidth": 80
},
+ "linter": {
+ "enabled": true
+ },
"javascript": {
"formatter": {
"quoteStyle": "double"
@@ -42,5 +45,12 @@ pub const CONFIG_BAD_LINE_WIDTH: &str = r#"{
"root": true,
"formatter": {
"lineWidth": 500
- },
+ }
+}"#;
+
+pub const CONFIG_LINTER_DISABLED: &str = r#"{
+ "root": true,
+ "linter": {
+ "enabled": false
+ }
}"#;

MODIFY
crates/rome_cli/tests/main.rs
crates/rome_cli/tests/main.rs
@@ -56,6 +56,7 @@ const CUSTOM_FORMAT_AFTER: &str = r#"function f() {
mod check {
use super::*;
+ use crate::configs::CONFIG_LINTER_DISABLED;
use rome_console::LogLevel;
#[test]
@@ -213,6 +214,70 @@ mod check {
assert!(result.is_ok(), "run_cli returned {result:?}");
}
+
+ #[test]
+ fn no_lint_if_linter_is_disabled_when_run_apply() {
+ let mut fs = MemoryFileSystem::default();
+ let mut console = BufferConsole::default();
+
+ let file_path = Path::new("fix.js");
+ fs.insert(file_path.into(), FIX_BEFORE.as_bytes());
+
+ let config_path = Path::new("rome.json");
+ fs.insert(config_path.into(), CONFIG_LINTER_DISABLED.as_bytes());
+
+ let result = run_cli(CliSession {
+ app: App::with_filesystem_and_console(
+ DynRef::Borrowed(&mut fs),
+ DynRef::Borrowed(&mut console),
+ ),
+ args: Arguments::from_vec(vec![
+ OsString::from("check"),
+ OsString::from("--apply"),
+ file_path.as_os_str().into(),
+ ]),
+ });
+
+ assert!(result.is_ok(), "run_cli returned {result:?}");
+
+ let mut buffer = String::new();
+ fs.open(file_path)
+ .unwrap()
+ .read_to_string(&mut buffer)
+ .unwrap();
+
+ assert_eq!(buffer, FIX_BEFORE);
+ }
+
+ #[test]
+ fn no_lint_if_linter_is_disabled() {
+ let mut fs = MemoryFileSystem::default();
+ let mut console = BufferConsole::default();
+
+ let file_path = Path::new("fix.js");
+ fs.insert(file_path.into(), FIX_BEFORE.as_bytes());
+
+ let config_path = Path::new("rome.json");
+ fs.insert(config_path.into(), CONFIG_LINTER_DISABLED.as_bytes());
+
+ let result = run_cli(CliSession {
+ app: App::with_filesystem_and_console(
+ DynRef::Borrowed(&mut fs),
+ DynRef::Borrowed(&mut console),
+ ),
+ args: Arguments::from_vec(vec![OsString::from("check"), file_path.as_os_str().into()]),
+ });
+
+ assert!(result.is_ok(), "run_cli returned {result:?}");
+
+ let mut buffer = String::new();
+ fs.open(file_path)
+ .unwrap()
+ .read_to_string(&mut buffer)
+ .unwrap();
+
+ assert_eq!(buffer, FIX_BEFORE);
+ }
}
mod ci {

MODIFY
crates/rome_service/src/configuration/formatter.rs
crates/rome_service/src/configuration/formatter.rs
@@ -42,6 +42,7 @@ impl From<&FormatterConfiguration> for FormatSettings {
PlainIndentStyle::Space => IndentStyle::Space(conf.indent_size),
};
Self {
+ enabled: conf.enabled,
indent_style: Some(indent_style),
line_width: Some(conf.line_width),
format_with_errors: false,

ADD
crates/rome_service/src/configuration/linter.rs
+use crate::settings::LinterSettings;
+use serde::Deserialize;
+
+#[derive(Deserialize, Debug, Eq, PartialEq)]
+#[serde(rename_all = "camelCase", default)]
+pub struct LinterConfiguration {
+ // if `false`, it disables the feature. `true` by default
+ pub enabled: bool,
+}
+
+impl Default for LinterConfiguration {
+ fn default() -> Self {
+ Self { enabled: true }
+ }
+}
+
+impl From<&LinterConfiguration> for LinterSettings {
+ fn from(conf: &LinterConfiguration) -> Self {
+ Self {
+ enabled: conf.enabled,
+ }
+ }
+}

MODIFY
crates/rome_service/src/configuration/mod.rs
crates/rome_service/src/configuration/mod.rs
use crate::configuration::formatter::FormatterConfiguration;
use crate::configuration::javascript::JavascriptConfiguration;
+use crate::configuration::linter::LinterConfiguration;
use serde::Deserialize;
use std::fmt::{Debug, Display, Formatter};
mod formatter;
mod javascript;
+mod linter;
/// The configuration that is contained inside the file `rome.json`
#[derive(Default, Debug, Eq, PartialEq, Deserialize)]
@@ -23,6 +25,9 @@ pub struct Configuration {
/// The configuration of the formatter
pub formatter: FormatterConfiguration,
+ /// The configuration for the linter
+ pub linter: LinterConfiguration,
+
/// Specific configuration for the JavaScript language
pub javascript: JavascriptConfiguration,
}
@@ -31,6 +36,10 @@ impl Configuration {
pub fn is_formatter_disabled(&self) -> bool {
!self.formatter.enabled
}
+
+ pub fn is_linter_disabled(&self) -> bool {
+ !self.linter.enabled
+ }
}
/// Series of errors that can be thrown while computing the configuration

MODIFY
crates/rome_service/src/settings.rs
crates/rome_service/src/settings.rs
@@ -9,13 +9,16 @@ use rome_js_syntax::JsLanguage;
pub struct WorkspaceSettings {
/// Formatter settings applied to all files in the workspaces
pub format: FormatSettings,
+ /// Linter settings applied to all files in the workspace
+ pub linter: LinterSettings,
/// Language specific settings
pub languages: LanguagesSettings,
}
/// Formatter settings for the entire workspace
-#[derive(Default)]
pub struct FormatSettings {
+ /// Enabled by default
+ pub enabled: bool,
/// Stores whether formatting should be allowed to proceed if a given file
/// has syntax errors
pub format_with_errors: bool,
@@ -23,6 +26,29 @@ pub struct FormatSettings {
pub line_width: Option<LineWidth>,
}
+impl Default for FormatSettings {
+ fn default() -> Self {
+ Self {
+ enabled: true,
+ format_with_errors: false,
+ indent_style: Some(IndentStyle::default()),
+ line_width: Some(LineWidth::default()),
+ }
+ }
+}
+
+/// Formatter settings for the entire workspace
+pub struct LinterSettings {
+ /// Enabled by default
+ pub enabled: bool,
+}
+
+impl Default for LinterSettings {
+ fn default() -> Self {
+ Self { enabled: true }
+ }
+}
+
/// Static map of language names to language-specific settings
#[derive(Default)]
pub struct LanguagesSettings {

MODIFY
crates/rome_service/src/workspace.rs
crates/rome_service/src/workspace.rs
@@ -138,7 +138,9 @@ pub struct FixFileResult {
}
pub trait Workspace: Send + Sync + RefUnwindSafe {
- /// Checks whether a certain feature is supported for a file at a given path
+ /// Checks whether a certain feature is supported. There are different conditions:
+ /// - Rome doesn't recognize a file, so it can provide the feature;
+ /// - the feature is disabled inside the configuration;
fn supports_feature(&self, params: SupportsFeatureParams) -> bool;
/// Update the global settings for this workspace

MODIFY
crates/rome_service/src/workspace/server.rs
crates/rome_service/src/workspace/server.rs
@@ -137,9 +137,10 @@ impl WorkspaceServer {
impl Workspace for WorkspaceServer {
fn supports_feature(&self, params: SupportsFeatureParams) -> bool {
let capabilities = self.features.get_capabilities(&params.path);
+ let settings = self.settings.read().unwrap();
match params.feature {
- FeatureName::Format => capabilities.format.is_some(),
- FeatureName::Lint => capabilities.lint.is_some(),
+ FeatureName::Format => capabilities.format.is_some() && settings.format.enabled,
+ FeatureName::Lint => capabilities.lint.is_some() && settings.linter.enabled,
}
}

feat(rome_service, rome_cli): linter via configuration (#2866)