Show annotations more like the inline assistant (#11530)

* compute gutter width
* show the AI icon
* borders, background, and text color for annotations

<img width="1840" alt="image"
src="https://github.com/zed-industries/zed/assets/836375/93f2e9b8-d7f7-4c25-b3e2-cf77a0c4ca36">

Release Notes:

- N/A
This commit is contained in:
Kyle Kelley 2024-05-07 19:16:35 -07:00 committed by GitHub
parent 1cf40d77e2
commit 6ddcff25e3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 49 additions and 29 deletions

View file

@ -1,3 +1,3 @@
Use tools frequently, especially when referring to files and code. I prefer to see the file directly rather than you just chatting with me. Use tools frequently, especially when referring to files and code. The Zed editor we're working in can show me files directly when you add annotations. Be concise in chat, bountiful in tool calling.
Teach me everything you can about settings files and how they're loaded. Teach me everything you can about how zed loads settings. Please annotate the code inline.

View file

@ -36,7 +36,7 @@ use semantic_index::{CloudEmbeddingProvider, ProjectIndex, ProjectIndexDebugView
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use settings::Settings; use settings::Settings;
use std::sync::Arc; use std::sync::Arc;
use tools::OpenBufferTool; use tools::AnnotationTool;
use ui::{ActiveFileButton, Composer, ProjectIndexButton}; use ui::{ActiveFileButton, Composer, ProjectIndexButton};
use util::paths::CONVERSATIONS_DIR; use util::paths::CONVERSATIONS_DIR;
use util::{maybe, paths::EMBEDDINGS_DIR, ResultExt}; use util::{maybe, paths::EMBEDDINGS_DIR, ResultExt};
@ -150,7 +150,7 @@ impl AssistantPanel {
) )
.unwrap(); .unwrap();
tool_registry tool_registry
.register(OpenBufferTool::new(workspace.clone(), project.clone()), cx) .register(AnnotationTool::new(workspace.clone(), project.clone()), cx)
.unwrap(); .unwrap();
let mut attachment_registry = AttachmentRegistry::new(); let mut attachment_registry = AttachmentRegistry::new();

View file

@ -1,7 +1,7 @@
mod annotate_code;
mod create_buffer; mod create_buffer;
mod open_buffer;
mod project_index; mod project_index;
pub use annotate_code::*;
pub use create_buffer::*; pub use create_buffer::*;
pub use open_buffer::*;
pub use project_index::*; pub use project_index::*;

View file

@ -14,51 +14,51 @@ use ui::prelude::*;
use util::ResultExt; use util::ResultExt;
use workspace::Workspace; use workspace::Workspace;
pub struct OpenBufferTool { pub struct AnnotationTool {
workspace: WeakView<Workspace>, workspace: WeakView<Workspace>,
project: Model<Project>, project: Model<Project>,
} }
impl OpenBufferTool { impl AnnotationTool {
pub fn new(workspace: WeakView<Workspace>, project: Model<Project>) -> Self { pub fn new(workspace: WeakView<Workspace>, project: Model<Project>) -> Self {
Self { workspace, project } Self { workspace, project }
} }
} }
#[derive(Debug, Deserialize, JsonSchema, Clone)] #[derive(Debug, Deserialize, JsonSchema, Clone)]
pub struct ExplainInput { pub struct AnnotationInput {
/// Name for this set of excerpts /// Name for this set of annotations
title: String, title: String,
excerpts: Vec<ExplainedExcerpt>, annotations: Vec<Annotation>,
} }
#[derive(Debug, Deserialize, JsonSchema, Clone)] #[derive(Debug, Deserialize, JsonSchema, Clone)]
struct ExplainedExcerpt { struct Annotation {
/// Path to the file /// Path to the file
path: String, path: String,
/// Name of a symbol in the buffer to show /// Name of a symbol in the code
symbol_name: String, symbol_name: String,
/// Text to display near the symbol definition /// Text to display near the symbol definition
comment: String, text: String,
} }
impl LanguageModelTool for OpenBufferTool { impl LanguageModelTool for AnnotationTool {
type Input = ExplainInput; type Input = AnnotationInput;
type Output = String; type Output = String;
type View = OpenBufferView; type View = AnnotationResultView;
fn name(&self) -> String { fn name(&self) -> String {
"explain_code".to_string() "annotate_code".to_string()
} }
fn description(&self) -> String { fn description(&self) -> String {
"Show and explain one or more code snippets from files in the current project. Code snippets are identified using a file path and the name of a symbol defined in that file.".to_string() "Dynamically annotate symbols in the current codebase. Opens a buffer in a panel in their editor, to the side of the conversation. The annotations are shown in the editor as a block decoration.".to_string()
} }
fn execute(&self, input: &Self::Input, cx: &mut WindowContext) -> Task<Result<Self::Output>> { fn execute(&self, input: &Self::Input, cx: &mut WindowContext) -> Task<Result<Self::Output>> {
let workspace = self.workspace.clone(); let workspace = self.workspace.clone();
let project = self.project.clone(); let project = self.project.clone();
let excerpts = input.excerpts.clone(); let excerpts = input.annotations.clone();
let title = input.title.clone(); let title = input.title.clone();
let worktree_id = project.update(cx, |project, cx| { let worktree_id = project.update(cx, |project, cx| {
@ -115,11 +115,11 @@ impl LanguageModelTool for OpenBufferTool {
cx, cx,
) )
}); });
let explanation = SharedString::from(excerpt.comment.clone()); let explanation = SharedString::from(excerpt.text.clone());
editor.insert_blocks( editor.insert_blocks(
[BlockProperties { [BlockProperties {
position: ranges[0].start, position: ranges[0].start,
height: 1, height: 2,
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
render: Box::new(move |cx| { render: Box::new(move |cx| {
Self::render_note_block(&explanation, cx) Self::render_note_block(&explanation, cx)
@ -149,21 +149,41 @@ impl LanguageModelTool for OpenBufferTool {
output: Result<Self::Output>, output: Result<Self::Output>,
cx: &mut WindowContext, cx: &mut WindowContext,
) -> View<Self::View> { ) -> View<Self::View> {
cx.new_view(|_cx| OpenBufferView { output }) cx.new_view(|_cx| AnnotationResultView { output })
} }
} }
impl OpenBufferTool { impl AnnotationTool {
fn render_note_block(explanation: &SharedString, _cx: &mut BlockContext) -> AnyElement { fn render_note_block(explanation: &SharedString, cx: &mut BlockContext) -> AnyElement {
div().child(explanation.clone()).into_any_element() let anchor_x = cx.anchor_x;
let gutter_width = cx.gutter_dimensions.width;
h_flex()
.w_full()
.py_2()
.border_y_1()
.border_color(cx.theme().colors().border)
.child(
h_flex()
.justify_center()
.w(gutter_width)
.child(Icon::new(IconName::Ai).color(Color::Hint)),
)
.child(
h_flex()
.w_full()
.ml(anchor_x - gutter_width)
.child(explanation.clone()),
)
.into_any_element()
} }
} }
pub struct OpenBufferView { pub struct AnnotationResultView {
output: Result<String>, output: Result<String>,
} }
impl Render for OpenBufferView { impl Render for AnnotationResultView {
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement { fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
match &self.output { match &self.output {
Ok(output) => div().child(output.clone().into_any_element()), Ok(output) => div().child(output.clone().into_any_element()),
@ -172,7 +192,7 @@ impl Render for OpenBufferView {
} }
} }
impl ToolOutput for OpenBufferView { impl ToolOutput for AnnotationResultView {
fn generate(&self, _: &mut ProjectContext, _: &mut WindowContext) -> String { fn generate(&self, _: &mut ProjectContext, _: &mut WindowContext) -> String {
match &self.output { match &self.output {
Ok(output) => output.clone(), Ok(output) => output.clone(),