Merge branch 'main' into ollama-inline-completions

This commit is contained in:
Oliver Azevedo Barnes 2025-08-24 10:30:18 -04:00
commit 1dbcc8aae3
No known key found for this signature in database
10 changed files with 50 additions and 23 deletions

1
Cargo.lock generated
View file

@ -8474,6 +8474,7 @@ dependencies = [
"theme", "theme",
"ui", "ui",
"util", "util",
"util_macros",
"workspace", "workspace",
"workspace-hack", "workspace-hack",
"zed_actions", "zed_actions",

View file

@ -509,7 +509,7 @@ impl ContentBlock {
"`Image`".into() "`Image`".into()
} }
fn to_markdown<'a>(&'a self, cx: &'a App) -> &'a str { pub fn to_markdown<'a>(&'a self, cx: &'a App) -> &'a str {
match self { match self {
ContentBlock::Empty => "", ContentBlock::Empty => "",
ContentBlock::Markdown { markdown } => markdown.read(cx).source(), ContentBlock::Markdown { markdown } => markdown.read(cx).source(),

View file

@ -74,6 +74,7 @@ pub enum MessageEditorEvent {
Send, Send,
Cancel, Cancel,
Focus, Focus,
LostFocus,
} }
impl EventEmitter<MessageEditorEvent> for MessageEditor {} impl EventEmitter<MessageEditorEvent> for MessageEditor {}
@ -131,10 +132,14 @@ impl MessageEditor {
editor editor
}); });
cx.on_focus(&editor.focus_handle(cx), window, |_, _, cx| { cx.on_focus_in(&editor.focus_handle(cx), window, |_, _, cx| {
cx.emit(MessageEditorEvent::Focus) cx.emit(MessageEditorEvent::Focus)
}) })
.detach(); .detach();
cx.on_focus_out(&editor.focus_handle(cx), window, |_, _, _, cx| {
cx.emit(MessageEditorEvent::LostFocus)
})
.detach();
let mut subscriptions = Vec::new(); let mut subscriptions = Vec::new();
subscriptions.push(cx.subscribe_in(&editor, window, { subscriptions.push(cx.subscribe_in(&editor, window, {
@ -1169,17 +1174,16 @@ impl MessageEditor {
}) })
} }
pub fn text(&self, cx: &App) -> String {
self.editor.read(cx).text(cx)
}
#[cfg(test)] #[cfg(test)]
pub fn set_text(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) { pub fn set_text(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
self.editor.update(cx, |editor, cx| { self.editor.update(cx, |editor, cx| {
editor.set_text(text, window, cx); editor.set_text(text, window, cx);
}); });
} }
#[cfg(test)]
pub fn text(&self, cx: &App) -> String {
self.editor.read(cx).text(cx)
}
} }
fn render_directory_contents(entries: Vec<(Arc<Path>, PathBuf, String)>) -> String { fn render_directory_contents(entries: Vec<(Arc<Path>, PathBuf, String)>) -> String {

View file

@ -274,7 +274,6 @@ pub struct AcpThreadView {
edits_expanded: bool, edits_expanded: bool,
plan_expanded: bool, plan_expanded: bool,
editor_expanded: bool, editor_expanded: bool,
terminal_expanded: bool,
editing_message: Option<usize>, editing_message: Option<usize>,
prompt_capabilities: Rc<Cell<PromptCapabilities>>, prompt_capabilities: Rc<Cell<PromptCapabilities>>,
is_loading_contents: bool, is_loading_contents: bool,
@ -386,7 +385,6 @@ impl AcpThreadView {
edits_expanded: false, edits_expanded: false,
plan_expanded: false, plan_expanded: false,
editor_expanded: false, editor_expanded: false,
terminal_expanded: true,
history_store, history_store,
hovered_recent_history_item: None, hovered_recent_history_item: None,
prompt_capabilities, prompt_capabilities,
@ -764,6 +762,7 @@ impl AcpThreadView {
MessageEditorEvent::Focus => { MessageEditorEvent::Focus => {
self.cancel_editing(&Default::default(), window, cx); self.cancel_editing(&Default::default(), window, cx);
} }
MessageEditorEvent::LostFocus => {}
} }
} }
@ -795,6 +794,18 @@ impl AcpThreadView {
cx.notify(); cx.notify();
} }
} }
ViewEvent::MessageEditorEvent(editor, MessageEditorEvent::LostFocus) => {
if let Some(thread) = self.thread()
&& let Some(AgentThreadEntry::UserMessage(user_message)) =
thread.read(cx).entries().get(event.entry_index)
&& user_message.id.is_some()
{
if editor.read(cx).text(cx).as_str() == user_message.content.to_markdown(cx) {
self.editing_message = None;
cx.notify();
}
}
}
ViewEvent::MessageEditorEvent(editor, MessageEditorEvent::Send) => { ViewEvent::MessageEditorEvent(editor, MessageEditorEvent::Send) => {
self.regenerate(event.entry_index, editor, window, cx); self.regenerate(event.entry_index, editor, window, cx);
} }
@ -1722,10 +1733,9 @@ impl AcpThreadView {
matches!(tool_call.kind, acp::ToolKind::Edit) || tool_call.diffs().next().is_some(); matches!(tool_call.kind, acp::ToolKind::Edit) || tool_call.diffs().next().is_some();
let use_card_layout = needs_confirmation || is_edit; let use_card_layout = needs_confirmation || is_edit;
let is_collapsible = !tool_call.content.is_empty() && !use_card_layout; let is_collapsible = !tool_call.content.is_empty() && !needs_confirmation;
let is_open = let is_open = needs_confirmation || self.expanded_tool_calls.contains(&tool_call.id);
needs_confirmation || is_edit || self.expanded_tool_calls.contains(&tool_call.id);
let gradient_overlay = |color: Hsla| { let gradient_overlay = |color: Hsla| {
div() div()
@ -2195,6 +2205,8 @@ impl AcpThreadView {
.map(|path| format!("{}", path.display())) .map(|path| format!("{}", path.display()))
.unwrap_or_else(|| "current directory".to_string()); .unwrap_or_else(|| "current directory".to_string());
let is_expanded = self.expanded_tool_calls.contains(&tool_call.id);
let header = h_flex() let header = h_flex()
.id(SharedString::from(format!( .id(SharedString::from(format!(
"terminal-tool-header-{}", "terminal-tool-header-{}",
@ -2328,21 +2340,27 @@ impl AcpThreadView {
"terminal-tool-disclosure-{}", "terminal-tool-disclosure-{}",
terminal.entity_id() terminal.entity_id()
)), )),
self.terminal_expanded, is_expanded,
) )
.opened_icon(IconName::ChevronUp) .opened_icon(IconName::ChevronUp)
.closed_icon(IconName::ChevronDown) .closed_icon(IconName::ChevronDown)
.on_click(cx.listener(move |this, _event, _window, _cx| { .on_click(cx.listener({
this.terminal_expanded = !this.terminal_expanded; let id = tool_call.id.clone();
})), move |this, _event, _window, _cx| {
); if is_expanded {
this.expanded_tool_calls.remove(&id);
} else {
this.expanded_tool_calls.insert(id.clone());
}
}})),
);
let terminal_view = self let terminal_view = self
.entry_view_state .entry_view_state
.read(cx) .read(cx)
.entry(entry_ix) .entry(entry_ix)
.and_then(|entry| entry.terminal(terminal)); .and_then(|entry| entry.terminal(terminal));
let show_output = self.terminal_expanded && terminal_view.is_some(); let show_output = is_expanded && terminal_view.is_some();
v_flex() v_flex()
.mb_2() .mb_2()

View file

@ -24,6 +24,7 @@ serde_json_lenient.workspace = true
theme.workspace = true theme.workspace = true
ui.workspace = true ui.workspace = true
util.workspace = true util.workspace = true
util_macros.workspace = true
workspace-hack.workspace = true workspace-hack.workspace = true
workspace.workspace = true workspace.workspace = true
zed_actions.workspace = true zed_actions.workspace = true

View file

@ -25,7 +25,7 @@ use util::split_str_with_ranges;
/// Path used for unsaved buffer that contains style json. To support the json language server, this /// Path used for unsaved buffer that contains style json. To support the json language server, this
/// matches the name used in the generated schemas. /// matches the name used in the generated schemas.
const ZED_INSPECTOR_STYLE_JSON: &str = "/zed-inspector-style.json"; const ZED_INSPECTOR_STYLE_JSON: &str = util_macros::path!("/zed-inspector-style.json");
pub(crate) struct DivInspector { pub(crate) struct DivInspector {
state: State, state: State,

View file

@ -231,6 +231,7 @@
"implements" "implements"
"interface" "interface"
"keyof" "keyof"
"module"
"namespace" "namespace"
"private" "private"
"protected" "protected"
@ -250,4 +251,4 @@
(jsx_closing_element (["</" ">"]) @punctuation.bracket.jsx) (jsx_closing_element (["</" ">"]) @punctuation.bracket.jsx)
(jsx_self_closing_element (["<" "/>"]) @punctuation.bracket.jsx) (jsx_self_closing_element (["<" "/>"]) @punctuation.bracket.jsx)
(jsx_attribute "=" @punctuation.delimiter.jsx) (jsx_attribute "=" @punctuation.delimiter.jsx)
(jsx_text) @text.jsx (jsx_text) @text.jsx

View file

@ -237,6 +237,7 @@
"implements" "implements"
"interface" "interface"
"keyof" "keyof"
"module"
"namespace" "namespace"
"private" "private"
"protected" "protected"
@ -256,4 +257,4 @@
(jsx_closing_element (["</" ">"]) @punctuation.bracket.jsx) (jsx_closing_element (["</" ">"]) @punctuation.bracket.jsx)
(jsx_self_closing_element (["<" "/>"]) @punctuation.bracket.jsx) (jsx_self_closing_element (["<" "/>"]) @punctuation.bracket.jsx)
(jsx_attribute "=" @punctuation.delimiter.jsx) (jsx_attribute "=" @punctuation.delimiter.jsx)
(jsx_text) @text.jsx (jsx_text) @text.jsx

View file

@ -248,6 +248,7 @@
"is" "is"
"keyof" "keyof"
"let" "let"
"module"
"namespace" "namespace"
"new" "new"
"of" "of"
@ -272,4 +273,4 @@
"while" "while"
"with" "with"
"yield" "yield"
] @keyword ] @keyword

View file

@ -835,7 +835,7 @@ impl MultiBuffer {
this.convert_edits_to_buffer_edits(edits, &snapshot, &original_indent_columns); this.convert_edits_to_buffer_edits(edits, &snapshot, &original_indent_columns);
drop(snapshot); drop(snapshot);
let mut buffer_ids = Vec::new(); let mut buffer_ids = Vec::with_capacity(buffer_edits.len());
for (buffer_id, mut edits) in buffer_edits { for (buffer_id, mut edits) in buffer_edits {
buffer_ids.push(buffer_id); buffer_ids.push(buffer_id);
edits.sort_by_key(|edit| edit.range.start); edits.sort_by_key(|edit| edit.range.start);