assistant2: Show file icons for context entries (#22928)
https://github.com/user-attachments/assets/d3d6f5f1-23ec-449b-a762-9869b9d4b5a5 Release Notes: - N/A --------- Co-authored-by: Nathan <nathan@zed.dev> Co-authored-by: Michael <michael@zed.dev>
This commit is contained in:
parent
c9008fb8c1
commit
ec4c6744d6
7 changed files with 78 additions and 17 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -469,6 +469,7 @@ dependencies = [
|
||||||
"db",
|
"db",
|
||||||
"editor",
|
"editor",
|
||||||
"feature_flags",
|
"feature_flags",
|
||||||
|
"file_icons",
|
||||||
"fs",
|
"fs",
|
||||||
"futures 0.3.31",
|
"futures 0.3.31",
|
||||||
"fuzzy",
|
"fuzzy",
|
||||||
|
|
|
@ -18,15 +18,16 @@ anyhow.workspace = true
|
||||||
assets.workspace = true
|
assets.workspace = true
|
||||||
assistant_tool.workspace = true
|
assistant_tool.workspace = true
|
||||||
async-watch.workspace = true
|
async-watch.workspace = true
|
||||||
|
chrono.workspace = true
|
||||||
client.workspace = true
|
client.workspace = true
|
||||||
clock.workspace = true
|
clock.workspace = true
|
||||||
chrono.workspace = true
|
|
||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
command_palette_hooks.workspace = true
|
command_palette_hooks.workspace = true
|
||||||
context_server.workspace = true
|
context_server.workspace = true
|
||||||
db.workspace = true
|
db.workspace = true
|
||||||
editor.workspace = true
|
editor.workspace = true
|
||||||
feature_flags.workspace = true
|
feature_flags.workspace = true
|
||||||
|
file_icons.workspace = true
|
||||||
fs.workspace = true
|
fs.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
fuzzy.workspace = true
|
fuzzy.workspace = true
|
||||||
|
@ -47,8 +48,8 @@ multi_buffer.workspace = true
|
||||||
ollama = { workspace = true, features = ["schemars"] }
|
ollama = { workspace = true, features = ["schemars"] }
|
||||||
open_ai = { workspace = true, features = ["schemars"] }
|
open_ai = { workspace = true, features = ["schemars"] }
|
||||||
ordered-float.workspace = true
|
ordered-float.workspace = true
|
||||||
paths.workspace = true
|
|
||||||
parking_lot.workspace = true
|
parking_lot.workspace = true
|
||||||
|
paths.workspace = true
|
||||||
picker.workspace = true
|
picker.workspace = true
|
||||||
project.workspace = true
|
project.workspace = true
|
||||||
proto.workspace = true
|
proto.workspace = true
|
||||||
|
@ -61,9 +62,9 @@ settings.workspace = true
|
||||||
similar.workspace = true
|
similar.workspace = true
|
||||||
smol.workspace = true
|
smol.workspace = true
|
||||||
telemetry_events.workspace = true
|
telemetry_events.workspace = true
|
||||||
|
terminal.workspace = true
|
||||||
terminal_view.workspace = true
|
terminal_view.workspace = true
|
||||||
text.workspace = true
|
text.workspace = true
|
||||||
terminal.workspace = true
|
|
||||||
theme.workspace = true
|
theme.workspace = true
|
||||||
time.workspace = true
|
time.workspace = true
|
||||||
time_format.workspace = true
|
time_format.workspace = true
|
||||||
|
|
|
@ -2,11 +2,13 @@ use std::path::Path;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use file_icons::FileIcons;
|
||||||
use gpui::{AppContext, Model, SharedString};
|
use gpui::{AppContext, Model, SharedString};
|
||||||
use language::Buffer;
|
use language::Buffer;
|
||||||
use language_model::{LanguageModelRequestMessage, MessageContent};
|
use language_model::{LanguageModelRequestMessage, MessageContent};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use text::BufferId;
|
use text::BufferId;
|
||||||
|
use ui::IconName;
|
||||||
use util::post_inc;
|
use util::post_inc;
|
||||||
|
|
||||||
use crate::thread::Thread;
|
use crate::thread::Thread;
|
||||||
|
@ -27,6 +29,7 @@ pub struct ContextSnapshot {
|
||||||
pub name: SharedString,
|
pub name: SharedString,
|
||||||
pub parent: Option<SharedString>,
|
pub parent: Option<SharedString>,
|
||||||
pub tooltip: Option<SharedString>,
|
pub tooltip: Option<SharedString>,
|
||||||
|
pub icon_path: Option<SharedString>,
|
||||||
pub kind: ContextKind,
|
pub kind: ContextKind,
|
||||||
/// Concatenating these strings yields text to send to the model. Not refreshed by `snapshot`.
|
/// Concatenating these strings yields text to send to the model. Not refreshed by `snapshot`.
|
||||||
pub text: Box<[SharedString]>,
|
pub text: Box<[SharedString]>,
|
||||||
|
@ -40,6 +43,17 @@ pub enum ContextKind {
|
||||||
Thread,
|
Thread,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ContextKind {
|
||||||
|
pub fn icon(&self) -> IconName {
|
||||||
|
match self {
|
||||||
|
ContextKind::File => IconName::File,
|
||||||
|
ContextKind::Directory => IconName::Folder,
|
||||||
|
ContextKind::FetchedUrl => IconName::Globe,
|
||||||
|
ContextKind::Thread => IconName::MessageCircle,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Context {
|
pub enum Context {
|
||||||
File(FileContext),
|
File(FileContext),
|
||||||
|
@ -138,11 +152,14 @@ impl FileContext {
|
||||||
.and_then(|p| p.file_name())
|
.and_then(|p| p.file_name())
|
||||||
.map(|p| p.to_string_lossy().into_owned().into());
|
.map(|p| p.to_string_lossy().into_owned().into());
|
||||||
|
|
||||||
|
let icon_path = FileIcons::get_icon(&path, cx);
|
||||||
|
|
||||||
Some(ContextSnapshot {
|
Some(ContextSnapshot {
|
||||||
id: self.id,
|
id: self.id,
|
||||||
name,
|
name,
|
||||||
parent,
|
parent,
|
||||||
tooltip: Some(full_path),
|
tooltip: Some(full_path),
|
||||||
|
icon_path,
|
||||||
kind: ContextKind::File,
|
kind: ContextKind::File,
|
||||||
text: Box::new([self.buffer.text.clone()]),
|
text: Box::new([self.buffer.text.clone()]),
|
||||||
})
|
})
|
||||||
|
@ -162,6 +179,7 @@ impl FetchedUrlContext {
|
||||||
name: self.url.clone(),
|
name: self.url.clone(),
|
||||||
parent: None,
|
parent: None,
|
||||||
tooltip: None,
|
tooltip: None,
|
||||||
|
icon_path: None,
|
||||||
kind: ContextKind::FetchedUrl,
|
kind: ContextKind::FetchedUrl,
|
||||||
text: Box::new([self.text.clone()]),
|
text: Box::new([self.text.clone()]),
|
||||||
}
|
}
|
||||||
|
@ -176,6 +194,7 @@ impl ThreadContext {
|
||||||
name: thread.summary().unwrap_or("New thread".into()),
|
name: thread.summary().unwrap_or("New thread".into()),
|
||||||
parent: None,
|
parent: None,
|
||||||
tooltip: None,
|
tooltip: None,
|
||||||
|
icon_path: None,
|
||||||
kind: ContextKind::Thread,
|
kind: ContextKind::Thread,
|
||||||
text: Box::new([self.text.clone()]),
|
text: Box::new([self.text.clone()]),
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ use std::path::Path;
|
||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::AtomicBool;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use file_icons::FileIcons;
|
||||||
use fuzzy::PathMatch;
|
use fuzzy::PathMatch;
|
||||||
use gpui::{AppContext, DismissEvent, FocusHandle, FocusableView, Task, View, WeakModel, WeakView};
|
use gpui::{AppContext, DismissEvent, FocusHandle, FocusableView, Task, View, WeakModel, WeakView};
|
||||||
use picker::{Picker, PickerDelegate};
|
use picker::{Picker, PickerDelegate};
|
||||||
|
@ -281,6 +282,10 @@ impl PickerDelegate for FileContextPickerDelegate {
|
||||||
.will_include_file_path(&path_match.path, cx)
|
.will_include_file_path(&path_match.path, cx)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let file_icon = FileIcons::get_icon(&path_match.path.clone(), cx)
|
||||||
|
.map(Icon::from_path)
|
||||||
|
.unwrap_or_else(|| Icon::new(IconName::File));
|
||||||
|
|
||||||
Some(
|
Some(
|
||||||
ListItem::new(ix)
|
ListItem::new(ix)
|
||||||
.inset(true)
|
.inset(true)
|
||||||
|
@ -288,6 +293,7 @@ impl PickerDelegate for FileContextPickerDelegate {
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
.gap_2()
|
.gap_2()
|
||||||
|
.child(file_icon.size(IconSize::Small))
|
||||||
.child(Label::new(file_name))
|
.child(Label::new(file_name))
|
||||||
.children(directory.map(|directory| {
|
.children(directory.map(|directory| {
|
||||||
Label::new(directory)
|
Label::new(directory)
|
||||||
|
|
|
@ -273,6 +273,7 @@ impl ContextStore {
|
||||||
name,
|
name,
|
||||||
parent,
|
parent,
|
||||||
tooltip: Some(full_path),
|
tooltip: Some(full_path),
|
||||||
|
icon_path: None,
|
||||||
kind: ContextKind::Directory,
|
kind: ContextKind::Directory,
|
||||||
text,
|
text,
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,6 +3,7 @@ use std::rc::Rc;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use collections::HashSet;
|
use collections::HashSet;
|
||||||
use editor::Editor;
|
use editor::Editor;
|
||||||
|
use file_icons::FileIcons;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
DismissEvent, EventEmitter, FocusHandle, Model, ModelContext, Subscription, Task, View,
|
DismissEvent, EventEmitter, FocusHandle, Model, ModelContext, Subscription, Task, View,
|
||||||
WeakModel, WeakView,
|
WeakModel, WeakView,
|
||||||
|
@ -95,9 +96,12 @@ impl ContextStrip {
|
||||||
None => path.to_string_lossy().into_owned().into(),
|
None => path.to_string_lossy().into_owned().into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let icon_path = FileIcons::get_icon(path, cx);
|
||||||
|
|
||||||
Some(SuggestedContext::File {
|
Some(SuggestedContext::File {
|
||||||
name,
|
name,
|
||||||
buffer: active_buffer_model.downgrade(),
|
buffer: active_buffer_model.downgrade(),
|
||||||
|
icon_path,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,6 +232,7 @@ impl Render for ContextStrip {
|
||||||
.when_some(suggested_context, |el, suggested| {
|
.when_some(suggested_context, |el, suggested| {
|
||||||
el.child(ContextPill::new_suggested(
|
el.child(ContextPill::new_suggested(
|
||||||
suggested.name().clone(),
|
suggested.name().clone(),
|
||||||
|
suggested.icon_path(),
|
||||||
suggested.kind(),
|
suggested.kind(),
|
||||||
{
|
{
|
||||||
let context_store = self.context_store.clone();
|
let context_store = self.context_store.clone();
|
||||||
|
@ -304,6 +309,7 @@ pub enum SuggestContextKind {
|
||||||
pub enum SuggestedContext {
|
pub enum SuggestedContext {
|
||||||
File {
|
File {
|
||||||
name: SharedString,
|
name: SharedString,
|
||||||
|
icon_path: Option<SharedString>,
|
||||||
buffer: WeakModel<Buffer>,
|
buffer: WeakModel<Buffer>,
|
||||||
},
|
},
|
||||||
Thread {
|
Thread {
|
||||||
|
@ -320,13 +326,24 @@ impl SuggestedContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn icon_path(&self) -> Option<SharedString> {
|
||||||
|
match self {
|
||||||
|
Self::File { icon_path, .. } => icon_path.clone(),
|
||||||
|
Self::Thread { .. } => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn accept(
|
pub fn accept(
|
||||||
&self,
|
&self,
|
||||||
context_store: &mut ContextStore,
|
context_store: &mut ContextStore,
|
||||||
cx: &mut ModelContext<ContextStore>,
|
cx: &mut ModelContext<ContextStore>,
|
||||||
) -> Task<Result<()>> {
|
) -> Task<Result<()>> {
|
||||||
match self {
|
match self {
|
||||||
Self::File { buffer, name: _ } => {
|
Self::File {
|
||||||
|
buffer,
|
||||||
|
icon_path: _,
|
||||||
|
name: _,
|
||||||
|
} => {
|
||||||
if let Some(buffer) = buffer.upgrade() {
|
if let Some(buffer) = buffer.upgrade() {
|
||||||
return context_store.add_file_from_buffer(buffer, cx);
|
return context_store.add_file_from_buffer(buffer, cx);
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,6 +14,7 @@ pub enum ContextPill {
|
||||||
},
|
},
|
||||||
Suggested {
|
Suggested {
|
||||||
name: SharedString,
|
name: SharedString,
|
||||||
|
icon_path: Option<SharedString>,
|
||||||
kind: ContextKind,
|
kind: ContextKind,
|
||||||
on_add: Rc<dyn Fn(&ClickEvent, &mut WindowContext)>,
|
on_add: Rc<dyn Fn(&ClickEvent, &mut WindowContext)>,
|
||||||
},
|
},
|
||||||
|
@ -34,10 +35,16 @@ impl ContextPill {
|
||||||
|
|
||||||
pub fn new_suggested(
|
pub fn new_suggested(
|
||||||
name: SharedString,
|
name: SharedString,
|
||||||
|
icon_path: Option<SharedString>,
|
||||||
kind: ContextKind,
|
kind: ContextKind,
|
||||||
on_add: Rc<dyn Fn(&ClickEvent, &mut WindowContext)>,
|
on_add: Rc<dyn Fn(&ClickEvent, &mut WindowContext)>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::Suggested { name, kind, on_add }
|
Self::Suggested {
|
||||||
|
name,
|
||||||
|
icon_path,
|
||||||
|
kind,
|
||||||
|
on_add,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn id(&self) -> ElementId {
|
pub fn id(&self) -> ElementId {
|
||||||
|
@ -49,23 +56,27 @@ impl ContextPill {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn kind(&self) -> ContextKind {
|
pub fn icon(&self) -> Icon {
|
||||||
match self {
|
match self {
|
||||||
Self::Added { context, .. } => context.kind,
|
Self::Added { context, .. } => match &context.icon_path {
|
||||||
Self::Suggested { kind, .. } => *kind,
|
Some(icon_path) => Icon::from_path(icon_path),
|
||||||
|
None => Icon::new(context.kind.icon()),
|
||||||
|
},
|
||||||
|
Self::Suggested {
|
||||||
|
icon_path: Some(icon_path),
|
||||||
|
..
|
||||||
|
} => Icon::from_path(icon_path),
|
||||||
|
Self::Suggested {
|
||||||
|
kind,
|
||||||
|
icon_path: None,
|
||||||
|
..
|
||||||
|
} => Icon::new(kind.icon()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderOnce for ContextPill {
|
impl RenderOnce for ContextPill {
|
||||||
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
||||||
let icon = match &self.kind() {
|
|
||||||
ContextKind::File => IconName::File,
|
|
||||||
ContextKind::Directory => IconName::Folder,
|
|
||||||
ContextKind::FetchedUrl => IconName::Globe,
|
|
||||||
ContextKind::Thread => IconName::MessageCircle,
|
|
||||||
};
|
|
||||||
|
|
||||||
let color = cx.theme().colors();
|
let color = cx.theme().colors();
|
||||||
|
|
||||||
let base_pill = h_flex()
|
let base_pill = h_flex()
|
||||||
|
@ -75,7 +86,7 @@ impl RenderOnce for ContextPill {
|
||||||
.border_1()
|
.border_1()
|
||||||
.rounded_md()
|
.rounded_md()
|
||||||
.gap_1()
|
.gap_1()
|
||||||
.child(Icon::new(icon).size(IconSize::XSmall).color(Color::Muted));
|
.child(self.icon().size(IconSize::XSmall).color(Color::Muted));
|
||||||
|
|
||||||
match &self {
|
match &self {
|
||||||
ContextPill::Added {
|
ContextPill::Added {
|
||||||
|
@ -118,7 +129,12 @@ impl RenderOnce for ContextPill {
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
ContextPill::Suggested { name, kind, on_add } => base_pill
|
ContextPill::Suggested {
|
||||||
|
name,
|
||||||
|
icon_path: _,
|
||||||
|
kind,
|
||||||
|
on_add,
|
||||||
|
} => base_pill
|
||||||
.cursor_pointer()
|
.cursor_pointer()
|
||||||
.pr_1()
|
.pr_1()
|
||||||
.border_color(color.border_variant.opacity(0.5))
|
.border_color(color.border_variant.opacity(0.5))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue