mirror of
https://github.com/zed-industries/zed.git
synced 2024-09-05 19:35:29 +03:00
Add copilot.disabled_globs setting
This commit is contained in:
parent
dc999f719b
commit
c485fc86a2
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -1983,6 +1983,7 @@ dependencies = [
|
||||
"futures 0.3.25",
|
||||
"fuzzy",
|
||||
"git",
|
||||
"glob",
|
||||
"gpui",
|
||||
"indoc",
|
||||
"itertools",
|
||||
@ -5964,6 +5965,7 @@ dependencies = [
|
||||
"collections",
|
||||
"fs",
|
||||
"futures 0.3.25",
|
||||
"glob",
|
||||
"gpui",
|
||||
"json_comments",
|
||||
"postage",
|
||||
|
@ -77,6 +77,7 @@ async-trait = { version = "0.1" }
|
||||
ctor = { version = "0.1" }
|
||||
env_logger = { version = "0.9" }
|
||||
futures = { version = "0.3" }
|
||||
glob = { version = "0.3.1" }
|
||||
lazy_static = { version = "1.4.0" }
|
||||
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
|
||||
ordered-float = { version = "2.1.1" }
|
||||
|
@ -115,6 +115,13 @@
|
||||
// "git_gutter": "hide"
|
||||
"git_gutter": "tracked_files"
|
||||
},
|
||||
"copilot": {
|
||||
// The set of glob patterns for which copilot should be disabled
|
||||
// in any matching file.
|
||||
"disabled_globs": [
|
||||
".env"
|
||||
]
|
||||
},
|
||||
// Settings specific to journaling
|
||||
"journal": {
|
||||
// The path of the directory where journal entries are stored
|
||||
|
@ -79,6 +79,7 @@ workspace = { path = "../workspace", features = ["test-support"] }
|
||||
|
||||
ctor.workspace = true
|
||||
env_logger.workspace = true
|
||||
glob.workspace = true
|
||||
rand.workspace = true
|
||||
unindent.workspace = true
|
||||
tree-sitter = "0.20"
|
||||
|
@ -2925,11 +2925,7 @@ impl Editor {
|
||||
|
||||
let snapshot = self.buffer.read(cx).snapshot(cx);
|
||||
let cursor = self.selections.newest_anchor().head();
|
||||
let language_name = snapshot.language_at(cursor).map(|language| language.name());
|
||||
if !cx
|
||||
.global::<Settings>()
|
||||
.show_copilot_suggestions(language_name.as_deref())
|
||||
{
|
||||
if !self.is_copilot_enabled_at(cursor, &snapshot, cx) {
|
||||
self.clear_copilot_suggestions(cx);
|
||||
return None;
|
||||
}
|
||||
@ -3080,6 +3076,37 @@ impl Editor {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_copilot_enabled_at(
|
||||
&self,
|
||||
location: Anchor,
|
||||
snapshot: &MultiBufferSnapshot,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> bool {
|
||||
let settings = cx.global::<Settings>();
|
||||
|
||||
let language_name = snapshot
|
||||
.language_at(location)
|
||||
.map(|language| language.name());
|
||||
if !settings.show_copilot_suggestions(language_name.as_deref()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let file = snapshot.file_at(location);
|
||||
if let Some(file) = file {
|
||||
let path = file.path();
|
||||
if settings
|
||||
.copilot
|
||||
.disabled_globs
|
||||
.iter()
|
||||
.any(|glob| glob.matches_path(path))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
|
||||
self.display_map.read(cx).has_suggestion()
|
||||
}
|
||||
|
@ -6387,6 +6387,97 @@ async fn test_copilot_multibuffer(
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_copilot_disabled_globs(
|
||||
deterministic: Arc<Deterministic>,
|
||||
cx: &mut gpui::TestAppContext,
|
||||
) {
|
||||
let (copilot, copilot_lsp) = Copilot::fake(cx);
|
||||
cx.update(|cx| {
|
||||
let mut settings = Settings::test(cx);
|
||||
settings.copilot.disabled_globs = vec![glob::Pattern::new(".env*").unwrap()];
|
||||
cx.set_global(settings);
|
||||
cx.set_global(copilot)
|
||||
});
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
fs.insert_tree(
|
||||
"/test",
|
||||
json!({
|
||||
".env": "SECRET=something\n",
|
||||
"README.md": "hello\n"
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
let project = Project::test(fs, ["/test".as_ref()], cx).await;
|
||||
|
||||
let private_buffer = project
|
||||
.update(cx, |project, cx| {
|
||||
project.open_local_buffer("/test/.env", cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
let public_buffer = project
|
||||
.update(cx, |project, cx| {
|
||||
project.open_local_buffer("/test/README.md", cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let multibuffer = cx.add_model(|cx| {
|
||||
let mut multibuffer = MultiBuffer::new(0);
|
||||
multibuffer.push_excerpts(
|
||||
private_buffer.clone(),
|
||||
[ExcerptRange {
|
||||
context: Point::new(0, 0)..Point::new(1, 0),
|
||||
primary: None,
|
||||
}],
|
||||
cx,
|
||||
);
|
||||
multibuffer.push_excerpts(
|
||||
public_buffer.clone(),
|
||||
[ExcerptRange {
|
||||
context: Point::new(0, 0)..Point::new(1, 0),
|
||||
primary: None,
|
||||
}],
|
||||
cx,
|
||||
);
|
||||
multibuffer
|
||||
});
|
||||
let (_, editor) = cx.add_window(|cx| build_editor(multibuffer, cx));
|
||||
|
||||
let mut copilot_requests = copilot_lsp
|
||||
.handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| async move {
|
||||
Ok(copilot::request::GetCompletionsResult {
|
||||
completions: vec![copilot::request::Completion {
|
||||
text: "next line".into(),
|
||||
range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
|
||||
..Default::default()
|
||||
}],
|
||||
})
|
||||
});
|
||||
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.change_selections(None, cx, |selections| {
|
||||
selections.select_ranges([Point::new(0, 0)..Point::new(0, 0)])
|
||||
});
|
||||
editor.next_copilot_suggestion(&Default::default(), cx);
|
||||
});
|
||||
|
||||
deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
|
||||
assert!(copilot_requests.try_next().is_err());
|
||||
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.change_selections(None, cx, |s| {
|
||||
s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
|
||||
});
|
||||
editor.next_copilot_suggestion(&Default::default(), cx);
|
||||
});
|
||||
|
||||
deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
|
||||
assert!(copilot_requests.try_next().is_ok());
|
||||
}
|
||||
|
||||
fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
|
||||
let point = DisplayPoint::new(row as u32, column as u32);
|
||||
point..point
|
||||
|
@ -10,9 +10,9 @@ use gpui::{AppContext, Entity, ModelContext, ModelHandle, Task};
|
||||
pub use language::Completion;
|
||||
use language::{
|
||||
char_kind, AutoindentMode, Buffer, BufferChunks, BufferSnapshot, CharKind, Chunk, CursorShape,
|
||||
DiagnosticEntry, IndentSize, Language, LanguageScope, OffsetRangeExt, OffsetUtf16, Outline,
|
||||
OutlineItem, Point, PointUtf16, Selection, TextDimension, ToOffset as _, ToOffsetUtf16 as _,
|
||||
ToPoint as _, ToPointUtf16 as _, TransactionId, Unclipped,
|
||||
DiagnosticEntry, File, IndentSize, Language, LanguageScope, OffsetRangeExt, OffsetUtf16,
|
||||
Outline, OutlineItem, Point, PointUtf16, Selection, TextDimension, ToOffset as _,
|
||||
ToOffsetUtf16 as _, ToPoint as _, ToPointUtf16 as _, TransactionId, Unclipped,
|
||||
};
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
@ -2754,6 +2754,11 @@ impl MultiBufferSnapshot {
|
||||
self.trailing_excerpt_update_count
|
||||
}
|
||||
|
||||
pub fn file_at<'a, T: ToOffset>(&'a self, point: T) -> Option<&'a Arc<dyn File>> {
|
||||
self.point_to_buffer_offset(point)
|
||||
.and_then(|(buffer, _)| buffer.file())
|
||||
}
|
||||
|
||||
pub fn language_at<'a, T: ToOffset>(&'a self, point: T) -> Option<&'a Arc<Language>> {
|
||||
self.point_to_buffer_offset(point)
|
||||
.and_then(|(buffer, offset)| buffer.language_at(offset))
|
||||
|
@ -28,7 +28,6 @@ fs = { path = "../fs" }
|
||||
fsevent = { path = "../fsevent" }
|
||||
fuzzy = { path = "../fuzzy" }
|
||||
git = { path = "../git" }
|
||||
glob = { version = "0.3.1" }
|
||||
gpui = { path = "../gpui" }
|
||||
language = { path = "../language" }
|
||||
lsp = { path = "../lsp" }
|
||||
@ -43,6 +42,7 @@ anyhow.workspace = true
|
||||
async-trait.workspace = true
|
||||
backtrace = "0.3"
|
||||
futures.workspace = true
|
||||
glob.workspace = true
|
||||
ignore = "0.4"
|
||||
lazy_static.workspace = true
|
||||
log.workspace = true
|
||||
|
@ -23,6 +23,7 @@ theme = { path = "../theme" }
|
||||
staff_mode = { path = "../staff_mode" }
|
||||
util = { path = "../util" }
|
||||
|
||||
glob.workspace = true
|
||||
json_comments = "0.2"
|
||||
postage.workspace = true
|
||||
schemars = "0.8"
|
||||
|
@ -47,6 +47,7 @@ pub struct Settings {
|
||||
pub editor_overrides: EditorSettings,
|
||||
pub git: GitSettings,
|
||||
pub git_overrides: GitSettings,
|
||||
pub copilot: CopilotSettings,
|
||||
pub journal_defaults: JournalSettings,
|
||||
pub journal_overrides: JournalSettings,
|
||||
pub terminal_defaults: TerminalSettings,
|
||||
@ -61,29 +62,6 @@ pub struct Settings {
|
||||
pub base_keymap: BaseKeymap,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Default)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum CopilotSettings {
|
||||
#[default]
|
||||
On,
|
||||
Off,
|
||||
}
|
||||
|
||||
impl From<CopilotSettings> for bool {
|
||||
fn from(value: CopilotSettings) -> Self {
|
||||
match value {
|
||||
CopilotSettings::On => true,
|
||||
CopilotSettings::Off => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CopilotSettings {
|
||||
pub fn is_on(&self) -> bool {
|
||||
<CopilotSettings as Into<bool>>::into(*self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Default)]
|
||||
pub enum BaseKeymap {
|
||||
#[default]
|
||||
@ -150,6 +128,29 @@ impl TelemetrySettings {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct CopilotSettings {
|
||||
pub disabled_globs: Vec<glob::Pattern>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct CopilotSettingsContent {
|
||||
#[serde(default)]
|
||||
disabled_globs: Vec<String>,
|
||||
}
|
||||
|
||||
impl From<CopilotSettingsContent> for CopilotSettings {
|
||||
fn from(value: CopilotSettingsContent) -> Self {
|
||||
Self {
|
||||
disabled_globs: value
|
||||
.disabled_globs
|
||||
.into_iter()
|
||||
.filter_map(|p| glob::Pattern::new(&p).ok())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct GitSettings {
|
||||
pub git_gutter: Option<GitGutter>,
|
||||
@ -390,6 +391,8 @@ pub struct SettingsFileContent {
|
||||
#[serde(default)]
|
||||
pub buffer_font_features: Option<fonts::Features>,
|
||||
#[serde(default)]
|
||||
pub copilot: Option<CopilotSettingsContent>,
|
||||
#[serde(default)]
|
||||
pub active_pane_magnification: Option<f32>,
|
||||
#[serde(default)]
|
||||
pub cursor_blink: Option<bool>,
|
||||
@ -438,8 +441,7 @@ pub struct LspSettings {
|
||||
pub initialization_options: Option<Value>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Features {
|
||||
pub copilot: bool,
|
||||
}
|
||||
@ -506,6 +508,7 @@ impl Settings {
|
||||
show_copilot_suggestions: required(defaults.editor.show_copilot_suggestions),
|
||||
},
|
||||
editor_overrides: Default::default(),
|
||||
copilot: defaults.copilot.unwrap().into(),
|
||||
git: defaults.git.unwrap(),
|
||||
git_overrides: Default::default(),
|
||||
journal_defaults: defaults.journal,
|
||||
@ -576,6 +579,9 @@ impl Settings {
|
||||
merge(&mut self.base_keymap, data.base_keymap);
|
||||
merge(&mut self.features.copilot, data.features.copilot);
|
||||
|
||||
if let Some(copilot) = data.copilot.map(CopilotSettings::from) {
|
||||
self.copilot = copilot;
|
||||
}
|
||||
self.editor_overrides = data.editor;
|
||||
self.git_overrides = data.git.unwrap_or_default();
|
||||
self.journal_overrides = data.journal;
|
||||
@ -751,6 +757,7 @@ impl Settings {
|
||||
show_copilot_suggestions: Some(true),
|
||||
},
|
||||
editor_overrides: Default::default(),
|
||||
copilot: Default::default(),
|
||||
journal_defaults: Default::default(),
|
||||
journal_overrides: Default::default(),
|
||||
terminal_defaults: Default::default(),
|
||||
|
Loading…
Reference in New Issue
Block a user