keybindings.rs

   1use std::{
   2    ops::{Not, Range},
   3    sync::Arc,
   4};
   5
   6use anyhow::{Context as _, anyhow};
   7use collections::{HashMap, HashSet};
   8use editor::{CompletionProvider, Editor, EditorEvent};
   9use feature_flags::FeatureFlagViewExt;
  10use fs::Fs;
  11use fuzzy::{StringMatch, StringMatchCandidate};
  12use gpui::{
  13    AppContext as _, AsyncApp, Context, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable,
  14    Global, KeyContext, Keystroke, ModifiersChangedEvent, ScrollStrategy, StyledText, Subscription,
  15    WeakEntity, actions, div, transparent_black,
  16};
  17use language::{Language, LanguageConfig, ToOffset as _};
  18use settings::{BaseKeymap, KeybindSource, KeymapFile, SettingsAssets};
  19
  20use util::ResultExt;
  21
  22use ui::{
  23    ActiveTheme as _, App, BorrowAppContext, ContextMenu, ParentElement as _, Render, SharedString,
  24    Styled as _, Tooltip, Window, prelude::*, right_click_menu,
  25};
  26use workspace::{Item, ModalView, SerializableItem, Workspace, register_serializable_item};
  27
  28use crate::{
  29    SettingsUiFeatureFlag,
  30    keybindings::persistence::KEYBINDING_EDITORS,
  31    ui_components::table::{Table, TableInteractionState},
  32};
  33
  34actions!(
  35    zed,
  36    [
  37        /// Opens the keymap editor.
  38        OpenKeymapEditor
  39    ]
  40);
  41
  42const KEYMAP_EDITOR_NAMESPACE: &'static str = "keymap_editor";
  43actions!(
  44    keymap_editor,
  45    [
  46        /// Edits the selected key binding.
  47        EditBinding,
  48        /// Copies the action name to clipboard.
  49        CopyAction,
  50        /// Copies the context predicate to clipboard.
  51        CopyContext
  52    ]
  53);
  54
  55pub fn init(cx: &mut App) {
  56    let keymap_event_channel = KeymapEventChannel::new();
  57    cx.set_global(keymap_event_channel);
  58
  59    cx.on_action(|_: &OpenKeymapEditor, cx| {
  60        workspace::with_active_or_new_workspace(cx, move |workspace, window, cx| {
  61            let existing = workspace
  62                .active_pane()
  63                .read(cx)
  64                .items()
  65                .find_map(|item| item.downcast::<KeymapEditor>());
  66
  67            if let Some(existing) = existing {
  68                workspace.activate_item(&existing, true, true, window, cx);
  69            } else {
  70                let keymap_editor =
  71                    cx.new(|cx| KeymapEditor::new(workspace.weak_handle(), window, cx));
  72                workspace.add_item_to_active_pane(Box::new(keymap_editor), None, true, window, cx);
  73            }
  74        });
  75    });
  76
  77    cx.observe_new(|_workspace: &mut Workspace, window, cx| {
  78        let Some(window) = window else { return };
  79
  80        let keymap_ui_actions = [std::any::TypeId::of::<OpenKeymapEditor>()];
  81
  82        command_palette_hooks::CommandPaletteFilter::update_global(cx, |filter, _cx| {
  83            filter.hide_action_types(&keymap_ui_actions);
  84            filter.hide_namespace(KEYMAP_EDITOR_NAMESPACE);
  85        });
  86
  87        cx.observe_flag::<SettingsUiFeatureFlag, _>(
  88            window,
  89            move |is_enabled, _workspace, _, cx| {
  90                if is_enabled {
  91                    command_palette_hooks::CommandPaletteFilter::update_global(
  92                        cx,
  93                        |filter, _cx| {
  94                            filter.show_action_types(keymap_ui_actions.iter());
  95                            filter.show_namespace(KEYMAP_EDITOR_NAMESPACE);
  96                        },
  97                    );
  98                } else {
  99                    command_palette_hooks::CommandPaletteFilter::update_global(
 100                        cx,
 101                        |filter, _cx| {
 102                            filter.hide_action_types(&keymap_ui_actions);
 103                            filter.hide_namespace(KEYMAP_EDITOR_NAMESPACE);
 104                        },
 105                    );
 106                }
 107            },
 108        )
 109        .detach();
 110    })
 111    .detach();
 112
 113    register_serializable_item::<KeymapEditor>(cx);
 114}
 115
 116pub struct KeymapEventChannel {}
 117
 118impl Global for KeymapEventChannel {}
 119
 120impl KeymapEventChannel {
 121    fn new() -> Self {
 122        Self {}
 123    }
 124
 125    pub fn trigger_keymap_changed(cx: &mut App) {
 126        let Some(_event_channel) = cx.try_global::<Self>() else {
 127            // don't panic if no global defined. This usually happens in tests
 128            return;
 129        };
 130        cx.update_global(|_event_channel: &mut Self, _| {
 131            /* triggers observers in KeymapEditors */
 132        });
 133    }
 134}
 135
 136struct KeymapEditor {
 137    workspace: WeakEntity<Workspace>,
 138    focus_handle: FocusHandle,
 139    _keymap_subscription: Subscription,
 140    keybindings: Vec<ProcessedKeybinding>,
 141    // corresponds 1 to 1 with keybindings
 142    string_match_candidates: Arc<Vec<StringMatchCandidate>>,
 143    matches: Vec<StringMatch>,
 144    table_interaction_state: Entity<TableInteractionState>,
 145    filter_editor: Entity<Editor>,
 146    selected_index: Option<usize>,
 147}
 148
 149impl EventEmitter<()> for KeymapEditor {}
 150
 151impl Focusable for KeymapEditor {
 152    fn focus_handle(&self, cx: &App) -> gpui::FocusHandle {
 153        return self.filter_editor.focus_handle(cx);
 154    }
 155}
 156
 157impl KeymapEditor {
 158    fn new(workspace: WeakEntity<Workspace>, window: &mut Window, cx: &mut Context<Self>) -> Self {
 159        let focus_handle = cx.focus_handle();
 160
 161        let _keymap_subscription =
 162            cx.observe_global::<KeymapEventChannel>(Self::update_keybindings);
 163        let table_interaction_state = TableInteractionState::new(window, cx);
 164
 165        let filter_editor = cx.new(|cx| {
 166            let mut editor = Editor::single_line(window, cx);
 167            editor.set_placeholder_text("Filter action names…", cx);
 168            editor
 169        });
 170
 171        cx.subscribe(&filter_editor, |this, _, e: &EditorEvent, cx| {
 172            if !matches!(e, EditorEvent::BufferEdited) {
 173                return;
 174            }
 175
 176            this.update_matches(cx);
 177        })
 178        .detach();
 179
 180        let mut this = Self {
 181            workspace,
 182            keybindings: vec![],
 183            string_match_candidates: Arc::new(vec![]),
 184            matches: vec![],
 185            focus_handle: focus_handle.clone(),
 186            _keymap_subscription,
 187            table_interaction_state,
 188            filter_editor,
 189            selected_index: None,
 190        };
 191
 192        this.update_keybindings(cx);
 193
 194        this
 195    }
 196
 197    fn current_query(&self, cx: &mut Context<Self>) -> String {
 198        self.filter_editor.read(cx).text(cx)
 199    }
 200
 201    fn update_matches(&self, cx: &mut Context<Self>) {
 202        let query = self.current_query(cx);
 203
 204        cx.spawn(async move |this, cx| Self::process_query(this, query, cx).await)
 205            .detach();
 206    }
 207
 208    async fn process_query(
 209        this: WeakEntity<Self>,
 210        query: String,
 211        cx: &mut AsyncApp,
 212    ) -> anyhow::Result<()> {
 213        let query = command_palette::normalize_action_query(&query);
 214        let (string_match_candidates, keybind_count) = this.read_with(cx, |this, _| {
 215            (this.string_match_candidates.clone(), this.keybindings.len())
 216        })?;
 217        let executor = cx.background_executor().clone();
 218        let mut matches = fuzzy::match_strings(
 219            &string_match_candidates,
 220            &query,
 221            true,
 222            true,
 223            keybind_count,
 224            &Default::default(),
 225            executor,
 226        )
 227        .await;
 228        this.update(cx, |this, cx| {
 229            if query.is_empty() {
 230                // apply default sort
 231                // sorts by source precedence, and alphabetically by action name within each source
 232                matches.sort_by_key(|match_item| {
 233                    let keybind = &this.keybindings[match_item.candidate_id];
 234                    let source = keybind.source.as_ref().map(|s| s.0);
 235                    use KeybindSource::*;
 236                    let source_precedence = match source {
 237                        Some(User) => 0,
 238                        Some(Vim) => 1,
 239                        Some(Base) => 2,
 240                        Some(Default) => 3,
 241                        None => 4,
 242                    };
 243                    return (source_precedence, keybind.action_name.as_ref());
 244                });
 245            }
 246            this.selected_index.take();
 247            this.scroll_to_item(0, ScrollStrategy::Top, cx);
 248            this.matches = matches;
 249            cx.notify();
 250        })
 251    }
 252
 253    fn process_bindings(
 254        json_language: Arc<Language>,
 255        rust_language: Arc<Language>,
 256        cx: &mut App,
 257    ) -> (Vec<ProcessedKeybinding>, Vec<StringMatchCandidate>) {
 258        let key_bindings_ptr = cx.key_bindings();
 259        let lock = key_bindings_ptr.borrow();
 260        let key_bindings = lock.bindings();
 261        let mut unmapped_action_names =
 262            HashSet::from_iter(cx.all_action_names().into_iter().copied());
 263        let action_documentation = cx.action_documentation();
 264        let mut generator = KeymapFile::action_schema_generator();
 265        let action_schema = HashMap::from_iter(
 266            cx.action_schemas(&mut generator)
 267                .into_iter()
 268                .filter_map(|(name, schema)| schema.map(|schema| (name, schema))),
 269        );
 270
 271        let mut processed_bindings = Vec::new();
 272        let mut string_match_candidates = Vec::new();
 273
 274        for key_binding in key_bindings {
 275            let source = key_binding.meta().map(settings::KeybindSource::from_meta);
 276
 277            let keystroke_text = ui::text_for_keystrokes(key_binding.keystrokes(), cx);
 278            let ui_key_binding = Some(
 279                ui::KeyBinding::new_from_gpui(key_binding.clone(), cx)
 280                    .vim_mode(source == Some(settings::KeybindSource::Vim)),
 281            );
 282
 283            let context = key_binding
 284                .predicate()
 285                .map(|predicate| {
 286                    KeybindContextString::Local(predicate.to_string().into(), rust_language.clone())
 287                })
 288                .unwrap_or(KeybindContextString::Global);
 289
 290            let source = source.map(|source| (source, source.name().into()));
 291
 292            let action_name = key_binding.action().name();
 293            unmapped_action_names.remove(&action_name);
 294            let action_input = key_binding
 295                .action_input()
 296                .map(|input| SyntaxHighlightedText::new(input, json_language.clone()));
 297            let action_docs = action_documentation.get(action_name).copied();
 298
 299            let index = processed_bindings.len();
 300            let string_match_candidate = StringMatchCandidate::new(index, &action_name);
 301            processed_bindings.push(ProcessedKeybinding {
 302                keystroke_text: keystroke_text.into(),
 303                ui_key_binding,
 304                action_name: action_name.into(),
 305                action_input,
 306                action_docs,
 307                action_schema: action_schema.get(action_name).cloned(),
 308                context: Some(context),
 309                source,
 310            });
 311            string_match_candidates.push(string_match_candidate);
 312        }
 313
 314        let empty = SharedString::new_static("");
 315        for action_name in unmapped_action_names.into_iter() {
 316            let index = processed_bindings.len();
 317            let string_match_candidate = StringMatchCandidate::new(index, &action_name);
 318            processed_bindings.push(ProcessedKeybinding {
 319                keystroke_text: empty.clone(),
 320                ui_key_binding: None,
 321                action_name: action_name.into(),
 322                action_input: None,
 323                action_docs: action_documentation.get(action_name).copied(),
 324                action_schema: action_schema.get(action_name).cloned(),
 325                context: None,
 326                source: None,
 327            });
 328            string_match_candidates.push(string_match_candidate);
 329        }
 330
 331        (processed_bindings, string_match_candidates)
 332    }
 333
 334    fn update_keybindings(&mut self, cx: &mut Context<KeymapEditor>) {
 335        let workspace = self.workspace.clone();
 336        cx.spawn(async move |this, cx| {
 337            let json_language = load_json_language(workspace.clone(), cx).await;
 338            let rust_language = load_rust_language(workspace.clone(), cx).await;
 339
 340            let query = this.update(cx, |this, cx| {
 341                let (key_bindings, string_match_candidates) =
 342                    Self::process_bindings(json_language, rust_language, cx);
 343                this.keybindings = key_bindings;
 344                this.string_match_candidates = Arc::new(string_match_candidates);
 345                this.matches = this
 346                    .string_match_candidates
 347                    .iter()
 348                    .enumerate()
 349                    .map(|(ix, candidate)| StringMatch {
 350                        candidate_id: ix,
 351                        score: 0.0,
 352                        positions: vec![],
 353                        string: candidate.string.clone(),
 354                    })
 355                    .collect();
 356                this.current_query(cx)
 357            })?;
 358            // calls cx.notify
 359            Self::process_query(this, query, cx).await
 360        })
 361        .detach_and_log_err(cx);
 362    }
 363
 364    fn dispatch_context(&self, _window: &Window, _cx: &Context<Self>) -> KeyContext {
 365        let mut dispatch_context = KeyContext::new_with_defaults();
 366        dispatch_context.add("KeymapEditor");
 367        dispatch_context.add("menu");
 368
 369        dispatch_context
 370    }
 371
 372    fn scroll_to_item(&self, index: usize, strategy: ScrollStrategy, cx: &mut App) {
 373        let index = usize::min(index, self.matches.len().saturating_sub(1));
 374        self.table_interaction_state.update(cx, |this, _cx| {
 375            this.scroll_handle.scroll_to_item(index, strategy);
 376        });
 377    }
 378
 379    fn focus_search(
 380        &mut self,
 381        _: &search::FocusSearch,
 382        window: &mut Window,
 383        cx: &mut Context<Self>,
 384    ) {
 385        if !self
 386            .filter_editor
 387            .focus_handle(cx)
 388            .contains_focused(window, cx)
 389        {
 390            window.focus(&self.filter_editor.focus_handle(cx));
 391        } else {
 392            self.filter_editor.update(cx, |editor, cx| {
 393                editor.select_all(&Default::default(), window, cx);
 394            });
 395        }
 396        self.selected_index.take();
 397    }
 398
 399    fn selected_binding(&self) -> Option<&ProcessedKeybinding> {
 400        self.selected_index
 401            .and_then(|match_index| self.matches.get(match_index))
 402            .map(|r#match| r#match.candidate_id)
 403            .and_then(|keybind_index| self.keybindings.get(keybind_index))
 404    }
 405
 406    fn select_next(&mut self, _: &menu::SelectNext, window: &mut Window, cx: &mut Context<Self>) {
 407        if let Some(selected) = self.selected_index {
 408            let selected = selected + 1;
 409            if selected >= self.matches.len() {
 410                self.select_last(&Default::default(), window, cx);
 411            } else {
 412                self.selected_index = Some(selected);
 413                self.scroll_to_item(selected, ScrollStrategy::Center, cx);
 414                cx.notify();
 415            }
 416        } else {
 417            self.select_first(&Default::default(), window, cx);
 418        }
 419    }
 420
 421    fn select_previous(
 422        &mut self,
 423        _: &menu::SelectPrevious,
 424        window: &mut Window,
 425        cx: &mut Context<Self>,
 426    ) {
 427        if let Some(selected) = self.selected_index {
 428            if selected == 0 {
 429                return;
 430            }
 431
 432            let selected = selected - 1;
 433
 434            if selected >= self.matches.len() {
 435                self.select_last(&Default::default(), window, cx);
 436            } else {
 437                self.selected_index = Some(selected);
 438                self.scroll_to_item(selected, ScrollStrategy::Center, cx);
 439                cx.notify();
 440            }
 441        } else {
 442            self.select_last(&Default::default(), window, cx);
 443        }
 444    }
 445
 446    fn select_first(
 447        &mut self,
 448        _: &menu::SelectFirst,
 449        _window: &mut Window,
 450        cx: &mut Context<Self>,
 451    ) {
 452        if self.matches.get(0).is_some() {
 453            self.selected_index = Some(0);
 454            self.scroll_to_item(0, ScrollStrategy::Center, cx);
 455            cx.notify();
 456        }
 457    }
 458
 459    fn select_last(&mut self, _: &menu::SelectLast, _window: &mut Window, cx: &mut Context<Self>) {
 460        if self.matches.last().is_some() {
 461            let index = self.matches.len() - 1;
 462            self.selected_index = Some(index);
 463            self.scroll_to_item(index, ScrollStrategy::Center, cx);
 464            cx.notify();
 465        }
 466    }
 467
 468    fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
 469        self.edit_selected_keybinding(window, cx);
 470    }
 471
 472    fn edit_selected_keybinding(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 473        let Some(keybind) = self.selected_binding() else {
 474            return;
 475        };
 476        self.workspace
 477            .update(cx, |workspace, cx| {
 478                let fs = workspace.app_state().fs.clone();
 479                let workspace_weak = cx.weak_entity();
 480                workspace.toggle_modal(window, cx, |window, cx| {
 481                    let modal =
 482                        KeybindingEditorModal::new(keybind.clone(), workspace_weak, fs, window, cx);
 483                    window.focus(&modal.focus_handle(cx));
 484                    modal
 485                });
 486            })
 487            .log_err();
 488    }
 489
 490    fn edit_binding(&mut self, _: &EditBinding, window: &mut Window, cx: &mut Context<Self>) {
 491        self.edit_selected_keybinding(window, cx);
 492    }
 493
 494    fn copy_context_to_clipboard(
 495        &mut self,
 496        _: &CopyContext,
 497        _window: &mut Window,
 498        cx: &mut Context<Self>,
 499    ) {
 500        let context = self
 501            .selected_binding()
 502            .and_then(|binding| binding.context.as_ref())
 503            .and_then(KeybindContextString::local_str)
 504            .map(|context| context.to_string());
 505        let Some(context) = context else {
 506            return;
 507        };
 508        cx.write_to_clipboard(gpui::ClipboardItem::new_string(context.clone()));
 509    }
 510
 511    fn copy_action_to_clipboard(
 512        &mut self,
 513        _: &CopyAction,
 514        _window: &mut Window,
 515        cx: &mut Context<Self>,
 516    ) {
 517        let action = self
 518            .selected_binding()
 519            .map(|binding| binding.action_name.to_string());
 520        let Some(action) = action else {
 521            return;
 522        };
 523        cx.write_to_clipboard(gpui::ClipboardItem::new_string(action.clone()));
 524    }
 525}
 526
 527#[derive(Clone)]
 528struct ProcessedKeybinding {
 529    keystroke_text: SharedString,
 530    ui_key_binding: Option<ui::KeyBinding>,
 531    action_name: SharedString,
 532    action_input: Option<SyntaxHighlightedText>,
 533    action_docs: Option<&'static str>,
 534    action_schema: Option<schemars::Schema>,
 535    context: Option<KeybindContextString>,
 536    source: Option<(KeybindSource, SharedString)>,
 537}
 538
 539#[derive(Clone, Debug, IntoElement)]
 540enum KeybindContextString {
 541    Global,
 542    Local(SharedString, Arc<Language>),
 543}
 544
 545impl KeybindContextString {
 546    const GLOBAL: SharedString = SharedString::new_static("<global>");
 547
 548    pub fn local(&self) -> Option<&SharedString> {
 549        match self {
 550            KeybindContextString::Global => None,
 551            KeybindContextString::Local(name, _) => Some(name),
 552        }
 553    }
 554
 555    pub fn local_str(&self) -> Option<&str> {
 556        match self {
 557            KeybindContextString::Global => None,
 558            KeybindContextString::Local(name, _) => Some(name),
 559        }
 560    }
 561}
 562
 563impl RenderOnce for KeybindContextString {
 564    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
 565        match self {
 566            KeybindContextString::Global => StyledText::new(KeybindContextString::GLOBAL.clone())
 567                .with_highlights([(
 568                    0..KeybindContextString::GLOBAL.len(),
 569                    gpui::HighlightStyle::color(_cx.theme().colors().text_muted),
 570                )])
 571                .into_any_element(),
 572            KeybindContextString::Local(name, language) => {
 573                SyntaxHighlightedText::new(name, language).into_any_element()
 574            }
 575        }
 576    }
 577}
 578
 579impl Item for KeymapEditor {
 580    type Event = ();
 581
 582    fn tab_content_text(&self, _detail: usize, _cx: &App) -> ui::SharedString {
 583        "Keymap Editor".into()
 584    }
 585}
 586
 587impl Render for KeymapEditor {
 588    fn render(&mut self, window: &mut Window, cx: &mut ui::Context<Self>) -> impl ui::IntoElement {
 589        let row_count = self.matches.len();
 590        let theme = cx.theme();
 591
 592        v_flex()
 593            .id("keymap-editor")
 594            .track_focus(&self.focus_handle)
 595            .key_context(self.dispatch_context(window, cx))
 596            .on_action(cx.listener(Self::select_next))
 597            .on_action(cx.listener(Self::select_previous))
 598            .on_action(cx.listener(Self::select_first))
 599            .on_action(cx.listener(Self::select_last))
 600            .on_action(cx.listener(Self::focus_search))
 601            .on_action(cx.listener(Self::confirm))
 602            .on_action(cx.listener(Self::edit_binding))
 603            .on_action(cx.listener(Self::copy_action_to_clipboard))
 604            .on_action(cx.listener(Self::copy_context_to_clipboard))
 605            .size_full()
 606            .p_2()
 607            .gap_1()
 608            .bg(theme.colors().editor_background)
 609            .child(
 610                h_flex()
 611                    .key_context({
 612                        let mut context = KeyContext::new_with_defaults();
 613                        context.add("BufferSearchBar");
 614                        context
 615                    })
 616                    .h_8()
 617                    .pl_2()
 618                    .pr_1()
 619                    .py_1()
 620                    .border_1()
 621                    .border_color(theme.colors().border)
 622                    .rounded_lg()
 623                    .child(self.filter_editor.clone()),
 624            )
 625            .child(
 626                Table::new()
 627                    .interactable(&self.table_interaction_state)
 628                    .striped()
 629                    .column_widths([rems(16.), rems(16.), rems(16.), rems(32.), rems(8.)])
 630                    .header(["Action", "Arguments", "Keystrokes", "Context", "Source"])
 631                    .uniform_list(
 632                        "keymap-editor-table",
 633                        row_count,
 634                        cx.processor(move |this, range: Range<usize>, _window, _cx| {
 635                            range
 636                                .filter_map(|index| {
 637                                    let candidate_id = this.matches.get(index)?.candidate_id;
 638                                    let binding = &this.keybindings[candidate_id];
 639
 640                                    let action = div()
 641                                        .child(binding.action_name.clone())
 642                                        .id(("keymap action", index))
 643                                        .tooltip({
 644                                            let action_name = binding.action_name.clone();
 645                                            let action_docs = binding.action_docs;
 646                                            move |_, cx| {
 647                                                let action_tooltip = Tooltip::new(
 648                                                    command_palette::humanize_action_name(
 649                                                        &action_name,
 650                                                    ),
 651                                                );
 652                                                let action_tooltip = match action_docs {
 653                                                    Some(docs) => action_tooltip.meta(docs),
 654                                                    None => action_tooltip,
 655                                                };
 656                                                cx.new(|_| action_tooltip).into()
 657                                            }
 658                                        })
 659                                        .into_any_element();
 660                                    let keystrokes = binding.ui_key_binding.clone().map_or(
 661                                        binding.keystroke_text.clone().into_any_element(),
 662                                        IntoElement::into_any_element,
 663                                    );
 664                                    let action_input = binding
 665                                        .action_input
 666                                        .clone()
 667                                        .map_or(gpui::Empty.into_any_element(), |input| {
 668                                            input.into_any_element()
 669                                        });
 670                                    let context = binding
 671                                        .context
 672                                        .clone()
 673                                        .map_or(gpui::Empty.into_any_element(), |context| {
 674                                            context.into_any_element()
 675                                        });
 676                                    let source = binding
 677                                        .source
 678                                        .clone()
 679                                        .map(|(_source, name)| name)
 680                                        .unwrap_or_default()
 681                                        .into_any_element();
 682                                    Some([action, action_input, keystrokes, context, source])
 683                                })
 684                                .collect()
 685                        }),
 686                    )
 687                    .map_row(
 688                        cx.processor(|this, (row_index, row): (usize, Div), _window, cx| {
 689                            let is_selected = this.selected_index == Some(row_index);
 690                            let row = row
 691                                .id(("keymap-table-row", row_index))
 692                                .on_click(cx.listener(move |this, _event, _window, _cx| {
 693                                    this.selected_index = Some(row_index);
 694                                }))
 695                                .border_2()
 696                                .border_color(transparent_black())
 697                                .when(is_selected, |row| {
 698                                    row.border_color(cx.theme().colors().panel_focused_border)
 699                                });
 700
 701                            right_click_menu(("keymap-table-row-menu", row_index))
 702                                .trigger({
 703                                    let this = cx.weak_entity();
 704                                    move |is_menu_open: bool, _window, cx| {
 705                                        if is_menu_open {
 706                                            this.update(cx, |this, cx| {
 707                                                if this.selected_index != Some(row_index) {
 708                                                    this.selected_index = Some(row_index);
 709                                                    cx.notify();
 710                                                }
 711                                            })
 712                                            .ok();
 713                                        }
 714                                        row
 715                                    }
 716                                })
 717                                .menu({
 718                                    let this = cx.weak_entity();
 719                                    move |window, cx| build_keybind_context_menu(&this, window, cx)
 720                                })
 721                                .into_any_element()
 722                        }),
 723                    ),
 724            )
 725    }
 726}
 727
 728#[derive(Debug, Clone, IntoElement)]
 729struct SyntaxHighlightedText {
 730    text: SharedString,
 731    language: Arc<Language>,
 732}
 733
 734impl SyntaxHighlightedText {
 735    pub fn new(text: impl Into<SharedString>, language: Arc<Language>) -> Self {
 736        Self {
 737            text: text.into(),
 738            language,
 739        }
 740    }
 741}
 742
 743impl RenderOnce for SyntaxHighlightedText {
 744    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
 745        let text_style = window.text_style();
 746        let syntax_theme = cx.theme().syntax();
 747
 748        let text = self.text.clone();
 749
 750        let highlights = self
 751            .language
 752            .highlight_text(&text.as_ref().into(), 0..text.len());
 753        let mut runs = Vec::with_capacity(highlights.len());
 754        let mut offset = 0;
 755
 756        for (highlight_range, highlight_id) in highlights {
 757            // Add un-highlighted text before the current highlight
 758            if highlight_range.start > offset {
 759                runs.push(text_style.to_run(highlight_range.start - offset));
 760            }
 761
 762            let mut run_style = text_style.clone();
 763            if let Some(highlight_style) = highlight_id.style(syntax_theme) {
 764                run_style = run_style.highlight(highlight_style);
 765            }
 766            // add the highlighted range
 767            runs.push(run_style.to_run(highlight_range.len()));
 768            offset = highlight_range.end;
 769        }
 770
 771        // Add any remaining un-highlighted text
 772        if offset < text.len() {
 773            runs.push(text_style.to_run(text.len() - offset));
 774        }
 775
 776        return StyledText::new(text).with_runs(runs);
 777    }
 778}
 779
 780struct KeybindingEditorModal {
 781    editing_keybind: ProcessedKeybinding,
 782    keybind_editor: Entity<KeystrokeInput>,
 783    context_editor: Entity<Editor>,
 784    input_editor: Option<Entity<Editor>>,
 785    fs: Arc<dyn Fs>,
 786    error: Option<String>,
 787}
 788
 789impl ModalView for KeybindingEditorModal {}
 790
 791impl EventEmitter<DismissEvent> for KeybindingEditorModal {}
 792
 793impl Focusable for KeybindingEditorModal {
 794    fn focus_handle(&self, cx: &App) -> FocusHandle {
 795        self.keybind_editor.focus_handle(cx)
 796    }
 797}
 798
 799impl KeybindingEditorModal {
 800    pub fn new(
 801        editing_keybind: ProcessedKeybinding,
 802        workspace: WeakEntity<Workspace>,
 803        fs: Arc<dyn Fs>,
 804        window: &mut Window,
 805        cx: &mut App,
 806    ) -> Self {
 807        let keybind_editor = cx.new(KeystrokeInput::new);
 808
 809        let context_editor = cx.new(|cx| {
 810            let mut editor = Editor::single_line(window, cx);
 811
 812            if let Some(context) = editing_keybind
 813                .context
 814                .as_ref()
 815                .and_then(KeybindContextString::local)
 816            {
 817                editor.set_text(context.clone(), window, cx);
 818            } else {
 819                editor.set_placeholder_text("Keybinding context", cx);
 820            }
 821
 822            cx.spawn(async |editor, cx| {
 823                let contexts = cx
 824                    .background_spawn(async { collect_contexts_from_assets() })
 825                    .await;
 826
 827                editor
 828                    .update(cx, |editor, _cx| {
 829                        editor.set_completion_provider(Some(std::rc::Rc::new(
 830                            KeyContextCompletionProvider { contexts },
 831                        )));
 832                    })
 833                    .context("Failed to load completions for keybinding context")
 834            })
 835            .detach_and_log_err(cx);
 836
 837            editor
 838        });
 839
 840        let input_editor = editing_keybind.action_schema.clone().map(|_schema| {
 841            cx.new(|cx| {
 842                let mut editor = Editor::auto_height_unbounded(1, window, cx);
 843                if let Some(input) = editing_keybind.action_input.clone() {
 844                    editor.set_text(input.text, window, cx);
 845                } else {
 846                    // TODO: default value from schema?
 847                    editor.set_placeholder_text("Action input", cx);
 848                }
 849                cx.spawn(async |editor, cx| {
 850                    let json_language = load_json_language(workspace, cx).await;
 851                    editor
 852                        .update(cx, |editor, cx| {
 853                            if let Some(buffer) = editor.buffer().read(cx).as_singleton() {
 854                                buffer.update(cx, |buffer, cx| {
 855                                    buffer.set_language(Some(json_language), cx)
 856                                });
 857                            }
 858                        })
 859                        .context("Failed to load JSON language for editing keybinding action input")
 860                })
 861                .detach_and_log_err(cx);
 862                editor
 863            })
 864        });
 865
 866        Self {
 867            editing_keybind,
 868            fs,
 869            keybind_editor,
 870            context_editor,
 871            input_editor,
 872            error: None,
 873        }
 874    }
 875
 876    fn save(&mut self, cx: &mut Context<Self>) {
 877        let existing_keybind = self.editing_keybind.clone();
 878        let fs = self.fs.clone();
 879        let new_keystrokes = self
 880            .keybind_editor
 881            .read_with(cx, |editor, _| editor.keystrokes().to_vec());
 882        if new_keystrokes.is_empty() {
 883            self.error = Some("Keystrokes cannot be empty".to_string());
 884            cx.notify();
 885            return;
 886        }
 887        let tab_size = cx.global::<settings::SettingsStore>().json_tab_size();
 888        let new_context = self
 889            .context_editor
 890            .read_with(cx, |editor, cx| editor.text(cx));
 891        let new_context = new_context.is_empty().not().then_some(new_context);
 892        let new_context_err = new_context.as_deref().and_then(|context| {
 893            gpui::KeyBindingContextPredicate::parse(context)
 894                .context("Failed to parse key context")
 895                .err()
 896        });
 897        if let Some(err) = new_context_err {
 898            // TODO: store and display as separate error
 899            // TODO: also, should be validating on keystroke
 900            self.error = Some(err.to_string());
 901            cx.notify();
 902            return;
 903        }
 904
 905        cx.spawn(async move |this, cx| {
 906            if let Err(err) = save_keybinding_update(
 907                existing_keybind,
 908                &new_keystrokes,
 909                new_context.as_deref(),
 910                &fs,
 911                tab_size,
 912            )
 913            .await
 914            {
 915                this.update(cx, |this, cx| {
 916                    this.error = Some(err.to_string());
 917                    cx.notify();
 918                })
 919                .log_err();
 920            } else {
 921                this.update(cx, |_this, cx| {
 922                    cx.emit(DismissEvent);
 923                })
 924                .ok();
 925            }
 926        })
 927        .detach();
 928    }
 929}
 930
 931impl Render for KeybindingEditorModal {
 932    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
 933        let theme = cx.theme().colors();
 934
 935        return v_flex()
 936            .w(rems(34.))
 937            .elevation_3(cx)
 938            .child(
 939                v_flex()
 940                    .p_3()
 941                    .gap_2()
 942                    .child(
 943                        v_flex().child(Label::new("Edit Keystroke")).child(
 944                            Label::new("Input the desired keystroke for the selected action.")
 945                                .color(Color::Muted),
 946                        ),
 947                    )
 948                    .child(self.keybind_editor.clone()),
 949            )
 950            .when_some(self.input_editor.clone(), |this, editor| {
 951                this.child(
 952                    v_flex()
 953                        .p_3()
 954                        .gap_3()
 955                        .child(
 956                            v_flex().child(Label::new("Edit Input")).child(
 957                                Label::new("Input the desired input to the binding.")
 958                                    .color(Color::Muted),
 959                            ),
 960                        )
 961                        .child(
 962                            div()
 963                                .w_full()
 964                                .border_color(cx.theme().colors().border_variant)
 965                                .border_1()
 966                                .py_2()
 967                                .px_3()
 968                                .min_h_8()
 969                                .rounded_md()
 970                                .bg(theme.editor_background)
 971                                .child(editor),
 972                        ),
 973                )
 974            })
 975            .child(
 976                v_flex()
 977                    .p_3()
 978                    .gap_3()
 979                    .child(
 980                        v_flex().child(Label::new("Edit Context")).child(
 981                            Label::new("Input the desired context for the binding.")
 982                                .color(Color::Muted),
 983                        ),
 984                    )
 985                    .child(
 986                        div()
 987                            .w_full()
 988                            .border_color(cx.theme().colors().border_variant)
 989                            .border_1()
 990                            .py_2()
 991                            .px_3()
 992                            .min_h_8()
 993                            .rounded_md()
 994                            .bg(theme.editor_background)
 995                            .child(self.context_editor.clone()),
 996                    ),
 997            )
 998            .child(
 999                h_flex()
1000                    .p_2()
1001                    .w_full()
1002                    .gap_1()
1003                    .justify_end()
1004                    .border_t_1()
1005                    .border_color(cx.theme().colors().border_variant)
1006                    .child(
1007                        Button::new("cancel", "Cancel")
1008                            .on_click(cx.listener(|_, _, _, cx| cx.emit(DismissEvent))),
1009                    )
1010                    .child(
1011                        Button::new("save-btn", "Save").on_click(
1012                            cx.listener(|this, _event, _window, cx| Self::save(this, cx)),
1013                        ),
1014                    ),
1015            )
1016            .when_some(self.error.clone(), |this, error| {
1017                this.child(
1018                    div()
1019                        .bg(theme.background)
1020                        .border_color(theme.border)
1021                        .border_2()
1022                        .rounded_md()
1023                        .child(error),
1024                )
1025            });
1026    }
1027}
1028
1029struct KeyContextCompletionProvider {
1030    contexts: Vec<SharedString>,
1031}
1032
1033impl CompletionProvider for KeyContextCompletionProvider {
1034    fn completions(
1035        &self,
1036        _excerpt_id: editor::ExcerptId,
1037        buffer: &Entity<language::Buffer>,
1038        buffer_position: language::Anchor,
1039        _trigger: editor::CompletionContext,
1040        _window: &mut Window,
1041        cx: &mut Context<Editor>,
1042    ) -> gpui::Task<anyhow::Result<Vec<project::CompletionResponse>>> {
1043        let buffer = buffer.read(cx);
1044        let mut count_back = 0;
1045        for char in buffer.reversed_chars_at(buffer_position) {
1046            if char.is_ascii_alphanumeric() || char == '_' {
1047                count_back += 1;
1048            } else {
1049                break;
1050            }
1051        }
1052        let start_anchor = buffer.anchor_before(
1053            buffer_position
1054                .to_offset(&buffer)
1055                .saturating_sub(count_back),
1056        );
1057        let replace_range = start_anchor..buffer_position;
1058        gpui::Task::ready(Ok(vec![project::CompletionResponse {
1059            completions: self
1060                .contexts
1061                .iter()
1062                .map(|context| project::Completion {
1063                    replace_range: replace_range.clone(),
1064                    label: language::CodeLabel::plain(context.to_string(), None),
1065                    new_text: context.to_string(),
1066                    documentation: None,
1067                    source: project::CompletionSource::Custom,
1068                    icon_path: None,
1069                    insert_text_mode: None,
1070                    confirm: None,
1071                })
1072                .collect(),
1073            is_incomplete: false,
1074        }]))
1075    }
1076
1077    fn is_completion_trigger(
1078        &self,
1079        _buffer: &Entity<language::Buffer>,
1080        _position: language::Anchor,
1081        text: &str,
1082        _trigger_in_words: bool,
1083        _menu_is_open: bool,
1084        _cx: &mut Context<Editor>,
1085    ) -> bool {
1086        text.chars().last().map_or(false, |last_char| {
1087            last_char.is_ascii_alphanumeric() || last_char == '_'
1088        })
1089    }
1090}
1091
1092async fn load_json_language(workspace: WeakEntity<Workspace>, cx: &mut AsyncApp) -> Arc<Language> {
1093    let json_language_task = workspace
1094        .read_with(cx, |workspace, cx| {
1095            workspace
1096                .project()
1097                .read(cx)
1098                .languages()
1099                .language_for_name("JSON")
1100        })
1101        .context("Failed to load JSON language")
1102        .log_err();
1103    let json_language = match json_language_task {
1104        Some(task) => task.await.context("Failed to load JSON language").log_err(),
1105        None => None,
1106    };
1107    return json_language.unwrap_or_else(|| {
1108        Arc::new(Language::new(
1109            LanguageConfig {
1110                name: "JSON".into(),
1111                ..Default::default()
1112            },
1113            Some(tree_sitter_json::LANGUAGE.into()),
1114        ))
1115    });
1116}
1117
1118async fn load_rust_language(workspace: WeakEntity<Workspace>, cx: &mut AsyncApp) -> Arc<Language> {
1119    let rust_language_task = workspace
1120        .read_with(cx, |workspace, cx| {
1121            workspace
1122                .project()
1123                .read(cx)
1124                .languages()
1125                .language_for_name("Rust")
1126        })
1127        .context("Failed to load Rust language")
1128        .log_err();
1129    let rust_language = match rust_language_task {
1130        Some(task) => task.await.context("Failed to load Rust language").log_err(),
1131        None => None,
1132    };
1133    return rust_language.unwrap_or_else(|| {
1134        Arc::new(Language::new(
1135            LanguageConfig {
1136                name: "Rust".into(),
1137                ..Default::default()
1138            },
1139            Some(tree_sitter_rust::LANGUAGE.into()),
1140        ))
1141    });
1142}
1143
1144async fn save_keybinding_update(
1145    existing: ProcessedKeybinding,
1146    new_keystrokes: &[Keystroke],
1147    new_context: Option<&str>,
1148    fs: &Arc<dyn Fs>,
1149    tab_size: usize,
1150) -> anyhow::Result<()> {
1151    let keymap_contents = settings::KeymapFile::load_keymap_file(fs)
1152        .await
1153        .context("Failed to load keymap file")?;
1154
1155    let existing_keystrokes = existing
1156        .ui_key_binding
1157        .as_ref()
1158        .map(|keybinding| keybinding.keystrokes.as_slice())
1159        .unwrap_or_default();
1160
1161    let existing_context = existing
1162        .context
1163        .as_ref()
1164        .and_then(KeybindContextString::local_str);
1165
1166    let input = existing
1167        .action_input
1168        .as_ref()
1169        .map(|input| input.text.as_ref());
1170
1171    let operation = if existing.ui_key_binding.is_some() {
1172        settings::KeybindUpdateOperation::Replace {
1173            target: settings::KeybindUpdateTarget {
1174                context: existing_context,
1175                keystrokes: existing_keystrokes,
1176                action_name: &existing.action_name,
1177                use_key_equivalents: false,
1178                input,
1179            },
1180            target_keybind_source: existing
1181                .source
1182                .map(|(source, _name)| source)
1183                .unwrap_or(KeybindSource::User),
1184            source: settings::KeybindUpdateTarget {
1185                context: new_context,
1186                keystrokes: new_keystrokes,
1187                action_name: &existing.action_name,
1188                use_key_equivalents: false,
1189                input,
1190            },
1191        }
1192    } else {
1193        anyhow::bail!("Adding new bindings not implemented yet");
1194    };
1195    let updated_keymap_contents =
1196        settings::KeymapFile::update_keybinding(operation, keymap_contents, tab_size)
1197            .context("Failed to update keybinding")?;
1198    fs.atomic_write(paths::keymap_file().clone(), updated_keymap_contents)
1199        .await
1200        .context("Failed to write keymap file")?;
1201    Ok(())
1202}
1203
1204struct KeystrokeInput {
1205    keystrokes: Vec<Keystroke>,
1206    focus_handle: FocusHandle,
1207}
1208
1209impl KeystrokeInput {
1210    fn new(cx: &mut Context<Self>) -> Self {
1211        let focus_handle = cx.focus_handle();
1212        Self {
1213            keystrokes: Vec::new(),
1214            focus_handle,
1215        }
1216    }
1217
1218    fn on_modifiers_changed(
1219        &mut self,
1220        event: &ModifiersChangedEvent,
1221        _window: &mut Window,
1222        cx: &mut Context<Self>,
1223    ) {
1224        if let Some(last) = self.keystrokes.last_mut()
1225            && last.key.is_empty()
1226        {
1227            if !event.modifiers.modified() {
1228                self.keystrokes.pop();
1229            } else {
1230                last.modifiers = event.modifiers;
1231            }
1232        } else {
1233            self.keystrokes.push(Keystroke {
1234                modifiers: event.modifiers,
1235                key: "".to_string(),
1236                key_char: None,
1237            });
1238        }
1239        cx.stop_propagation();
1240        cx.notify();
1241    }
1242
1243    fn on_key_down(
1244        &mut self,
1245        event: &gpui::KeyDownEvent,
1246        _window: &mut Window,
1247        cx: &mut Context<Self>,
1248    ) {
1249        if event.is_held {
1250            return;
1251        }
1252        if let Some(last) = self.keystrokes.last_mut()
1253            && last.key.is_empty()
1254        {
1255            *last = event.keystroke.clone();
1256        } else {
1257            self.keystrokes.push(event.keystroke.clone());
1258        }
1259        cx.stop_propagation();
1260        cx.notify();
1261    }
1262
1263    fn on_key_up(
1264        &mut self,
1265        event: &gpui::KeyUpEvent,
1266        _window: &mut Window,
1267        cx: &mut Context<Self>,
1268    ) {
1269        if let Some(last) = self.keystrokes.last_mut()
1270            && !last.key.is_empty()
1271            && last.modifiers == event.keystroke.modifiers
1272        {
1273            self.keystrokes.push(Keystroke {
1274                modifiers: event.keystroke.modifiers,
1275                key: "".to_string(),
1276                key_char: None,
1277            });
1278        }
1279        cx.stop_propagation();
1280        cx.notify();
1281    }
1282
1283    fn keystrokes(&self) -> &[Keystroke] {
1284        if self
1285            .keystrokes
1286            .last()
1287            .map_or(false, |last| last.key.is_empty())
1288        {
1289            return &self.keystrokes[..self.keystrokes.len() - 1];
1290        }
1291        return &self.keystrokes;
1292    }
1293}
1294
1295impl Focusable for KeystrokeInput {
1296    fn focus_handle(&self, _cx: &App) -> FocusHandle {
1297        self.focus_handle.clone()
1298    }
1299}
1300
1301impl Render for KeystrokeInput {
1302    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
1303        let colors = cx.theme().colors();
1304
1305        return h_flex()
1306            .id("keybinding_input")
1307            .track_focus(&self.focus_handle)
1308            .on_modifiers_changed(cx.listener(Self::on_modifiers_changed))
1309            .on_key_down(cx.listener(Self::on_key_down))
1310            .on_key_up(cx.listener(Self::on_key_up))
1311            .focus(|mut style| {
1312                style.border_color = Some(colors.border_focused);
1313                style
1314            })
1315            .py_2()
1316            .px_3()
1317            .gap_2()
1318            .min_h_8()
1319            .w_full()
1320            .justify_between()
1321            .bg(colors.editor_background)
1322            .border_1()
1323            .rounded_md()
1324            .flex_1()
1325            .overflow_hidden()
1326            .child(
1327                h_flex()
1328                    .w_full()
1329                    .min_w_0()
1330                    .justify_center()
1331                    .flex_wrap()
1332                    .gap(ui::DynamicSpacing::Base04.rems(cx))
1333                    .children(self.keystrokes.iter().map(|keystroke| {
1334                        h_flex().children(ui::render_keystroke(
1335                            keystroke,
1336                            None,
1337                            Some(rems(0.875).into()),
1338                            ui::PlatformStyle::platform(),
1339                            false,
1340                        ))
1341                    })),
1342            )
1343            .child(
1344                h_flex()
1345                    .gap_0p5()
1346                    .flex_none()
1347                    .child(
1348                        IconButton::new("backspace-btn", IconName::Delete)
1349                            .tooltip(Tooltip::text("Delete Keystroke"))
1350                            .on_click(cx.listener(|this, _event, _window, cx| {
1351                                this.keystrokes.pop();
1352                                cx.notify();
1353                            })),
1354                    )
1355                    .child(
1356                        IconButton::new("clear-btn", IconName::Eraser)
1357                            .tooltip(Tooltip::text("Clear Keystrokes"))
1358                            .on_click(cx.listener(|this, _event, _window, cx| {
1359                                this.keystrokes.clear();
1360                                cx.notify();
1361                            })),
1362                    ),
1363            );
1364    }
1365}
1366
1367fn build_keybind_context_menu(
1368    this: &WeakEntity<KeymapEditor>,
1369    window: &mut Window,
1370    cx: &mut App,
1371) -> Entity<ContextMenu> {
1372    ContextMenu::build(window, cx, |menu, _window, cx| {
1373        let Some(this) = this.upgrade() else {
1374            return menu;
1375        };
1376        let selected_binding = this.read_with(cx, |this, _cx| this.selected_binding().cloned());
1377        let Some(selected_binding) = selected_binding else {
1378            return menu;
1379        };
1380
1381        let selected_binding_has_context = selected_binding
1382            .context
1383            .as_ref()
1384            .and_then(KeybindContextString::local)
1385            .is_some();
1386
1387        menu.action("Edit Binding", Box::new(EditBinding))
1388            .action("Copy action", Box::new(CopyAction))
1389            .action_disabled_when(
1390                !selected_binding_has_context,
1391                "Copy Context",
1392                Box::new(CopyContext),
1393            )
1394    })
1395}
1396
1397fn collect_contexts_from_assets() -> Vec<SharedString> {
1398    let mut keymap_assets = vec![
1399        util::asset_str::<SettingsAssets>(settings::DEFAULT_KEYMAP_PATH),
1400        util::asset_str::<SettingsAssets>(settings::VIM_KEYMAP_PATH),
1401    ];
1402    keymap_assets.extend(
1403        BaseKeymap::OPTIONS
1404            .iter()
1405            .filter_map(|(_, base_keymap)| base_keymap.asset_path())
1406            .map(util::asset_str::<SettingsAssets>),
1407    );
1408
1409    let mut contexts = HashSet::default();
1410
1411    for keymap_asset in keymap_assets {
1412        let Ok(keymap) = KeymapFile::parse(&keymap_asset) else {
1413            continue;
1414        };
1415
1416        for section in keymap.sections() {
1417            let context_expr = &section.context;
1418            let mut queue = Vec::new();
1419            let Ok(root_context) = gpui::KeyBindingContextPredicate::parse(context_expr) else {
1420                continue;
1421            };
1422
1423            queue.push(root_context);
1424            while let Some(context) = queue.pop() {
1425                match context {
1426                    gpui::KeyBindingContextPredicate::Identifier(ident) => {
1427                        contexts.insert(ident);
1428                    }
1429                    gpui::KeyBindingContextPredicate::Equal(ident_a, ident_b) => {
1430                        contexts.insert(ident_a);
1431                        contexts.insert(ident_b);
1432                    }
1433                    gpui::KeyBindingContextPredicate::NotEqual(ident_a, ident_b) => {
1434                        contexts.insert(ident_a);
1435                        contexts.insert(ident_b);
1436                    }
1437                    gpui::KeyBindingContextPredicate::Child(ctx_a, ctx_b) => {
1438                        queue.push(*ctx_a);
1439                        queue.push(*ctx_b);
1440                    }
1441                    gpui::KeyBindingContextPredicate::Not(ctx) => {
1442                        queue.push(*ctx);
1443                    }
1444                    gpui::KeyBindingContextPredicate::And(ctx_a, ctx_b) => {
1445                        queue.push(*ctx_a);
1446                        queue.push(*ctx_b);
1447                    }
1448                    gpui::KeyBindingContextPredicate::Or(ctx_a, ctx_b) => {
1449                        queue.push(*ctx_a);
1450                        queue.push(*ctx_b);
1451                    }
1452                }
1453            }
1454        }
1455    }
1456
1457    let mut contexts = contexts.into_iter().collect::<Vec<_>>();
1458    contexts.sort();
1459
1460    return contexts;
1461}
1462
1463impl SerializableItem for KeymapEditor {
1464    fn serialized_item_kind() -> &'static str {
1465        "KeymapEditor"
1466    }
1467
1468    fn cleanup(
1469        workspace_id: workspace::WorkspaceId,
1470        alive_items: Vec<workspace::ItemId>,
1471        _window: &mut Window,
1472        cx: &mut App,
1473    ) -> gpui::Task<gpui::Result<()>> {
1474        workspace::delete_unloaded_items(
1475            alive_items,
1476            workspace_id,
1477            "keybinding_editors",
1478            &KEYBINDING_EDITORS,
1479            cx,
1480        )
1481    }
1482
1483    fn deserialize(
1484        _project: Entity<project::Project>,
1485        workspace: WeakEntity<Workspace>,
1486        workspace_id: workspace::WorkspaceId,
1487        item_id: workspace::ItemId,
1488        window: &mut Window,
1489        cx: &mut App,
1490    ) -> gpui::Task<gpui::Result<Entity<Self>>> {
1491        window.spawn(cx, async move |cx| {
1492            if KEYBINDING_EDITORS
1493                .get_keybinding_editor(item_id, workspace_id)?
1494                .is_some()
1495            {
1496                cx.update(|window, cx| cx.new(|cx| KeymapEditor::new(workspace, window, cx)))
1497            } else {
1498                Err(anyhow!("No keybinding editor to deserialize"))
1499            }
1500        })
1501    }
1502
1503    fn serialize(
1504        &mut self,
1505        workspace: &mut Workspace,
1506        item_id: workspace::ItemId,
1507        _closing: bool,
1508        _window: &mut Window,
1509        cx: &mut ui::Context<Self>,
1510    ) -> Option<gpui::Task<gpui::Result<()>>> {
1511        let workspace_id = workspace.database_id()?;
1512        Some(cx.background_spawn(async move {
1513            KEYBINDING_EDITORS
1514                .save_keybinding_editor(item_id, workspace_id)
1515                .await
1516        }))
1517    }
1518
1519    fn should_serialize(&self, _event: &Self::Event) -> bool {
1520        false
1521    }
1522}
1523
1524mod persistence {
1525    use db::{define_connection, query, sqlez_macros::sql};
1526    use workspace::WorkspaceDb;
1527
1528    define_connection! {
1529        pub static ref KEYBINDING_EDITORS: KeybindingEditorDb<WorkspaceDb> =
1530            &[sql!(
1531                CREATE TABLE keybinding_editors (
1532                    workspace_id INTEGER,
1533                    item_id INTEGER UNIQUE,
1534
1535                    PRIMARY KEY(workspace_id, item_id),
1536                    FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id)
1537                    ON DELETE CASCADE
1538                ) STRICT;
1539            )];
1540    }
1541
1542    impl KeybindingEditorDb {
1543        query! {
1544            pub async fn save_keybinding_editor(
1545                item_id: workspace::ItemId,
1546                workspace_id: workspace::WorkspaceId
1547            ) -> Result<()> {
1548                INSERT OR REPLACE INTO keybinding_editors(item_id, workspace_id)
1549                VALUES (?, ?)
1550            }
1551        }
1552
1553        query! {
1554            pub fn get_keybinding_editor(
1555                item_id: workspace::ItemId,
1556                workspace_id: workspace::WorkspaceId
1557            ) -> Result<Option<workspace::ItemId>> {
1558                SELECT item_id
1559                FROM keybinding_editors
1560                WHERE item_id = ? AND workspace_id = ?
1561            }
1562        }
1563    }
1564}