Add terminal inline assistant (#13638)

Release Notes:

- N/A

---------

Co-authored-by: Antonio <antonio@zed.dev>
This commit is contained in:
Bennet Bo Fenner 2024-07-01 20:53:56 +02:00 committed by GitHub
parent c516b8f038
commit e243856559
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 1587 additions and 141 deletions

View file

@ -7,6 +7,7 @@ use crate::{
default_command::DefaultSlashCommand, SlashCommandCompletionProvider, SlashCommandLine,
SlashCommandRegistry,
},
terminal_inline_assistant::TerminalInlineAssistant,
ApplyEdit, Assist, CompletionProvider, ConfirmCommand, ContextStore, CycleMessageRole,
InlineAssist, InlineAssistant, LanguageModelRequest, LanguageModelRequestMessage, MessageId,
MessageMetadata, MessageStatus, ModelSelector, QuoteSelection, ResetKey, Role, SavedContext,
@ -58,6 +59,7 @@ use std::{
time::{Duration, Instant},
};
use telemetry_events::AssistantKind;
use terminal_view::{terminal_panel::TerminalPanel, TerminalView};
use ui::{
prelude::*, ButtonLike, ContextMenu, Disclosure, ElevationIndex, KeyBinding, ListItem,
ListItemSpacing, PopoverMenu, PopoverMenuHandle, Tab, TabBar, Tooltip,
@ -124,6 +126,11 @@ enum SavedContextPickerEvent {
Confirmed { path: PathBuf },
}
enum InlineAssistTarget {
Editor(View<Editor>, bool),
Terminal(View<TerminalView>),
}
impl EventEmitter<SavedContextPickerEvent> for Picker<SavedContextPickerDelegate> {}
impl SavedContextPickerDelegate {
@ -369,6 +376,103 @@ impl AssistantPanel {
return;
};
let Some(inline_assist_target) =
Self::resolve_inline_assist_target(workspace, &assistant_panel, cx)
else {
return;
};
if assistant_panel.update(cx, |assistant, cx| assistant.is_authenticated(cx)) {
match inline_assist_target {
InlineAssistTarget::Editor(active_editor, include_context) => {
InlineAssistant::update_global(cx, |assistant, cx| {
assistant.assist(
&active_editor,
Some(cx.view().downgrade()),
include_context.then_some(&assistant_panel),
cx,
)
})
}
InlineAssistTarget::Terminal(active_terminal) => {
TerminalInlineAssistant::update_global(cx, |assistant, cx| {
assistant.assist(
&active_terminal,
Some(cx.view().downgrade()),
Some(&assistant_panel),
cx,
)
})
}
}
} else {
let assistant_panel = assistant_panel.downgrade();
cx.spawn(|workspace, mut cx| async move {
assistant_panel
.update(&mut cx, |assistant, cx| assistant.authenticate(cx))?
.await?;
if assistant_panel.update(&mut cx, |panel, cx| panel.is_authenticated(cx))? {
cx.update(|cx| match inline_assist_target {
InlineAssistTarget::Editor(active_editor, include_context) => {
let assistant_panel = if include_context {
assistant_panel.upgrade()
} else {
None
};
InlineAssistant::update_global(cx, |assistant, cx| {
assistant.assist(
&active_editor,
Some(workspace),
assistant_panel.as_ref(),
cx,
)
})
}
InlineAssistTarget::Terminal(active_terminal) => {
TerminalInlineAssistant::update_global(cx, |assistant, cx| {
assistant.assist(
&active_terminal,
Some(workspace),
assistant_panel.upgrade().as_ref(),
cx,
)
})
}
})?
} else {
workspace.update(&mut cx, |workspace, cx| {
workspace.focus_panel::<AssistantPanel>(cx)
})?;
}
anyhow::Ok(())
})
.detach_and_log_err(cx)
}
}
fn resolve_inline_assist_target(
workspace: &mut Workspace,
assistant_panel: &View<AssistantPanel>,
cx: &mut WindowContext,
) -> Option<InlineAssistTarget> {
if let Some(terminal_panel) = workspace.panel::<TerminalPanel>(cx) {
if terminal_panel
.read(cx)
.focus_handle(cx)
.contains_focused(cx)
{
if let Some(terminal_view) = terminal_panel
.read(cx)
.pane()
.read(cx)
.active_item()
.and_then(|t| t.downcast::<TerminalView>())
{
return Some(InlineAssistTarget::Terminal(terminal_view));
}
}
}
let context_editor = assistant_panel
.read(cx)
.active_context_editor()
@ -381,63 +485,15 @@ impl AssistantPanel {
}
});
let include_context;
let active_editor;
if let Some(context_editor) = context_editor {
active_editor = context_editor;
include_context = false;
Some(InlineAssistTarget::Editor(context_editor, false))
} else if let Some(workspace_editor) = workspace
.active_item(cx)
.and_then(|item| item.act_as::<Editor>(cx))
{
active_editor = workspace_editor;
include_context = true;
Some(InlineAssistTarget::Editor(workspace_editor, true))
} else {
return;
};
if assistant_panel.update(cx, |panel, cx| panel.is_authenticated(cx)) {
InlineAssistant::update_global(cx, |assistant, cx| {
assistant.assist(
&active_editor,
Some(cx.view().downgrade()),
include_context.then_some(&assistant_panel),
cx,
)
})
} else {
let assistant_panel = assistant_panel.downgrade();
cx.spawn(|workspace, mut cx| async move {
assistant_panel
.update(&mut cx, |assistant, cx| assistant.authenticate(cx))?
.await?;
if assistant_panel
.update(&mut cx, |assistant, cx| assistant.is_authenticated(cx))?
{
cx.update(|cx| {
let assistant_panel = if include_context {
assistant_panel.upgrade()
} else {
None
};
InlineAssistant::update_global(cx, |assistant, cx| {
assistant.assist(
&active_editor,
Some(workspace),
assistant_panel.as_ref(),
cx,
)
})
})?
} else {
workspace.update(&mut cx, |workspace, cx| {
workspace.focus_panel::<AssistantPanel>(cx)
})?;
}
anyhow::Ok(())
})
.detach_and_log_err(cx)
None
}
}