keymap_ui: Syntax highlight context (#33864)

Ben Kunkle created

Closes #ISSUE

Uses Rust for syntax highlighting of context in the keymap editor.
Future pass will improve color choice to make colors less abrasive

Release Notes:

- N/A *or* Added/Fixed/Improved ...

Change summary

Cargo.lock                            |  1 
crates/settings_ui/Cargo.toml         |  1 
crates/settings_ui/src/keybindings.rs | 56 ++++++++++++++++++++++++----
3 files changed, 50 insertions(+), 8 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -14590,6 +14590,7 @@ dependencies = [
  "settings",
  "theme",
  "tree-sitter-json",
+ "tree-sitter-rust",
  "ui",
  "util",
  "workspace",

crates/settings_ui/Cargo.toml 🔗

@@ -34,6 +34,7 @@ serde.workspace = true
 settings.workspace = true
 theme.workspace = true
 tree-sitter-json.workspace = true
+tree-sitter-rust.workspace = true
 ui.workspace = true
 util.workspace = true
 workspace-hack.workspace = true

crates/settings_ui/src/keybindings.rs 🔗

@@ -249,6 +249,7 @@ impl KeymapEditor {
 
     fn process_bindings(
         json_language: Arc<Language>,
+        rust_language: Arc<Language>,
         cx: &mut App,
     ) -> (Vec<ProcessedKeybinding>, Vec<StringMatchCandidate>) {
         let key_bindings_ptr = cx.key_bindings();
@@ -272,7 +273,9 @@ impl KeymapEditor {
 
             let context = key_binding
                 .predicate()
-                .map(|predicate| KeybindContextString::Local(predicate.to_string().into()))
+                .map(|predicate| {
+                    KeybindContextString::Local(predicate.to_string().into(), rust_language.clone())
+                })
                 .unwrap_or(KeybindContextString::Global);
 
             let source = source.map(|source| (source, source.name().into()));
@@ -320,11 +323,12 @@ impl KeymapEditor {
     fn update_keybindings(&mut self, cx: &mut Context<KeymapEditor>) {
         let workspace = self.workspace.clone();
         cx.spawn(async move |this, cx| {
-            let json_language = Self::load_json_language(workspace, cx).await;
+            let json_language = Self::load_json_language(workspace.clone(), cx).await;
+            let rust_language = Self::load_rust_language(workspace.clone(), cx).await;
 
             let query = this.update(cx, |this, cx| {
                 let (key_bindings, string_match_candidates) =
-                    Self::process_bindings(json_language.clone(), cx);
+                    Self::process_bindings(json_language, rust_language, cx);
                 this.keybindings = key_bindings;
                 this.string_match_candidates = Arc::new(string_match_candidates);
                 this.matches = this
@@ -375,6 +379,35 @@ impl KeymapEditor {
         });
     }
 
+    async fn load_rust_language(
+        workspace: WeakEntity<Workspace>,
+        cx: &mut AsyncApp,
+    ) -> Arc<Language> {
+        let rust_language_task = workspace
+            .read_with(cx, |workspace, cx| {
+                workspace
+                    .project()
+                    .read(cx)
+                    .languages()
+                    .language_for_name("Rust")
+            })
+            .context("Failed to load Rust language")
+            .log_err();
+        let rust_language = match rust_language_task {
+            Some(task) => task.await.context("Failed to load Rust language").log_err(),
+            None => None,
+        };
+        return rust_language.unwrap_or_else(|| {
+            Arc::new(Language::new(
+                LanguageConfig {
+                    name: "Rust".into(),
+                    ..Default::default()
+                },
+                Some(tree_sitter_rust::LANGUAGE.into()),
+            ))
+        });
+    }
+
     fn dispatch_context(&self, _window: &Window, _cx: &Context<Self>) -> KeyContext {
         let mut dispatch_context = KeyContext::new_with_defaults();
         dispatch_context.add("KeymapEditor");
@@ -550,7 +583,7 @@ struct ProcessedKeybinding {
 #[derive(Clone, Debug, IntoElement)]
 enum KeybindContextString {
     Global,
-    Local(SharedString),
+    Local(SharedString, Arc<Language>),
 }
 
 impl KeybindContextString {
@@ -559,14 +592,14 @@ impl KeybindContextString {
     pub fn local(&self) -> Option<&SharedString> {
         match self {
             KeybindContextString::Global => None,
-            KeybindContextString::Local(name) => Some(name),
+            KeybindContextString::Local(name, _) => Some(name),
         }
     }
 
     pub fn local_str(&self) -> Option<&str> {
         match self {
             KeybindContextString::Global => None,
-            KeybindContextString::Local(name) => Some(name),
+            KeybindContextString::Local(name, _) => Some(name),
         }
     }
 }
@@ -574,8 +607,15 @@ impl KeybindContextString {
 impl RenderOnce for KeybindContextString {
     fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
         match self {
-            KeybindContextString::Global => KeybindContextString::GLOBAL.clone(),
-            KeybindContextString::Local(name) => name,
+            KeybindContextString::Global => StyledText::new(KeybindContextString::GLOBAL.clone())
+                .with_highlights([(
+                    0..KeybindContextString::GLOBAL.len(),
+                    gpui::HighlightStyle::color(_cx.theme().colors().text_muted),
+                )])
+                .into_any_element(),
+            KeybindContextString::Local(name, language) => {
+                SyntaxHighlightedText::new(name, language).into_any_element()
+            }
         }
     }
 }