From 686f5439ad033d61d72dcbf22f14a19d3ad45057 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 24 Feb 2023 16:28:56 +0100 Subject: [PATCH] Set buffer language when confirming selection in language selector Co-Authored-By: Julia Risley --- Cargo.lock | 1 + crates/editor/src/editor.rs | 9 ++ crates/language_selector/Cargo.toml | 1 + .../src/language_selector.rs | 83 ++++++++++++------- crates/project/src/project.rs | 36 +++++--- 5 files changed, 89 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 94e7af6766..0f19d4455f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3290,6 +3290,7 @@ dependencies = [ name = "language_selector" version = "0.1.0" dependencies = [ + "anyhow", "editor", "fuzzy", "gpui", diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 3e7a14d2ae..7f60309f6a 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1254,6 +1254,15 @@ impl Editor { self.buffer.read(cx).language_at(point, cx) } + pub fn active_excerpt( + &self, + cx: &AppContext, + ) -> Option<(ExcerptId, ModelHandle, Range)> { + self.buffer + .read(cx) + .excerpt_containing(self.selections.newest_anchor().head(), cx) + } + fn style(&self, cx: &AppContext) -> EditorStyle { build_style( cx.global::(), diff --git a/crates/language_selector/Cargo.toml b/crates/language_selector/Cargo.toml index 60ed0e6633..14aa41a465 100644 --- a/crates/language_selector/Cargo.toml +++ b/crates/language_selector/Cargo.toml @@ -18,3 +18,4 @@ project = { path = "../project" } theme = { path = "../theme" } settings = { path = "../settings" } workspace = { path = "../workspace" } +anyhow = "1.0" diff --git a/crates/language_selector/src/language_selector.rs b/crates/language_selector/src/language_selector.rs index d48a50ce93..5a2918660e 100644 --- a/crates/language_selector/src/language_selector.rs +++ b/crates/language_selector/src/language_selector.rs @@ -1,12 +1,14 @@ use std::sync::Arc; +use editor::Editor; use fuzzy::{match_strings, StringMatch, StringMatchCandidate}; use gpui::{ - actions, elements::*, AnyViewHandle, AppContext, Entity, MouseState, MutableAppContext, - RenderContext, View, ViewContext, ViewHandle, + actions, elements::*, AnyViewHandle, AppContext, Entity, ModelHandle, MouseState, + MutableAppContext, RenderContext, View, ViewContext, ViewHandle, }; -use language::LanguageRegistry; +use language::{Buffer, LanguageRegistry}; use picker::{Picker, PickerDelegate}; +use project::Project; use settings::Settings; use workspace::{AppState, Workspace}; @@ -27,32 +29,47 @@ pub enum Event { } pub struct LanguageSelector { + buffer: ModelHandle, + project: ModelHandle, language_registry: Arc, + candidates: Vec, matches: Vec, picker: ViewHandle>, selected_index: usize, } impl LanguageSelector { - fn new(language_registry: Arc, cx: &mut ViewContext) -> Self { + fn new( + buffer: ModelHandle, + project: ModelHandle, + language_registry: Arc, + cx: &mut ViewContext, + ) -> Self { let handle = cx.weak_handle(); let picker = cx.add_view(|cx| Picker::new("Select Language...", handle, cx)); - let mut matches = language_registry + let candidates = language_registry .language_names() .into_iter() .enumerate() - .map(|(candidate_id, name)| StringMatch { - candidate_id, - score: 0.0, + .map(|(candidate_id, name)| StringMatchCandidate::new(candidate_id, name)) + .collect::>(); + let mut matches = candidates + .iter() + .map(|candidate| StringMatch { + candidate_id: candidate.id, + score: 0., positions: Default::default(), - string: name, + string: candidate.string.clone(), }) .collect::>(); matches.sort_unstable_by(|mat1, mat2| mat1.string.cmp(&mat2.string)); Self { + buffer, + project, language_registry, + candidates, matches, picker, selected_index: 0, @@ -64,11 +81,18 @@ impl LanguageSelector { registry: Arc, cx: &mut ViewContext, ) { - workspace.toggle_modal(cx, |_, cx| { - let this = cx.add_view(|cx| Self::new(registry, cx)); - cx.subscribe(&this, Self::on_event).detach(); - this - }); + if let Some((_, buffer, _)) = workspace + .active_item(cx) + .and_then(|active_item| active_item.act_as::(cx)) + .and_then(|editor| editor.read(cx).active_excerpt(cx)) + { + workspace.toggle_modal(cx, |workspace, cx| { + let project = workspace.project().clone(); + let this = cx.add_view(|cx| Self::new(buffer, project, registry, cx)); + cx.subscribe(&this, Self::on_event).detach(); + this + }); + } } fn on_event( @@ -111,7 +135,21 @@ impl PickerDelegate for LanguageSelector { } fn confirm(&mut self, cx: &mut ViewContext) { - todo!(); + if let Some(mat) = self.matches.get(self.selected_index) { + let language_name = &self.candidates[mat.candidate_id].string; + let language = self.language_registry.language_for_name(language_name); + cx.spawn(|this, mut cx| async move { + let language = language.await?; + this.update(&mut cx, |this, cx| { + this.project.update(cx, |project, cx| { + project.set_language_for_buffer(&this.buffer, language, cx); + }); + }); + anyhow::Ok(()) + }) + .detach_and_log_err(cx); + } + cx.emit(Event::Dismissed); } @@ -123,24 +161,13 @@ impl PickerDelegate for LanguageSelector { self.selected_index } - fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext) { + fn set_selected_index(&mut self, ix: usize, _: &mut ViewContext) { self.selected_index = ix; } fn update_matches(&mut self, query: String, cx: &mut ViewContext) -> gpui::Task<()> { let background = cx.background().clone(); - let candidates = self - .language_registry - .language_names() - .into_iter() - .enumerate() - .map(|(id, name)| StringMatchCandidate { - id, - char_bag: name.as_str().into(), - string: name.clone(), - }) - .collect::>(); - + let candidates = self.candidates.clone(); cx.spawn(|this, mut cx| async move { let matches = if query.is_empty() { candidates diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 003e4dd899..a164b5b885 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -1464,7 +1464,7 @@ impl Project { }) .await?; this.update(&mut cx, |this, cx| { - this.assign_language_to_buffer(&buffer, cx); + this.detect_language_for_buffer(&buffer, cx); this.register_buffer_with_language_server(&buffer, cx); }); Ok(()) @@ -1531,7 +1531,7 @@ impl Project { }) .detach(); - self.assign_language_to_buffer(buffer, cx); + self.detect_language_for_buffer(buffer, cx); self.register_buffer_with_language_server(buffer, cx); cx.observe_release(buffer, |this, buffer, cx| { if let Some(file) = File::from_dyn(buffer.file()) { @@ -1818,7 +1818,7 @@ impl Project { } for buffer in plain_text_buffers { - project.assign_language_to_buffer(&buffer, cx); + project.detect_language_for_buffer(&buffer, cx); project.register_buffer_with_language_server(&buffer, cx); } @@ -1831,7 +1831,7 @@ impl Project { }) } - fn assign_language_to_buffer( + fn detect_language_for_buffer( &mut self, buffer: &ModelHandle, cx: &mut ModelContext, @@ -1843,6 +1843,16 @@ impl Project { .language_for_path(&full_path) .now_or_never()? .ok()?; + self.set_language_for_buffer(buffer, new_language, cx); + None + } + + pub fn set_language_for_buffer( + &mut self, + buffer: &ModelHandle, + new_language: Arc, + cx: &mut ModelContext, + ) { buffer.update(cx, |buffer, cx| { if buffer.language().map_or(true, |old_language| { !Arc::ptr_eq(old_language, &new_language) @@ -1851,13 +1861,13 @@ impl Project { } }); - let file = File::from_dyn(buffer.read(cx).file())?; - let worktree = file.worktree.read(cx).as_local()?; - let worktree_id = worktree.id(); - let worktree_abs_path = worktree.abs_path().clone(); - self.start_language_server(worktree_id, worktree_abs_path, new_language, cx); - - None + if let Some(file) = File::from_dyn(buffer.read(cx).file()) { + if let Some(worktree) = file.worktree.read(cx).as_local() { + let worktree_id = worktree.id(); + let worktree_abs_path = worktree.abs_path().clone(); + self.start_language_server(worktree_id, worktree_abs_path, new_language, cx); + } + } } fn merge_json_value_into(source: serde_json::Value, target: &mut serde_json::Value) { @@ -4553,7 +4563,7 @@ impl Project { for (buffer, old_path) in renamed_buffers { self.unregister_buffer_from_language_server(&buffer, old_path, cx); - self.assign_language_to_buffer(&buffer, cx); + self.detect_language_for_buffer(&buffer, cx); self.register_buffer_with_language_server(&buffer, cx); } } @@ -5222,7 +5232,7 @@ impl Project { buffer.update(cx, |buffer, cx| { buffer.file_updated(Arc::new(file), cx).detach(); }); - this.assign_language_to_buffer(&buffer, cx); + this.detect_language_for_buffer(&buffer, cx); } Ok(()) })