From 6b0668572378378b4173186e28bd594d6e894db7 Mon Sep 17 00:00:00 2001 From: Peter Tripp Date: Tue, 1 Jul 2025 14:21:26 -0400 Subject: [PATCH] Revert "settings: Remove `version` field migration" (#33729) - Reverts zed-industries/zed#33711 I think we should just make this a breaking change with v0.194.x. Forwards compatibility is hard, we should build abstractions that make this easier (next time). See also: - https://github.com/zed-industries/zed/pull/33372 Release Notes: - N/A --- crates/migrator/src/migrations.rs | 6 + .../src/migrations/m_2025_06_25/settings.rs | 133 ++++++++++++++++++ crates/migrator/src/migrator.rs | 79 +++++++++++ 3 files changed, 218 insertions(+) create mode 100644 crates/migrator/src/migrations/m_2025_06_25/settings.rs diff --git a/crates/migrator/src/migrations.rs b/crates/migrator/src/migrations.rs index 84ef0f0456e65da7dbb604e7733fec8ab1cdd386..4e3839358b6e55b0cbec72f92ca12d9b2b30c168 100644 --- a/crates/migrator/src/migrations.rs +++ b/crates/migrator/src/migrations.rs @@ -82,6 +82,12 @@ pub(crate) mod m_2025_06_16 { pub(crate) use settings::SETTINGS_PATTERNS; } +pub(crate) mod m_2025_06_25 { + mod settings; + + pub(crate) use settings::SETTINGS_PATTERNS; +} + pub(crate) mod m_2025_06_27 { mod settings; diff --git a/crates/migrator/src/migrations/m_2025_06_25/settings.rs b/crates/migrator/src/migrations/m_2025_06_25/settings.rs new file mode 100644 index 0000000000000000000000000000000000000000..5dd6c3093a43b00acff3db6c1e316a3fc6664175 --- /dev/null +++ b/crates/migrator/src/migrations/m_2025_06_25/settings.rs @@ -0,0 +1,133 @@ +use std::ops::Range; +use tree_sitter::{Query, QueryMatch}; + +use crate::MigrationPatterns; + +pub const SETTINGS_PATTERNS: MigrationPatterns = &[ + (SETTINGS_VERSION_PATTERN, remove_version_fields), + ( + SETTINGS_NESTED_VERSION_PATTERN, + remove_nested_version_fields, + ), +]; + +const SETTINGS_VERSION_PATTERN: &str = r#"(document + (object + (pair + key: (string (string_content) @key) + value: (object + (pair + key: (string (string_content) @version_key) + value: (_) @version_value + ) @version_pair + ) + ) + ) + (#eq? @key "agent") + (#eq? @version_key "version") +)"#; + +const SETTINGS_NESTED_VERSION_PATTERN: &str = r#"(document + (object + (pair + key: (string (string_content) @language_models) + value: (object + (pair + key: (string (string_content) @provider) + value: (object + (pair + key: (string (string_content) @version_key) + value: (_) @version_value + ) @version_pair + ) + ) + ) + ) + ) + (#eq? @language_models "language_models") + (#match? @provider "^(anthropic|openai)$") + (#eq? @version_key "version") +)"#; + +fn remove_version_fields( + contents: &str, + mat: &QueryMatch, + query: &Query, +) -> Option<(Range, String)> { + let version_pair_ix = query.capture_index_for_name("version_pair")?; + let version_pair_node = mat.nodes_for_capture_index(version_pair_ix).next()?; + + remove_pair_with_whitespace(contents, version_pair_node) +} + +fn remove_nested_version_fields( + contents: &str, + mat: &QueryMatch, + query: &Query, +) -> Option<(Range, String)> { + let version_pair_ix = query.capture_index_for_name("version_pair")?; + let version_pair_node = mat.nodes_for_capture_index(version_pair_ix).next()?; + + remove_pair_with_whitespace(contents, version_pair_node) +} + +fn remove_pair_with_whitespace( + contents: &str, + pair_node: tree_sitter::Node, +) -> Option<(Range, String)> { + let mut range_to_remove = pair_node.byte_range(); + + // Check if there's a comma after this pair + if let Some(next_sibling) = pair_node.next_sibling() { + if next_sibling.kind() == "," { + range_to_remove.end = next_sibling.end_byte(); + } + } else { + // If no next sibling, check if there's a comma before + if let Some(prev_sibling) = pair_node.prev_sibling() { + if prev_sibling.kind() == "," { + range_to_remove.start = prev_sibling.start_byte(); + } + } + } + + // Include any leading whitespace/newline, including comments + let text_before = &contents[..range_to_remove.start]; + if let Some(last_newline) = text_before.rfind('\n') { + let whitespace_start = last_newline + 1; + let potential_whitespace = &contents[whitespace_start..range_to_remove.start]; + + // Check if it's only whitespace or comments + let mut is_whitespace_or_comment = true; + let mut in_comment = false; + let mut chars = potential_whitespace.chars().peekable(); + + while let Some(ch) = chars.next() { + if in_comment { + if ch == '\n' { + in_comment = false; + } + } else if ch == '/' && chars.peek() == Some(&'/') { + in_comment = true; + chars.next(); // Skip the second '/' + } else if !ch.is_whitespace() { + is_whitespace_or_comment = false; + break; + } + } + + if is_whitespace_or_comment { + range_to_remove.start = whitespace_start; + } + } + + // Also check if we need to include trailing whitespace up to the next line + let text_after = &contents[range_to_remove.end..]; + if let Some(newline_pos) = text_after.find('\n') { + if text_after[..newline_pos].chars().all(|c| c.is_whitespace()) { + range_to_remove.end += newline_pos + 1; + } + } + + Some((range_to_remove, String::new())) +} diff --git a/crates/migrator/src/migrator.rs b/crates/migrator/src/migrator.rs index 06e96a6f865c227579ab2452426b5d8cf46fda7c..be32b2734e66ebd621ff858a79eef322468b11ae 100644 --- a/crates/migrator/src/migrator.rs +++ b/crates/migrator/src/migrator.rs @@ -152,6 +152,10 @@ pub fn migrate_settings(text: &str) -> Result> { migrations::m_2025_06_16::SETTINGS_PATTERNS, &SETTINGS_QUERY_2025_06_16, ), + ( + migrations::m_2025_06_25::SETTINGS_PATTERNS, + &SETTINGS_QUERY_2025_06_25, + ), ( migrations::m_2025_06_27::SETTINGS_PATTERNS, &SETTINGS_QUERY_2025_06_27, @@ -258,6 +262,10 @@ define_query!( SETTINGS_QUERY_2025_06_16, migrations::m_2025_06_16::SETTINGS_PATTERNS ); +define_query!( + SETTINGS_QUERY_2025_06_25, + migrations::m_2025_06_25::SETTINGS_PATTERNS +); define_query!( SETTINGS_QUERY_2025_06_27, migrations::m_2025_06_27::SETTINGS_PATTERNS @@ -1081,6 +1089,77 @@ mod tests { ); } + #[test] + fn test_remove_version_fields() { + assert_migrate_settings( + r#"{ + "language_models": { + "anthropic": { + "version": "1", + "api_url": "https://api.anthropic.com" + }, + "openai": { + "version": "1", + "api_url": "https://api.openai.com/v1" + } + }, + "agent": { + "version": "2", + "enabled": true, + "preferred_completion_mode": "normal", + "button": true, + "dock": "right", + "default_width": 640, + "default_height": 320, + "default_model": { + "provider": "zed.dev", + "model": "claude-sonnet-4" + } + } +}"#, + Some( + r#"{ + "language_models": { + "anthropic": { + "api_url": "https://api.anthropic.com" + }, + "openai": { + "api_url": "https://api.openai.com/v1" + } + }, + "agent": { + "enabled": true, + "preferred_completion_mode": "normal", + "button": true, + "dock": "right", + "default_width": 640, + "default_height": 320, + "default_model": { + "provider": "zed.dev", + "model": "claude-sonnet-4" + } + } +}"#, + ), + ); + + // Test that version fields in other contexts are not removed + assert_migrate_settings( + r#"{ + "language_models": { + "other_provider": { + "version": "1", + "api_url": "https://api.example.com" + } + }, + "other_section": { + "version": "1" + } +}"#, + None, + ); + } + #[test] fn test_flatten_context_server_command() { assert_migrate_settings(