diff --git a/crates/migrator/src/migrations.rs b/crates/migrator/src/migrations.rs
index a4db2df46437c8e78d3e610f78c5374898b842a2..c0b54b0afc3c74a23026100b706fd68d3476aff3 100644
--- a/crates/migrator/src/migrations.rs
+++ b/crates/migrator/src/migrations.rs
@@ -105,3 +105,9 @@ pub(crate) mod m_2025_10_01 {
pub(crate) use settings::SETTINGS_PATTERNS;
}
+
+pub(crate) mod m_2025_10_02 {
+ mod settings;
+
+ pub(crate) use settings::remove_formatters_on_save;
+}
diff --git a/crates/migrator/src/migrations/m_2025_10_02/settings.rs b/crates/migrator/src/migrations/m_2025_10_02/settings.rs
new file mode 100644
index 0000000000000000000000000000000000000000..2434ae4d0e100ce58e4cfdd2eee1039188c1d7bc
--- /dev/null
+++ b/crates/migrator/src/migrations/m_2025_10_02/settings.rs
@@ -0,0 +1,50 @@
+use anyhow::Result;
+use serde_json::Value;
+
+pub fn remove_formatters_on_save(value: &mut Value) -> Result<()> {
+ remove_formatters_on_save_inner(value, &[])?;
+ let languages = value
+ .as_object_mut()
+ .and_then(|obj| obj.get_mut("languages"))
+ .and_then(|languages| languages.as_object_mut());
+ if let Some(languages) = languages {
+ for (language_name, language) in languages.iter_mut() {
+ let path = vec!["languages", language_name];
+ remove_formatters_on_save_inner(language, &path)?;
+ }
+ }
+ Ok(())
+}
+
+fn remove_formatters_on_save_inner(value: &mut Value, path: &[&str]) -> Result<()> {
+ let Some(obj) = value.as_object_mut() else {
+ return Ok(());
+ };
+ let Some(format_on_save) = obj.get("format_on_save").cloned() else {
+ return Ok(());
+ };
+ let is_format_on_save_set_to_formatter = format_on_save
+ .as_str()
+ .map_or(true, |s| s != "on" && s != "off");
+ if !is_format_on_save_set_to_formatter {
+ return Ok(());
+ }
+
+ fn fmt_path(path: &[&str], key: &str) -> String {
+ let mut path = path.to_vec();
+ path.push(key);
+ path.join(".")
+ }
+
+ anyhow::ensure!(
+ obj.get("formatter").is_none(),
+ r#"Setting formatters in both "format_on_save" and "formatter" is deprecated. Please migrate the formatters from {} into {}"#,
+ fmt_path(path, "format_on_save"),
+ fmt_path(path, "formatter")
+ );
+
+ obj.insert("format_on_save".to_string(), serde_json::json!("on"));
+ obj.insert("formatter".to_string(), format_on_save);
+
+ Ok(())
+}
diff --git a/crates/migrator/src/migrator.rs b/crates/migrator/src/migrator.rs
index e6085f9c58a3867540c159530d1803c5ef5a5174..4e649a0791fb0acef9d1b56138972964fd8ee3ac 100644
--- a/crates/migrator/src/migrator.rs
+++ b/crates/migrator/src/migrator.rs
@@ -76,7 +76,7 @@ fn run_migrations(text: &str, migrations: &[MigrationType]) -> Result