Respect project-specific settings for copilot

Max Brunsfeld created

Change summary

crates/copilot/src/copilot.rs               |  2 
crates/copilot_button/src/copilot_button.rs | 32 ++++++++++++--------
crates/editor/src/editor.rs                 | 15 +++++---
crates/language/src/language_settings.rs    | 10 +++++-
crates/project/src/project.rs               | 35 +++++++++++-----------
crates/project/src/worktree.rs              |  2 
6 files changed, 56 insertions(+), 40 deletions(-)

Detailed changes

crates/copilot/src/copilot.rs 🔗

@@ -318,7 +318,7 @@ impl Copilot {
     fn enable_or_disable_copilot(&mut self, cx: &mut ModelContext<Copilot>) {
         let http = self.http.clone();
         let node_runtime = self.node_runtime.clone();
-        if all_language_settings(cx).copilot_enabled(None, None) {
+        if all_language_settings(None, cx).copilot_enabled(None, None) {
             if matches!(self.server, CopilotServer::Disabled) {
                 let start_task = cx
                     .spawn({

crates/copilot_button/src/copilot_button.rs 🔗

@@ -9,7 +9,10 @@ use gpui::{
     AnyElement, AppContext, AsyncAppContext, Element, Entity, MouseState, Subscription, View,
     ViewContext, ViewHandle, WeakViewHandle, WindowContext,
 };
-use language::language_settings::{self, all_language_settings, AllLanguageSettings};
+use language::{
+    language_settings::{self, all_language_settings, AllLanguageSettings},
+    File,
+};
 use settings::{update_settings_file, SettingsStore};
 use std::{path::Path, sync::Arc};
 use util::{paths, ResultExt};
@@ -27,7 +30,7 @@ pub struct CopilotButton {
     editor_subscription: Option<(Subscription, usize)>,
     editor_enabled: Option<bool>,
     language: Option<Arc<str>>,
-    path: Option<Arc<Path>>,
+    file: Option<Arc<dyn File>>,
     fs: Arc<dyn Fs>,
 }
 
@@ -41,7 +44,7 @@ impl View for CopilotButton {
     }
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
-        let all_language_settings = &all_language_settings(cx);
+        let all_language_settings = all_language_settings(None, cx);
         if !all_language_settings.copilot.feature_enabled {
             return Empty::new().into_any();
         }
@@ -165,7 +168,7 @@ impl CopilotButton {
             editor_subscription: None,
             editor_enabled: None,
             language: None,
-            path: None,
+            file: None,
             fs,
         }
     }
@@ -212,9 +215,9 @@ impl CopilotButton {
 
         let settings = settings::get::<AllLanguageSettings>(cx);
 
-        if let Some(path) = self.path.as_ref() {
-            let path_enabled = settings.copilot_enabled_for_path(path);
-            let path = path.clone();
+        if let Some(file) = &self.file {
+            let path = file.path().clone();
+            let path_enabled = settings.copilot_enabled_for_path(&path);
             menu_options.push(ContextMenuItem::handler(
                 format!(
                     "{} Suggestions for This Path",
@@ -279,14 +282,16 @@ impl CopilotButton {
         let language_name = snapshot
             .language_at(suggestion_anchor)
             .map(|language| language.name());
-        let path = snapshot.file_at(suggestion_anchor).map(|file| file.path());
+        let file = snapshot.file_at(suggestion_anchor).cloned();
 
         self.editor_enabled = Some(
-            all_language_settings(cx)
-                .copilot_enabled(language_name.as_deref(), path.map(|p| p.as_ref())),
+            all_language_settings(self.file.as_ref().map(|f| f.as_ref()), cx).copilot_enabled(
+                language_name.as_deref(),
+                file.as_ref().map(|file| file.path().as_ref()),
+            ),
         );
         self.language = language_name;
-        self.path = path.cloned();
+        self.file = file;
 
         cx.notify()
     }
@@ -363,14 +368,15 @@ async fn configure_disabled_globs(
 }
 
 fn toggle_copilot_globally(fs: Arc<dyn Fs>, cx: &mut AppContext) {
-    let show_copilot_suggestions = all_language_settings(cx).copilot_enabled(None, None);
+    let show_copilot_suggestions = all_language_settings(None, cx).copilot_enabled(None, None);
     update_settings_file::<AllLanguageSettings>(fs, cx, move |file| {
         file.defaults.show_copilot_suggestions = Some((!show_copilot_suggestions).into())
     });
 }
 
 fn toggle_copilot_for_language(language: Arc<str>, fs: Arc<dyn Fs>, cx: &mut AppContext) {
-    let show_copilot_suggestions = all_language_settings(cx).copilot_enabled(Some(&language), None);
+    let show_copilot_suggestions =
+        all_language_settings(None, cx).copilot_enabled(Some(&language), None);
     update_settings_file::<AllLanguageSettings>(fs, cx, move |file| {
         file.languages
             .entry(language)

crates/editor/src/editor.rs 🔗

@@ -3207,12 +3207,12 @@ impl Editor {
         snapshot: &MultiBufferSnapshot,
         cx: &mut ViewContext<Self>,
     ) -> bool {
-        let path = snapshot.file_at(location).map(|file| file.path().as_ref());
+        let file = snapshot.file_at(location);
         let language_name = snapshot
             .language_at(location)
             .map(|language| language.name());
-        let settings = all_language_settings(cx);
-        settings.copilot_enabled(language_name.as_deref(), path)
+        let settings = all_language_settings(file.map(|f| f.as_ref() as _), cx);
+        settings.copilot_enabled(language_name.as_deref(), file.map(|f| f.path().as_ref()))
     }
 
     fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
@@ -7076,11 +7076,13 @@ impl Editor {
         };
 
         // If None, we are in a file without an extension
-        let file_extension = file_extension.or(self
+        let file = self
             .buffer
             .read(cx)
             .as_singleton()
-            .and_then(|b| b.read(cx).file())
+            .and_then(|b| b.read(cx).file());
+        let file_extension = file_extension.or(file
+            .as_ref()
             .and_then(|file| Path::new(file.file_name(cx)).extension())
             .and_then(|e| e.to_str())
             .map(|a| a.to_string()));
@@ -7091,7 +7093,8 @@ impl Editor {
             .get("vim_mode")
             == Some(&serde_json::Value::Bool(true));
         let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
-        let copilot_enabled = all_language_settings(cx).copilot_enabled(None, None);
+        let copilot_enabled =
+            all_language_settings(file.map(|f| f.as_ref()), cx).copilot_enabled(None, None);
         let copilot_enabled_for_language = self
             .buffer
             .read(cx)

crates/language/src/language_settings.rs 🔗

@@ -26,8 +26,14 @@ pub fn language_settings<'a>(
     .language(language)
 }
 
-pub fn all_language_settings<'a>(cx: &'a AppContext) -> &'a AllLanguageSettings {
-    settings::get::<AllLanguageSettings>(cx)
+pub fn all_language_settings<'a>(
+    file: Option<&dyn File>,
+    cx: &'a AppContext,
+) -> &'a AllLanguageSettings {
+    settings::get_local::<AllLanguageSettings>(
+        file.map(|f| (f.worktree_id(), f.path().as_ref())),
+        cx,
+    )
 }
 
 #[derive(Debug, Clone)]

crates/project/src/project.rs 🔗

@@ -28,7 +28,7 @@ use gpui::{
     ModelHandle, Task, WeakModelHandle,
 };
 use language::{
-    language_settings::{all_language_settings, language_settings, FormatOnSave, Formatter},
+    language_settings::{language_settings, FormatOnSave, Formatter},
     point_to_lsp,
     proto::{
         deserialize_anchor, deserialize_fingerprint, deserialize_line_ending, deserialize_version,
@@ -689,18 +689,15 @@ impl Project {
     }
 
     fn on_settings_changed(&mut self, cx: &mut ModelContext<Self>) {
-        let settings = all_language_settings(cx);
-
         let mut language_servers_to_start = Vec::new();
         for buffer in self.opened_buffers.values() {
             if let Some(buffer) = buffer.upgrade(cx) {
                 let buffer = buffer.read(cx);
                 if let Some((file, language)) = File::from_dyn(buffer.file()).zip(buffer.language())
                 {
-                    if settings
-                        .language(Some(&language.name()))
-                        .enable_language_server
-                    {
+                    let settings =
+                        language_settings(Some(language.name().as_ref()), Some(file), cx);
+                    if settings.enable_language_server {
                         language_servers_to_start.push((file.worktree.clone(), language.clone()));
                     }
                 }
@@ -708,18 +705,22 @@ impl Project {
         }
 
         let mut language_servers_to_stop = Vec::new();
-        for language in self.languages.to_vec() {
-            for lsp_adapter in language.lsp_adapters() {
-                if !settings
-                    .language(Some(&language.name()))
+        let languages = self.languages.to_vec();
+        for (worktree_id, started_lsp_name) in self.language_server_ids.keys() {
+            let language = languages.iter().find(|l| {
+                l.lsp_adapters()
+                    .iter()
+                    .any(|adapter| &adapter.name == started_lsp_name)
+            });
+            if let Some(language) = language {
+                let worktree = self.worktree_for_id(*worktree_id, cx);
+                let file = worktree.and_then(|tree| tree.update(cx, |tree, cx| tree.root_file(cx)));
+                // let settings =
+                //     language_settings(Some(language.name().as_ref()), Some(file), cx);
+                if !language_settings(Some(&language.name()), file.as_ref().map(|f| f as _), cx)
                     .enable_language_server
                 {
-                    let lsp_name = &lsp_adapter.name;
-                    for (worktree_id, started_lsp_name) in self.language_server_ids.keys() {
-                        if lsp_name == started_lsp_name {
-                            language_servers_to_stop.push((*worktree_id, started_lsp_name.clone()));
-                        }
-                    }
+                    language_servers_to_stop.push((*worktree_id, started_lsp_name.clone()));
                 }
             }
         }

crates/project/src/worktree.rs 🔗

@@ -679,7 +679,7 @@ impl Worktree {
     }
 
     pub fn root_file(&self, cx: &mut ModelContext<Self>) -> Option<File> {
-        let entry = self.entry_for_path("")?;
+        let entry = self.root_entry()?;
         Some(File {
             worktree: cx.handle(),
             path: entry.path.clone(),