ep: Follow-ups for binding changes (#52258)
## Context
- Updates docs for how to update edit prediction bindings
- Walks back how often we use tab to accept prediction where we
previously didn't; now just when in leading whitespace, tab does not
accept completion when completions menu is open
- migration for keymaps using the old `edit_prediction_conflict` key
context
## How to Review
The only code worth reviewing is the migration
<!-- Help reviewers focus their attention:
- For small PRs: note what to focus on (e.g., "error handling in
foo.rs")
- For large PRs (>400 LOC): provide a guided tour — numbered list of
files/commits to read in order. (The `large-pr` label is applied
automatically.)
- See the review process guidelines for comment conventions -->
## Self-Review Checklist
<!-- Check before requesting review: -->
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- N/A or Added/Fixed/Improved ...
---------
Co-authored-by: Max <max@zed.dev>
This commit is contained in:
1 parent
930840c462
commit
93b7eeb9ba
7 files changed
+304
-97
No files matched your search
@@ -796,7 +796,7 @@
|
||||
},
|
||||
},
|
||||
{
|
||||
"context": "Editor && edit_prediction && edit_prediction_mode == eager",
|
||||
"context": "Editor && edit_prediction && edit_prediction_mode == eager && !showing_completions",
|
||||
"bindings": {
|
||||
"tab": "editor::AcceptEditPrediction",
|
||||
},
|
||||
|
||||
@@ -858,7 +858,7 @@
|
||||
},
|
||||
},
|
||||
{
|
||||
"context": "Editor && edit_prediction && edit_prediction_mode == eager",
|
||||
"context": "Editor && edit_prediction && edit_prediction_mode == eager && !showing_completions",
|
||||
"bindings": {
|
||||
"tab": "editor::AcceptEditPrediction",
|
||||
},
|
||||
|
||||
@@ -790,7 +790,7 @@
|
||||
},
|
||||
},
|
||||
{
|
||||
"context": "Editor && edit_prediction && edit_prediction_mode == eager",
|
||||
"context": "Editor && edit_prediction && edit_prediction_mode == eager && !showing_completions",
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"tab": "editor::AcceptEditPrediction",
|
||||
|
||||
@@ -1060,7 +1060,7 @@
|
||||
},
|
||||
},
|
||||
{
|
||||
"context": "Editor && edit_prediction && edit_prediction_mode == eager",
|
||||
"context": "Editor && edit_prediction && edit_prediction_mode == eager && !showing_completions",
|
||||
"bindings": {
|
||||
// This is identical to the binding in the base keymap, but the vim bindings above to
|
||||
// "vim::Tab" shadow it, so it needs to be bound again.
|
||||
|
||||
@@ -304,3 +304,9 @@ pub(crate) mod m_2026_02_25 {
|
||||
|
||||
pub(crate) use settings::migrate_builtin_agent_servers_to_registry;
|
||||
}
|
||||
|
||||
pub(crate) mod m_2026_03_23 {
|
||||
mod keymap;
|
||||
|
||||
pub(crate) use keymap::KEYMAP_PATTERNS;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
use std::ops::Range;
|
||||
|
||||
use tree_sitter::{Query, QueryMatch};
|
||||
|
||||
use crate::MigrationPatterns;
|
||||
|
||||
pub const KEYMAP_PATTERNS: MigrationPatterns =
|
||||
&[(crate::patterns::KEYMAP_CONTEXT_PATTERN, rename_context_key)];
|
||||
|
||||
fn rename_context_key(
|
||||
contents: &str,
|
||||
mat: &QueryMatch,
|
||||
query: &Query,
|
||||
) -> Option<(Range<usize>, String)> {
|
||||
let context_predicate_ix = query.capture_index_for_name("context_predicate")?;
|
||||
let context_predicate_range = mat
|
||||
.nodes_for_capture_index(context_predicate_ix)
|
||||
.next()?
|
||||
.byte_range();
|
||||
let old_predicate = contents.get(context_predicate_range.clone())?.to_string();
|
||||
let mut new_predicate = old_predicate.clone();
|
||||
|
||||
const REPLACEMENTS: &[(&str, &str)] = &[
|
||||
(
|
||||
"edit_prediction_conflict && !showing_completions",
|
||||
"(edit_prediction && in_leading_whitespace)",
|
||||
),
|
||||
(
|
||||
"edit_prediction_conflict && showing_completions",
|
||||
"(edit_prediction && showing_completions)",
|
||||
),
|
||||
(
|
||||
"edit_prediction_conflict",
|
||||
"(edit_prediction && (showing_completions || in_leading_whitespace))",
|
||||
),
|
||||
];
|
||||
|
||||
for (old, new) in REPLACEMENTS {
|
||||
new_predicate = new_predicate.replace(old, new);
|
||||
}
|
||||
|
||||
if new_predicate != old_predicate {
|
||||
Some((context_predicate_range, new_predicate))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
+247
-93
@@ -143,6 +143,10 @@ pub fn migrate_keymap(text: &str) -> Result<Option<String>> {
|
||||
migrations::m_2025_12_08::KEYMAP_PATTERNS,
|
||||
&KEYMAP_QUERY_2025_12_08,
|
||||
),
|
||||
MigrationType::TreeSitter(
|
||||
migrations::m_2026_03_23::KEYMAP_PATTERNS,
|
||||
&KEYMAP_QUERY_2026_03_23,
|
||||
),
|
||||
];
|
||||
run_migrations(text, migrations)
|
||||
}
|
||||
@@ -373,6 +377,10 @@ define_query!(
|
||||
SETTINGS_QUERY_2025_12_15,
|
||||
migrations::m_2025_12_15::SETTINGS_PATTERNS
|
||||
);
|
||||
define_query!(
|
||||
KEYMAP_QUERY_2026_03_23,
|
||||
migrations::m_2026_03_23::KEYMAP_PATTERNS
|
||||
);
|
||||
|
||||
// custom query
|
||||
static EDIT_PREDICTION_SETTINGS_MIGRATION_QUERY: LazyLock<Query> = LazyLock::new(|| {
|
||||
@@ -400,6 +408,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn assert_migrate_keymap(input: &str, output: Option<&str>) {
|
||||
let migrated = migrate_keymap(input).unwrap();
|
||||
pretty_assertions::assert_eq!(migrated.as_deref(), output);
|
||||
@@ -418,7 +427,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn assert_migrate_settings_with_migrations(
|
||||
fn assert_migrate_with_migrations(
|
||||
migrations: &[MigrationType],
|
||||
input: &str,
|
||||
output: Option<&str>,
|
||||
@@ -966,7 +975,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_mcp_settings_migration() {
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::TreeSitter(
|
||||
migrations::m_2025_06_16::SETTINGS_PATTERNS,
|
||||
&SETTINGS_QUERY_2025_06_16,
|
||||
@@ -1155,7 +1164,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
}"#;
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::TreeSitter(
|
||||
migrations::m_2025_06_16::SETTINGS_PATTERNS,
|
||||
&SETTINGS_QUERY_2025_06_16,
|
||||
@@ -1167,7 +1176,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_custom_agent_server_settings_migration() {
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::TreeSitter(
|
||||
migrations::m_2025_11_20::SETTINGS_PATTERNS,
|
||||
&SETTINGS_QUERY_2025_11_20,
|
||||
@@ -1383,7 +1392,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_flatten_code_action_formatters_basic_array() {
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_10_01::flatten_code_actions_formatters,
|
||||
)],
|
||||
@@ -1417,7 +1426,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_flatten_code_action_formatters_basic_object() {
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_10_01::flatten_code_actions_formatters,
|
||||
)],
|
||||
@@ -1574,7 +1583,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_flatten_code_action_formatters_array_with_multiple_action_blocks_in_defaults_and_multiple_languages()
|
||||
{
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_10_01::flatten_code_actions_formatters,
|
||||
)],
|
||||
@@ -1700,7 +1709,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_flatten_code_action_formatters_array_with_format_on_save_and_multiple_languages() {
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_10_01::flatten_code_actions_formatters,
|
||||
)],
|
||||
@@ -1887,7 +1896,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_format_on_save_formatter_migration_basic() {
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_10_02::remove_formatters_on_save,
|
||||
)],
|
||||
@@ -1907,7 +1916,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_format_on_save_formatter_migration_array() {
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_10_02::remove_formatters_on_save,
|
||||
)],
|
||||
@@ -1932,7 +1941,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_format_on_save_on_off_unchanged() {
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_10_02::remove_formatters_on_save,
|
||||
)],
|
||||
@@ -1943,7 +1952,7 @@ mod tests {
|
||||
None,
|
||||
);
|
||||
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_10_02::remove_formatters_on_save,
|
||||
)],
|
||||
@@ -1957,7 +1966,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_format_on_save_formatter_migration_in_languages() {
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_10_02::remove_formatters_on_save,
|
||||
)],
|
||||
@@ -1995,7 +2004,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_format_on_save_formatter_migration_mixed_global_and_languages() {
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_10_02::remove_formatters_on_save,
|
||||
)],
|
||||
@@ -2032,7 +2041,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_format_on_save_no_migration_when_no_format_on_save() {
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_10_02::remove_formatters_on_save,
|
||||
)],
|
||||
@@ -2046,7 +2055,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_restore_code_actions_on_format() {
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_10_16::restore_code_actions_on_format,
|
||||
)],
|
||||
@@ -2067,7 +2076,7 @@ mod tests {
|
||||
),
|
||||
);
|
||||
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_10_16::restore_code_actions_on_format,
|
||||
)],
|
||||
@@ -2081,7 +2090,7 @@ mod tests {
|
||||
None,
|
||||
);
|
||||
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_10_16::restore_code_actions_on_format,
|
||||
)],
|
||||
@@ -2108,7 +2117,7 @@ mod tests {
|
||||
),
|
||||
);
|
||||
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_10_16::restore_code_actions_on_format,
|
||||
)],
|
||||
@@ -2137,7 +2146,7 @@ mod tests {
|
||||
),
|
||||
);
|
||||
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_10_16::restore_code_actions_on_format,
|
||||
)],
|
||||
@@ -2155,7 +2164,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_make_file_finder_include_ignored_an_enum() {
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_10_17::make_file_finder_include_ignored_an_enum,
|
||||
)],
|
||||
@@ -2163,7 +2172,7 @@ mod tests {
|
||||
None,
|
||||
);
|
||||
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_10_17::make_file_finder_include_ignored_an_enum,
|
||||
)],
|
||||
@@ -2183,7 +2192,7 @@ mod tests {
|
||||
),
|
||||
);
|
||||
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_10_17::make_file_finder_include_ignored_an_enum,
|
||||
)],
|
||||
@@ -2203,7 +2212,7 @@ mod tests {
|
||||
),
|
||||
);
|
||||
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_10_17::make_file_finder_include_ignored_an_enum,
|
||||
)],
|
||||
@@ -2224,7 +2233,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Platform key: settings nested inside "linux" should be migrated
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_10_17::make_file_finder_include_ignored_an_enum,
|
||||
)],
|
||||
@@ -2253,7 +2262,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Profile: settings nested inside profiles should be migrated
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_10_17::make_file_finder_include_ignored_an_enum,
|
||||
)],
|
||||
@@ -2288,7 +2297,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_make_relative_line_numbers_an_enum() {
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_10_21::make_relative_line_numbers_an_enum,
|
||||
)],
|
||||
@@ -2296,7 +2305,7 @@ mod tests {
|
||||
None,
|
||||
);
|
||||
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_10_21::make_relative_line_numbers_an_enum,
|
||||
)],
|
||||
@@ -2312,7 +2321,7 @@ mod tests {
|
||||
),
|
||||
);
|
||||
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_10_21::make_relative_line_numbers_an_enum,
|
||||
)],
|
||||
@@ -2329,7 +2338,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Platform key: settings nested inside "macos" should be migrated
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_10_21::make_relative_line_numbers_an_enum,
|
||||
)],
|
||||
@@ -2354,7 +2363,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Profile: settings nested inside profiles should be migrated
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_10_21::make_relative_line_numbers_an_enum,
|
||||
)],
|
||||
@@ -2431,7 +2440,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Platform key: settings nested inside "linux" should be migrated
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_11_25::remove_context_server_source,
|
||||
)],
|
||||
@@ -2469,7 +2478,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Profile: settings nested inside profiles should be migrated
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_11_25::remove_context_server_source,
|
||||
)],
|
||||
@@ -2610,7 +2619,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_make_auto_indent_an_enum() {
|
||||
// Empty settings should not change
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_01_27::make_auto_indent_an_enum,
|
||||
)],
|
||||
@@ -2619,7 +2628,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// true should become "syntax_aware"
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_01_27::make_auto_indent_an_enum,
|
||||
)],
|
||||
@@ -2636,7 +2645,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// false should become "none"
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_01_27::make_auto_indent_an_enum,
|
||||
)],
|
||||
@@ -2653,7 +2662,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Already valid enum values should not change
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_01_27::make_auto_indent_an_enum,
|
||||
)],
|
||||
@@ -2665,7 +2674,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Should also work inside languages
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2025_01_27::make_auto_indent_an_enum,
|
||||
)],
|
||||
@@ -2694,7 +2703,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_move_edit_prediction_provider_to_edit_predictions() {
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_02::move_edit_prediction_provider_to_edit_predictions,
|
||||
)],
|
||||
@@ -2702,7 +2711,7 @@ mod tests {
|
||||
None,
|
||||
);
|
||||
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_02::move_edit_prediction_provider_to_edit_predictions,
|
||||
)],
|
||||
@@ -2726,7 +2735,7 @@ mod tests {
|
||||
),
|
||||
);
|
||||
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_02::move_edit_prediction_provider_to_edit_predictions,
|
||||
)],
|
||||
@@ -2754,7 +2763,7 @@ mod tests {
|
||||
),
|
||||
);
|
||||
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_02::move_edit_prediction_provider_to_edit_predictions,
|
||||
)],
|
||||
@@ -2781,7 +2790,7 @@ mod tests {
|
||||
),
|
||||
);
|
||||
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_02::move_edit_prediction_provider_to_edit_predictions,
|
||||
)],
|
||||
@@ -2798,7 +2807,7 @@ mod tests {
|
||||
|
||||
// Non-object edit_predictions (e.g. true) should gracefully skip
|
||||
// instead of bail!-ing and aborting the entire migration chain.
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_02::move_edit_prediction_provider_to_edit_predictions,
|
||||
)],
|
||||
@@ -2822,7 +2831,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Platform key: settings nested inside "macos" should be migrated
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_02::move_edit_prediction_provider_to_edit_predictions,
|
||||
)],
|
||||
@@ -2851,7 +2860,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Profile: settings nested inside profiles should be migrated
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_02::move_edit_prediction_provider_to_edit_predictions,
|
||||
)],
|
||||
@@ -2884,7 +2893,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Combined: root + platform + profile should all be migrated simultaneously
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_02::move_edit_prediction_provider_to_edit_predictions,
|
||||
)],
|
||||
@@ -2935,7 +2944,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_migrate_experimental_sweep_mercury() {
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_03::migrate_experimental_sweep_mercury,
|
||||
)],
|
||||
@@ -2943,7 +2952,7 @@ mod tests {
|
||||
None,
|
||||
);
|
||||
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_03::migrate_experimental_sweep_mercury,
|
||||
)],
|
||||
@@ -2969,7 +2978,7 @@ mod tests {
|
||||
),
|
||||
);
|
||||
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_03::migrate_experimental_sweep_mercury,
|
||||
)],
|
||||
@@ -2995,7 +3004,7 @@ mod tests {
|
||||
),
|
||||
);
|
||||
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_03::migrate_experimental_sweep_mercury,
|
||||
)],
|
||||
@@ -3021,7 +3030,7 @@ mod tests {
|
||||
),
|
||||
);
|
||||
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_03::migrate_experimental_sweep_mercury,
|
||||
)],
|
||||
@@ -3036,7 +3045,7 @@ mod tests {
|
||||
None,
|
||||
);
|
||||
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_03::migrate_experimental_sweep_mercury,
|
||||
)],
|
||||
@@ -3054,7 +3063,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Platform key: settings nested inside "linux" should be migrated
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_03::migrate_experimental_sweep_mercury,
|
||||
)],
|
||||
@@ -3085,7 +3094,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Profile: settings nested inside profiles should be migrated
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_03::migrate_experimental_sweep_mercury,
|
||||
)],
|
||||
@@ -3120,7 +3129,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Combined: root + platform + profile should all be migrated simultaneously
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_03::migrate_experimental_sweep_mercury,
|
||||
)],
|
||||
@@ -3178,7 +3187,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_migrate_always_allow_tool_actions_to_default() {
|
||||
// No agent settings - no change
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_04::migrate_tool_permission_defaults,
|
||||
)],
|
||||
@@ -3187,7 +3196,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// always_allow_tool_actions: true -> tool_permissions.default: "allow"
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_04::migrate_tool_permission_defaults,
|
||||
)],
|
||||
@@ -3214,7 +3223,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// always_allow_tool_actions: false -> just remove it
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_04::migrate_tool_permission_defaults,
|
||||
)],
|
||||
@@ -3233,7 +3242,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Preserve existing tool_permissions.tools when migrating
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_04::migrate_tool_permission_defaults,
|
||||
)],
|
||||
@@ -3272,7 +3281,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Don't override existing default (and migrate default_mode to default)
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_04::migrate_tool_permission_defaults,
|
||||
)],
|
||||
@@ -3302,7 +3311,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Migrate existing default_mode to default (no always_allow_tool_actions)
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_04::migrate_tool_permission_defaults,
|
||||
)],
|
||||
@@ -3331,7 +3340,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// No migration needed if already using new format with "default"
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_04::migrate_tool_permission_defaults,
|
||||
)],
|
||||
@@ -3349,7 +3358,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Migrate default_mode to default in tool-specific rules
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_04::migrate_tool_permission_defaults,
|
||||
)],
|
||||
@@ -3388,7 +3397,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// When tool_permissions is null, replace it so always_allow is preserved
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_04::migrate_tool_permission_defaults,
|
||||
)],
|
||||
@@ -3416,7 +3425,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Platform-specific agent migration
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_04::migrate_tool_permission_defaults,
|
||||
)],
|
||||
@@ -3447,7 +3456,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Channel-specific agent migration
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_04::migrate_tool_permission_defaults,
|
||||
)],
|
||||
@@ -3488,7 +3497,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Profile-level migration
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_04::migrate_tool_permission_defaults,
|
||||
)],
|
||||
@@ -3526,7 +3535,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Platform-specific agent with profiles
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_04::migrate_tool_permission_defaults,
|
||||
)],
|
||||
@@ -3571,7 +3580,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Root-level profile with always_allow_tool_actions
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_04::migrate_tool_permission_defaults,
|
||||
)],
|
||||
@@ -3606,7 +3615,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Root-level profile with default_mode
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_04::migrate_tool_permission_defaults,
|
||||
)],
|
||||
@@ -3643,7 +3652,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Root-level profile + root-level agent both migrated
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_04::migrate_tool_permission_defaults,
|
||||
)],
|
||||
@@ -3689,7 +3698,7 @@ mod tests {
|
||||
|
||||
// Non-boolean always_allow_tool_actions (string "true") is left in place
|
||||
// so the schema validator can report it, rather than silently dropping user data.
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_04::migrate_tool_permission_defaults,
|
||||
)],
|
||||
@@ -3705,7 +3714,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// null always_allow_tool_actions is removed (treated as false)
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_04::migrate_tool_permission_defaults,
|
||||
)],
|
||||
@@ -3722,7 +3731,7 @@ mod tests {
|
||||
|
||||
// Project-local settings (.zed/settings.json) with always_allow_tool_actions
|
||||
// These files have no platform/channel overrides or root-level profiles.
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_04::migrate_tool_permission_defaults,
|
||||
)],
|
||||
@@ -3763,7 +3772,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Project-local settings with only default_mode (no always_allow_tool_actions)
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_04::migrate_tool_permission_defaults,
|
||||
)],
|
||||
@@ -3792,7 +3801,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Project-local settings with no agent section at all - no change
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_04::migrate_tool_permission_defaults,
|
||||
)],
|
||||
@@ -3807,7 +3816,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Existing agent_servers are left untouched
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_04::migrate_tool_permission_defaults,
|
||||
)],
|
||||
@@ -3850,7 +3859,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Existing agent_servers are left untouched even with partial entries
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_04::migrate_tool_permission_defaults,
|
||||
)],
|
||||
@@ -3887,7 +3896,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// always_allow_tool_actions: false leaves agent_servers untouched
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_04::migrate_tool_permission_defaults,
|
||||
)],
|
||||
@@ -3910,7 +3919,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_migrate_builtin_agent_servers_to_registry_simple() {
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_25::migrate_builtin_agent_servers_to_registry,
|
||||
)],
|
||||
@@ -3950,7 +3959,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_migrate_builtin_agent_servers_empty_entries() {
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_25::migrate_builtin_agent_servers_to_registry,
|
||||
)],
|
||||
@@ -3981,7 +3990,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_migrate_builtin_agent_servers_with_command() {
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_25::migrate_builtin_agent_servers_to_registry,
|
||||
)],
|
||||
@@ -4019,7 +4028,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_migrate_builtin_agent_servers_gemini_with_command() {
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_25::migrate_builtin_agent_servers_to_registry,
|
||||
)],
|
||||
@@ -4047,7 +4056,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_migrate_builtin_agent_servers_gemini_ignore_system_version_false() {
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_25::migrate_builtin_agent_servers_to_registry,
|
||||
)],
|
||||
@@ -4075,7 +4084,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_migrate_builtin_agent_servers_gemini_ignore_system_version_true() {
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_25::migrate_builtin_agent_servers_to_registry,
|
||||
)],
|
||||
@@ -4102,7 +4111,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_migrate_builtin_agent_servers_already_typed_unchanged() {
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_25::migrate_builtin_agent_servers_to_registry,
|
||||
)],
|
||||
@@ -4124,7 +4133,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_migrate_builtin_agent_servers_preserves_custom_entries() {
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_25::migrate_builtin_agent_servers_to_registry,
|
||||
)],
|
||||
@@ -4158,7 +4167,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_migrate_builtin_agent_servers_target_already_exists() {
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_25::migrate_builtin_agent_servers_to_registry,
|
||||
)],
|
||||
@@ -4188,7 +4197,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_migrate_builtin_agent_servers_no_agent_servers_key() {
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_25::migrate_builtin_agent_servers_to_registry,
|
||||
)],
|
||||
@@ -4203,7 +4212,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_migrate_builtin_agent_servers_all_fields() {
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_25::migrate_builtin_agent_servers_to_registry,
|
||||
)],
|
||||
@@ -4251,7 +4260,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_migrate_builtin_agent_servers_codex_with_command() {
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_25::migrate_builtin_agent_servers_to_registry,
|
||||
)],
|
||||
@@ -4283,7 +4292,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_migrate_builtin_agent_servers_mixed_migrated_and_not() {
|
||||
assert_migrate_settings_with_migrations(
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::Json(
|
||||
migrations::m_2026_02_25::migrate_builtin_agent_servers_to_registry,
|
||||
)],
|
||||
@@ -4318,4 +4327,149 @@ mod tests {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_migrate_edit_prediction_conflict_context() {
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::TreeSitter(
|
||||
migrations::m_2026_03_23::KEYMAP_PATTERNS,
|
||||
&KEYMAP_QUERY_2026_03_23,
|
||||
)],
|
||||
&r#"
|
||||
[
|
||||
{
|
||||
"context": "Editor && edit_prediction_conflict",
|
||||
"bindings": {
|
||||
"ctrl-enter": "editor::AcceptEditPrediction" // Example of a modified keybinding
|
||||
}
|
||||
}
|
||||
]
|
||||
"#.unindent(),
|
||||
Some(
|
||||
&r#"
|
||||
[
|
||||
{
|
||||
"context": "Editor && (edit_prediction && (showing_completions || in_leading_whitespace))",
|
||||
"bindings": {
|
||||
"ctrl-enter": "editor::AcceptEditPrediction" // Example of a modified keybinding
|
||||
}
|
||||
}
|
||||
]
|
||||
"#.unindent(),
|
||||
),
|
||||
);
|
||||
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::TreeSitter(
|
||||
migrations::m_2026_03_23::KEYMAP_PATTERNS,
|
||||
&KEYMAP_QUERY_2026_03_23,
|
||||
)],
|
||||
&r#"
|
||||
[
|
||||
{
|
||||
"context": "Editor && edit_prediction_conflict && !showing_completions",
|
||||
"bindings": {
|
||||
// Here we don't require a modifier unless there's a language server completion
|
||||
"tab": "editor::AcceptEditPrediction"
|
||||
}
|
||||
}
|
||||
]
|
||||
"#.unindent(),
|
||||
Some(
|
||||
&r#"
|
||||
[
|
||||
{
|
||||
"context": "Editor && (edit_prediction && in_leading_whitespace)",
|
||||
"bindings": {
|
||||
// Here we don't require a modifier unless there's a language server completion
|
||||
"tab": "editor::AcceptEditPrediction"
|
||||
}
|
||||
}
|
||||
]
|
||||
"#.unindent(),
|
||||
),
|
||||
);
|
||||
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::TreeSitter(
|
||||
migrations::m_2026_03_23::KEYMAP_PATTERNS,
|
||||
&KEYMAP_QUERY_2026_03_23,
|
||||
)],
|
||||
&r#"
|
||||
[
|
||||
{
|
||||
"context": "Editor && edit_prediction_conflict && showing_completions",
|
||||
"bindings": {
|
||||
"tab": "editor::AcceptEditPrediction"
|
||||
}
|
||||
}
|
||||
]
|
||||
"#
|
||||
.unindent(),
|
||||
Some(
|
||||
&r#"
|
||||
[
|
||||
{
|
||||
"context": "Editor && (edit_prediction && showing_completions)",
|
||||
"bindings": {
|
||||
"tab": "editor::AcceptEditPrediction"
|
||||
}
|
||||
}
|
||||
]
|
||||
"#
|
||||
.unindent(),
|
||||
),
|
||||
);
|
||||
|
||||
assert_migrate_with_migrations(
|
||||
&[MigrationType::TreeSitter(
|
||||
migrations::m_2026_03_23::KEYMAP_PATTERNS,
|
||||
&KEYMAP_QUERY_2026_03_23,
|
||||
)],
|
||||
&r#"
|
||||
[
|
||||
{
|
||||
"context": "Editor && edit_prediction",
|
||||
"bindings": {
|
||||
"tab": "editor::AcceptEditPrediction",
|
||||
// Optional: This makes the default `alt-l` binding do nothing.
|
||||
"alt-l": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "Editor && edit_prediction_conflict",
|
||||
"bindings": {
|
||||
"alt-tab": "editor::AcceptEditPrediction",
|
||||
// Optional: This makes the default `alt-l` binding do nothing.
|
||||
"alt-l": null
|
||||
}
|
||||
},
|
||||
]
|
||||
"#
|
||||
.unindent(),
|
||||
Some(
|
||||
&r#"
|
||||
[
|
||||
{
|
||||
"context": "Editor && edit_prediction",
|
||||
"bindings": {
|
||||
"tab": "editor::AcceptEditPrediction",
|
||||
// Optional: This makes the default `alt-l` binding do nothing.
|
||||
"alt-l": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "Editor && (edit_prediction && (showing_completions || in_leading_whitespace))",
|
||||
"bindings": {
|
||||
"alt-tab": "editor::AcceptEditPrediction",
|
||||
// Optional: This makes the default `alt-l` binding do nothing.
|
||||
"alt-l": null
|
||||
}
|
||||
},
|
||||
]
|
||||
"#
|
||||
.unindent(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user