Extract ContextEditor
to assistant_context_editor
(#23433)
This PR extracts the `ContextEditor` to the `assistant_context_editor` crate. As part of this, we have decoupled the `ContextEditor` from the `AssistantPanel`. There is now an `AssistantPanelDelegate` that the `ContextEditor` uses when it needs to interface with the Assistant panel. Release Notes: - N/A
This commit is contained in:
parent
9a7f1d1de4
commit
417760ade7
12 changed files with 263 additions and 166 deletions
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -389,7 +389,6 @@ dependencies = [
|
||||||
"feature_flags",
|
"feature_flags",
|
||||||
"fs",
|
"fs",
|
||||||
"futures 0.3.31",
|
"futures 0.3.31",
|
||||||
"fuzzy",
|
|
||||||
"gpui",
|
"gpui",
|
||||||
"indexed_docs",
|
"indexed_docs",
|
||||||
"indoc",
|
"indoc",
|
||||||
|
@ -502,6 +501,7 @@ name = "assistant_context_editor"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"assistant_settings",
|
||||||
"assistant_slash_command",
|
"assistant_slash_command",
|
||||||
"assistant_slash_commands",
|
"assistant_slash_commands",
|
||||||
"assistant_tool",
|
"assistant_tool",
|
||||||
|
@ -516,18 +516,24 @@ dependencies = [
|
||||||
"futures 0.3.31",
|
"futures 0.3.31",
|
||||||
"fuzzy",
|
"fuzzy",
|
||||||
"gpui",
|
"gpui",
|
||||||
|
"indexed_docs",
|
||||||
"language",
|
"language",
|
||||||
"language_model",
|
"language_model",
|
||||||
|
"language_model_selector",
|
||||||
"language_models",
|
"language_models",
|
||||||
|
"languages",
|
||||||
"log",
|
"log",
|
||||||
|
"multi_buffer",
|
||||||
"open_ai",
|
"open_ai",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"paths",
|
"paths",
|
||||||
|
"picker",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"project",
|
"project",
|
||||||
"prompt_library",
|
"prompt_library",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"regex",
|
"regex",
|
||||||
|
"rope",
|
||||||
"rpc",
|
"rpc",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
@ -537,6 +543,8 @@ dependencies = [
|
||||||
"strum",
|
"strum",
|
||||||
"telemetry_events",
|
"telemetry_events",
|
||||||
"text",
|
"text",
|
||||||
|
"theme",
|
||||||
|
"tree-sitter-md",
|
||||||
"ui",
|
"ui",
|
||||||
"unindent",
|
"unindent",
|
||||||
"util",
|
"util",
|
||||||
|
|
|
@ -37,7 +37,6 @@ editor.workspace = true
|
||||||
feature_flags.workspace = true
|
feature_flags.workspace = true
|
||||||
fs.workspace = true
|
fs.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
fuzzy.workspace = true
|
|
||||||
gpui.workspace = true
|
gpui.workspace = true
|
||||||
indexed_docs.workspace = true
|
indexed_docs.workspace = true
|
||||||
indoc.workspace = true
|
indoc.workspace = true
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
#![cfg_attr(target_os = "windows", allow(unused, dead_code))]
|
#![cfg_attr(target_os = "windows", allow(unused, dead_code))]
|
||||||
|
|
||||||
pub mod assistant_panel;
|
pub mod assistant_panel;
|
||||||
mod context_editor;
|
|
||||||
mod context_history;
|
mod context_history;
|
||||||
mod inline_assistant;
|
mod inline_assistant;
|
||||||
mod slash_command;
|
|
||||||
pub(crate) mod slash_command_picker;
|
|
||||||
pub mod slash_command_settings;
|
pub mod slash_command_settings;
|
||||||
mod terminal_inline_assistant;
|
mod terminal_inline_assistant;
|
||||||
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use assistant_settings::AssistantSettings;
|
use assistant_settings::AssistantSettings;
|
||||||
|
@ -19,7 +15,6 @@ use client::Client;
|
||||||
use command_palette_hooks::CommandPaletteFilter;
|
use command_palette_hooks::CommandPaletteFilter;
|
||||||
use feature_flags::FeatureFlagAppExt;
|
use feature_flags::FeatureFlagAppExt;
|
||||||
use fs::Fs;
|
use fs::Fs;
|
||||||
use gpui::impl_internal_actions;
|
|
||||||
use gpui::{actions, AppContext, Global, UpdateGlobal};
|
use gpui::{actions, AppContext, Global, UpdateGlobal};
|
||||||
use language_model::{
|
use language_model::{
|
||||||
LanguageModelId, LanguageModelProviderId, LanguageModelRegistry, LanguageModelResponseMessage,
|
LanguageModelId, LanguageModelProviderId, LanguageModelRegistry, LanguageModelResponseMessage,
|
||||||
|
@ -37,33 +32,16 @@ use crate::slash_command_settings::SlashCommandSettings;
|
||||||
actions!(
|
actions!(
|
||||||
assistant,
|
assistant,
|
||||||
[
|
[
|
||||||
Assist,
|
|
||||||
Edit,
|
|
||||||
Split,
|
|
||||||
CopyCode,
|
|
||||||
CycleMessageRole,
|
|
||||||
QuoteSelection,
|
|
||||||
InsertIntoEditor,
|
|
||||||
ToggleFocus,
|
ToggleFocus,
|
||||||
InsertActivePrompt,
|
InsertActivePrompt,
|
||||||
DeployHistory,
|
DeployHistory,
|
||||||
DeployPromptLibrary,
|
DeployPromptLibrary,
|
||||||
ConfirmCommand,
|
|
||||||
NewContext,
|
NewContext,
|
||||||
ToggleModelSelector,
|
|
||||||
CycleNextInlineAssist,
|
CycleNextInlineAssist,
|
||||||
CyclePreviousInlineAssist
|
CyclePreviousInlineAssist
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
#[derive(PartialEq, Clone)]
|
|
||||||
pub enum InsertDraggedFiles {
|
|
||||||
ProjectPaths(Vec<PathBuf>),
|
|
||||||
ExternalFiles(Vec<PathBuf>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_internal_actions!(assistant, [InsertDraggedFiles]);
|
|
||||||
|
|
||||||
const DEFAULT_CONTEXT_LINES: usize = 50;
|
const DEFAULT_CONTEXT_LINES: usize = 50;
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
|
@ -334,24 +312,6 @@ fn update_slash_commands_from_settings(cx: &mut AppContext) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn humanize_token_count(count: usize) -> String {
|
|
||||||
match count {
|
|
||||||
0..=999 => count.to_string(),
|
|
||||||
1000..=9999 => {
|
|
||||||
let thousands = count / 1000;
|
|
||||||
let hundreds = (count % 1000 + 50) / 100;
|
|
||||||
if hundreds == 0 {
|
|
||||||
format!("{}k", thousands)
|
|
||||||
} else if hundreds == 10 {
|
|
||||||
format!("{}k", thousands + 1)
|
|
||||||
} else {
|
|
||||||
format!("{}.{}k", thousands, hundreds)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => format!("{}k", (count + 500) / 1000),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[ctor::ctor]
|
#[ctor::ctor]
|
||||||
fn init_logger() {
|
fn init_logger() {
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
use crate::context_editor::{
|
|
||||||
ContextEditor, ContextEditorToolbarItem, ContextEditorToolbarItemEvent, DEFAULT_TAB_TITLE,
|
|
||||||
};
|
|
||||||
use crate::context_history::ContextHistory;
|
use crate::context_history::ContextHistory;
|
||||||
use crate::{
|
use crate::{
|
||||||
slash_command::SlashCommandCompletionProvider,
|
|
||||||
terminal_inline_assistant::TerminalInlineAssistant, DeployHistory, DeployPromptLibrary,
|
terminal_inline_assistant::TerminalInlineAssistant, DeployHistory, DeployPromptLibrary,
|
||||||
InlineAssistant, InsertDraggedFiles, NewContext, ToggleFocus, ToggleModelSelector,
|
InlineAssistant, NewContext, ToggleFocus,
|
||||||
|
};
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use assistant_context_editor::{
|
||||||
|
AssistantPanelDelegate, Context, ContextEditor, ContextEditorToolbarItem,
|
||||||
|
ContextEditorToolbarItemEvent, ContextId, ContextStore, ContextStoreEvent, InsertDraggedFiles,
|
||||||
|
SlashCommandCompletionProvider, ToggleModelSelector, DEFAULT_TAB_TITLE,
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
|
||||||
use assistant_context_editor::{Context, ContextId, ContextStore, ContextStoreEvent};
|
|
||||||
use assistant_settings::{AssistantDockPosition, AssistantSettings};
|
use assistant_settings::{AssistantDockPosition, AssistantSettings};
|
||||||
use assistant_slash_command::SlashCommandWorkingSet;
|
use assistant_slash_command::SlashCommandWorkingSet;
|
||||||
use assistant_tool::ToolWorkingSet;
|
use assistant_tool::ToolWorkingSet;
|
||||||
|
@ -46,6 +46,8 @@ use workspace::{
|
||||||
use zed_actions::InlineAssist;
|
use zed_actions::InlineAssist;
|
||||||
|
|
||||||
pub fn init(cx: &mut AppContext) {
|
pub fn init(cx: &mut AppContext) {
|
||||||
|
<dyn AssistantPanelDelegate>::set_global(Arc::new(ConcreteAssistantPanelDelegate), cx);
|
||||||
|
|
||||||
workspace::FollowableViewRegistry::register::<ContextEditor>(cx);
|
workspace::FollowableViewRegistry::register::<ContextEditor>(cx);
|
||||||
cx.observe_new_views(
|
cx.observe_new_views(
|
||||||
|workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
|
|workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
|
||||||
|
@ -438,7 +440,7 @@ impl AssistantPanel {
|
||||||
if let Some(context_editor) = self.active_context_editor(cx) {
|
if let Some(context_editor) = self.active_context_editor(cx) {
|
||||||
let new_summary = model_summary_editor.read(cx).text(cx);
|
let new_summary = model_summary_editor.read(cx).text(cx);
|
||||||
context_editor.update(cx, |context_editor, cx| {
|
context_editor.update(cx, |context_editor, cx| {
|
||||||
context_editor.context.update(cx, |context, cx| {
|
context_editor.context().update(cx, |context, cx| {
|
||||||
if context.summary().is_none()
|
if context.summary().is_none()
|
||||||
&& (new_summary == DEFAULT_TAB_TITLE || new_summary.trim().is_empty())
|
&& (new_summary == DEFAULT_TAB_TITLE || new_summary.trim().is_empty())
|
||||||
{
|
{
|
||||||
|
@ -475,7 +477,7 @@ impl AssistantPanel {
|
||||||
) {
|
) {
|
||||||
if let Some(context_editor) = self.active_context_editor(cx) {
|
if let Some(context_editor) = self.active_context_editor(cx) {
|
||||||
context_editor.update(cx, |context_editor, cx| {
|
context_editor.update(cx, |context_editor, cx| {
|
||||||
context_editor.context.update(cx, |context, cx| {
|
context_editor.context().update(cx, |context, cx| {
|
||||||
context.summarize(true, cx);
|
context.summarize(true, cx);
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -501,7 +503,6 @@ impl AssistantPanel {
|
||||||
.log_err()
|
.log_err()
|
||||||
.flatten();
|
.flatten();
|
||||||
|
|
||||||
let assistant_panel = cx.view().downgrade();
|
|
||||||
let editor = cx.new_view(|cx| {
|
let editor = cx.new_view(|cx| {
|
||||||
let mut editor = ContextEditor::for_context(
|
let mut editor = ContextEditor::for_context(
|
||||||
context,
|
context,
|
||||||
|
@ -509,7 +510,6 @@ impl AssistantPanel {
|
||||||
self.workspace.clone(),
|
self.workspace.clone(),
|
||||||
self.project.clone(),
|
self.project.clone(),
|
||||||
lsp_adapter_delegate,
|
lsp_adapter_delegate,
|
||||||
assistant_panel,
|
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
editor.insert_default_prompt(cx);
|
editor.insert_default_prompt(cx);
|
||||||
|
@ -523,7 +523,7 @@ impl AssistantPanel {
|
||||||
if let Some(editor) = self.active_context_editor(cx) {
|
if let Some(editor) = self.active_context_editor(cx) {
|
||||||
editor.update(cx, |active_context, cx| {
|
editor.update(cx, |active_context, cx| {
|
||||||
active_context
|
active_context
|
||||||
.context
|
.context()
|
||||||
.update(cx, |context, cx| context.completion_provider_changed(cx))
|
.update(cx, |context, cx| context.completion_provider_changed(cx))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -716,7 +716,7 @@ impl AssistantPanel {
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.active_context_editor(cx)
|
.active_context_editor(cx)
|
||||||
.and_then(|editor| {
|
.and_then(|editor| {
|
||||||
let editor = &editor.read(cx).editor;
|
let editor = &editor.read(cx).editor().clone();
|
||||||
if editor.read(cx).is_focused(cx) {
|
if editor.read(cx).is_focused(cx) {
|
||||||
Some(editor.clone())
|
Some(editor.clone())
|
||||||
} else {
|
} else {
|
||||||
|
@ -778,7 +778,6 @@ impl AssistantPanel {
|
||||||
|
|
||||||
let fs = this.fs.clone();
|
let fs = this.fs.clone();
|
||||||
let project = this.project.clone();
|
let project = this.project.clone();
|
||||||
let weak_assistant_panel = cx.view().downgrade();
|
|
||||||
|
|
||||||
let editor = cx.new_view(|cx| {
|
let editor = cx.new_view(|cx| {
|
||||||
ContextEditor::for_context(
|
ContextEditor::for_context(
|
||||||
|
@ -787,7 +786,6 @@ impl AssistantPanel {
|
||||||
workspace,
|
workspace,
|
||||||
project,
|
project,
|
||||||
lsp_adapter_delegate,
|
lsp_adapter_delegate,
|
||||||
weak_assistant_panel,
|
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
@ -808,7 +806,6 @@ impl AssistantPanel {
|
||||||
.log_err()
|
.log_err()
|
||||||
.flatten();
|
.flatten();
|
||||||
|
|
||||||
let assistant_panel = cx.view().downgrade();
|
|
||||||
let editor = cx.new_view(|cx| {
|
let editor = cx.new_view(|cx| {
|
||||||
let mut editor = ContextEditor::for_context(
|
let mut editor = ContextEditor::for_context(
|
||||||
context,
|
context,
|
||||||
|
@ -816,7 +813,6 @@ impl AssistantPanel {
|
||||||
self.workspace.clone(),
|
self.workspace.clone(),
|
||||||
self.project.clone(),
|
self.project.clone(),
|
||||||
lsp_adapter_delegate,
|
lsp_adapter_delegate,
|
||||||
assistant_panel,
|
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
editor.insert_default_prompt(cx);
|
editor.insert_default_prompt(cx);
|
||||||
|
@ -1013,7 +1009,7 @@ impl AssistantPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn active_context(&self, cx: &AppContext) -> Option<Model<Context>> {
|
pub fn active_context(&self, cx: &AppContext) -> Option<Model<Context>> {
|
||||||
Some(self.active_context_editor(cx)?.read(cx).context.clone())
|
Some(self.active_context_editor(cx)?.read(cx).context().clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_saved_context(
|
pub fn open_saved_context(
|
||||||
|
@ -1023,7 +1019,7 @@ impl AssistantPanel {
|
||||||
) -> Task<Result<()>> {
|
) -> Task<Result<()>> {
|
||||||
let existing_context = self.pane.read(cx).items().find_map(|item| {
|
let existing_context = self.pane.read(cx).items().find_map(|item| {
|
||||||
item.downcast::<ContextEditor>()
|
item.downcast::<ContextEditor>()
|
||||||
.filter(|editor| editor.read(cx).context.read(cx).path() == Some(&path))
|
.filter(|editor| editor.read(cx).context().read(cx).path() == Some(&path))
|
||||||
});
|
});
|
||||||
if let Some(existing_context) = existing_context {
|
if let Some(existing_context) = existing_context {
|
||||||
return cx.spawn(|this, mut cx| async move {
|
return cx.spawn(|this, mut cx| async move {
|
||||||
|
@ -1042,7 +1038,6 @@ impl AssistantPanel {
|
||||||
|
|
||||||
cx.spawn(|this, mut cx| async move {
|
cx.spawn(|this, mut cx| async move {
|
||||||
let context = context.await?;
|
let context = context.await?;
|
||||||
let assistant_panel = this.clone();
|
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
let editor = cx.new_view(|cx| {
|
let editor = cx.new_view(|cx| {
|
||||||
ContextEditor::for_context(
|
ContextEditor::for_context(
|
||||||
|
@ -1051,7 +1046,6 @@ impl AssistantPanel {
|
||||||
workspace,
|
workspace,
|
||||||
project,
|
project,
|
||||||
lsp_adapter_delegate,
|
lsp_adapter_delegate,
|
||||||
assistant_panel,
|
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
@ -1069,7 +1063,7 @@ impl AssistantPanel {
|
||||||
) -> Task<Result<View<ContextEditor>>> {
|
) -> Task<Result<View<ContextEditor>>> {
|
||||||
let existing_context = self.pane.read(cx).items().find_map(|item| {
|
let existing_context = self.pane.read(cx).items().find_map(|item| {
|
||||||
item.downcast::<ContextEditor>()
|
item.downcast::<ContextEditor>()
|
||||||
.filter(|editor| *editor.read(cx).context.read(cx).id() == id)
|
.filter(|editor| *editor.read(cx).context().read(cx).id() == id)
|
||||||
});
|
});
|
||||||
if let Some(existing_context) = existing_context {
|
if let Some(existing_context) = existing_context {
|
||||||
return cx.spawn(|this, mut cx| async move {
|
return cx.spawn(|this, mut cx| async move {
|
||||||
|
@ -1091,7 +1085,6 @@ impl AssistantPanel {
|
||||||
|
|
||||||
cx.spawn(|this, mut cx| async move {
|
cx.spawn(|this, mut cx| async move {
|
||||||
let context = context.await?;
|
let context = context.await?;
|
||||||
let assistant_panel = this.clone();
|
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
let editor = cx.new_view(|cx| {
|
let editor = cx.new_view(|cx| {
|
||||||
ContextEditor::for_context(
|
ContextEditor::for_context(
|
||||||
|
@ -1100,7 +1093,6 @@ impl AssistantPanel {
|
||||||
workspace,
|
workspace,
|
||||||
this.project.clone(),
|
this.project.clone(),
|
||||||
lsp_adapter_delegate,
|
lsp_adapter_delegate,
|
||||||
assistant_panel,
|
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
@ -1304,6 +1296,61 @@ impl prompt_library::InlineAssistDelegate for PromptLibraryInlineAssist {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ConcreteAssistantPanelDelegate;
|
||||||
|
|
||||||
|
impl AssistantPanelDelegate for ConcreteAssistantPanelDelegate {
|
||||||
|
fn active_context_editor(
|
||||||
|
&self,
|
||||||
|
workspace: &mut Workspace,
|
||||||
|
cx: &mut ViewContext<Workspace>,
|
||||||
|
) -> Option<View<ContextEditor>> {
|
||||||
|
let panel = workspace.panel::<AssistantPanel>(cx)?;
|
||||||
|
panel.read(cx).active_context_editor(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open_remote_context(
|
||||||
|
&self,
|
||||||
|
workspace: &mut Workspace,
|
||||||
|
context_id: ContextId,
|
||||||
|
cx: &mut ViewContext<Workspace>,
|
||||||
|
) -> Task<Result<View<ContextEditor>>> {
|
||||||
|
let Some(panel) = workspace.panel::<AssistantPanel>(cx) else {
|
||||||
|
return Task::ready(Err(anyhow!("no Assistant panel found")));
|
||||||
|
};
|
||||||
|
|
||||||
|
panel.update(cx, |panel, cx| panel.open_remote_context(context_id, cx))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn quote_selection(
|
||||||
|
&self,
|
||||||
|
workspace: &mut Workspace,
|
||||||
|
creases: Vec<(String, String)>,
|
||||||
|
cx: &mut ViewContext<Workspace>,
|
||||||
|
) {
|
||||||
|
let Some(panel) = workspace.panel::<AssistantPanel>(cx) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Activate the panel
|
||||||
|
if !panel.focus_handle(cx).contains_focused(cx) {
|
||||||
|
workspace.toggle_panel_focus::<AssistantPanel>(cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
panel.update(cx, |_, cx| {
|
||||||
|
// Wait to create a new context until the workspace is no longer
|
||||||
|
// being updated.
|
||||||
|
cx.defer(move |panel, cx| {
|
||||||
|
if let Some(context) = panel
|
||||||
|
.active_context_editor(cx)
|
||||||
|
.or_else(|| panel.new_context(cx))
|
||||||
|
{
|
||||||
|
context.update(cx, |context, cx| context.quote_creases(creases, cx));
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
pub enum WorkflowAssistStatus {
|
pub enum WorkflowAssistStatus {
|
||||||
Pending,
|
Pending,
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use assistant_context_editor::{ContextStore, RemoteContextMetadata, SavedContextMetadata};
|
use assistant_context_editor::{
|
||||||
|
ContextStore, RemoteContextMetadata, SavedContextMetadata, DEFAULT_TAB_TITLE,
|
||||||
|
};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
AppContext, EventEmitter, FocusHandle, FocusableView, Model, Subscription, Task, View, WeakView,
|
AppContext, EventEmitter, FocusHandle, FocusableView, Model, Subscription, Task, View, WeakView,
|
||||||
};
|
};
|
||||||
|
@ -10,7 +12,6 @@ use ui::utils::{format_distance_from_now, DateTimeType};
|
||||||
use ui::{prelude::*, Avatar, ListItem, ListItemSpacing};
|
use ui::{prelude::*, Avatar, ListItem, ListItemSpacing};
|
||||||
use workspace::Item;
|
use workspace::Item;
|
||||||
|
|
||||||
use crate::context_editor::DEFAULT_TAB_TITLE;
|
|
||||||
use crate::AssistantPanel;
|
use crate::AssistantPanel;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
humanize_token_count, AssistantPanel, AssistantPanelEvent, CycleNextInlineAssist,
|
AssistantPanel, AssistantPanelEvent, CycleNextInlineAssist, CyclePreviousInlineAssist,
|
||||||
CyclePreviousInlineAssist,
|
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Context as _, Result};
|
use anyhow::{anyhow, Context as _, Result};
|
||||||
use assistant_context_editor::RequestType;
|
use assistant_context_editor::{humanize_token_count, RequestType};
|
||||||
use assistant_settings::AssistantSettings;
|
use assistant_settings::AssistantSettings;
|
||||||
use client::{telemetry::Telemetry, ErrorExt};
|
use client::{telemetry::Telemetry, ErrorExt};
|
||||||
use collections::{hash_map, HashMap, HashSet, VecDeque};
|
use collections::{hash_map, HashMap, HashSet, VecDeque};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{humanize_token_count, AssistantPanel, AssistantPanelEvent, DEFAULT_CONTEXT_LINES};
|
use crate::{AssistantPanel, AssistantPanelEvent, DEFAULT_CONTEXT_LINES};
|
||||||
use anyhow::{Context as _, Result};
|
use anyhow::{Context as _, Result};
|
||||||
use assistant_context_editor::RequestType;
|
use assistant_context_editor::{humanize_token_count, RequestType};
|
||||||
use assistant_settings::AssistantSettings;
|
use assistant_settings::AssistantSettings;
|
||||||
use client::telemetry::Telemetry;
|
use client::telemetry::Telemetry;
|
||||||
use collections::{HashMap, VecDeque};
|
use collections::{HashMap, VecDeque};
|
||||||
|
|
|
@ -13,6 +13,7 @@ path = "src/assistant_context_editor.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
|
assistant_settings.workspace = true
|
||||||
assistant_slash_command.workspace = true
|
assistant_slash_command.workspace = true
|
||||||
assistant_slash_commands.workspace = true
|
assistant_slash_commands.workspace = true
|
||||||
assistant_tool.workspace = true
|
assistant_tool.workspace = true
|
||||||
|
@ -27,32 +28,41 @@ fs.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
fuzzy.workspace = true
|
fuzzy.workspace = true
|
||||||
gpui.workspace = true
|
gpui.workspace = true
|
||||||
|
indexed_docs.workspace = true
|
||||||
language.workspace = true
|
language.workspace = true
|
||||||
language_model.workspace = true
|
language_model.workspace = true
|
||||||
|
language_model_selector.workspace = true
|
||||||
language_models.workspace = true
|
language_models.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
|
multi_buffer.workspace = true
|
||||||
open_ai.workspace = true
|
open_ai.workspace = true
|
||||||
|
parking_lot.workspace = true
|
||||||
paths.workspace = true
|
paths.workspace = true
|
||||||
|
picker.workspace = true
|
||||||
project.workspace = true
|
project.workspace = true
|
||||||
prompt_library.workspace = true
|
prompt_library.workspace = true
|
||||||
regex.workspace = true
|
regex.workspace = true
|
||||||
|
rope.workspace = true
|
||||||
rpc.workspace = true
|
rpc.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
|
settings.workspace = true
|
||||||
smallvec.workspace = true
|
smallvec.workspace = true
|
||||||
smol.workspace = true
|
smol.workspace = true
|
||||||
strum.workspace = true
|
strum.workspace = true
|
||||||
telemetry_events.workspace = true
|
telemetry_events.workspace = true
|
||||||
text.workspace = true
|
text.workspace = true
|
||||||
|
theme.workspace = true
|
||||||
ui.workspace = true
|
ui.workspace = true
|
||||||
util.workspace = true
|
util.workspace = true
|
||||||
uuid.workspace = true
|
uuid.workspace = true
|
||||||
|
workspace.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
language_model = { workspace = true, features = ["test-support"] }
|
language_model = { workspace = true, features = ["test-support"] }
|
||||||
parking_lot.workspace = true
|
languages = { workspace = true, features = ["test-support"] }
|
||||||
pretty_assertions.workspace = true
|
pretty_assertions.workspace = true
|
||||||
rand.workspace = true
|
rand.workspace = true
|
||||||
settings.workspace = true
|
tree-sitter-md.workspace = true
|
||||||
unindent.workspace = true
|
unindent.workspace = true
|
||||||
workspace = { workspace = true, features = ["test-support"] }
|
workspace = { workspace = true, features = ["test-support"] }
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
mod context;
|
mod context;
|
||||||
|
mod context_editor;
|
||||||
mod context_store;
|
mod context_store;
|
||||||
mod patch;
|
mod patch;
|
||||||
|
mod slash_command;
|
||||||
|
mod slash_command_picker;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -8,8 +11,10 @@ use client::Client;
|
||||||
use gpui::AppContext;
|
use gpui::AppContext;
|
||||||
|
|
||||||
pub use crate::context::*;
|
pub use crate::context::*;
|
||||||
|
pub use crate::context_editor::*;
|
||||||
pub use crate::context_store::*;
|
pub use crate::context_store::*;
|
||||||
pub use crate::patch::*;
|
pub use crate::patch::*;
|
||||||
|
pub use crate::slash_command::*;
|
||||||
|
|
||||||
pub fn init(client: Arc<Client>, _cx: &mut AppContext) {
|
pub fn init(client: Arc<Client>, _cx: &mut AppContext) {
|
||||||
context_store::init(&client.into());
|
context_store::init(&client.into());
|
||||||
|
|
|
@ -1,9 +1,4 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use assistant_context_editor::{
|
|
||||||
AssistantPatch, AssistantPatchStatus, CacheStatus, Content, Context, ContextEvent, ContextId,
|
|
||||||
InvokedSlashCommandId, InvokedSlashCommandStatus, Message, MessageId, MessageMetadata,
|
|
||||||
MessageStatus, ParsedSlashCommand, PendingSlashCommandStatus, RequestType,
|
|
||||||
};
|
|
||||||
use assistant_settings::AssistantSettings;
|
use assistant_settings::AssistantSettings;
|
||||||
use assistant_slash_command::{SlashCommand, SlashCommandOutputSection, SlashCommandWorkingSet};
|
use assistant_slash_command::{SlashCommand, SlashCommandOutputSection, SlashCommandWorkingSet};
|
||||||
use assistant_slash_commands::{
|
use assistant_slash_commands::{
|
||||||
|
@ -27,12 +22,12 @@ use editor::{display_map::CreaseId, FoldPlaceholder};
|
||||||
use fs::Fs;
|
use fs::Fs;
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, img, percentage, point, prelude::*, pulsating_between, size, Animation, AnimationExt,
|
actions, div, img, impl_internal_actions, percentage, point, prelude::*, pulsating_between,
|
||||||
AnyElement, AnyView, AppContext, AsyncWindowContext, ClipboardEntry, ClipboardItem,
|
size, Animation, AnimationExt, AnyElement, AnyView, AppContext, AsyncWindowContext,
|
||||||
CursorStyle, Empty, Entity, EventEmitter, FocusHandle, FocusableView, FontWeight,
|
ClipboardEntry, ClipboardItem, CursorStyle, Empty, Entity, EventEmitter, FocusHandle,
|
||||||
InteractiveElement, IntoElement, Model, ParentElement, Pixels, Render, RenderImage,
|
FocusableView, FontWeight, Global, InteractiveElement, IntoElement, Model, ParentElement,
|
||||||
SharedString, Size, StatefulInteractiveElement, Styled, Subscription, Task, Transformation,
|
Pixels, Render, RenderImage, SharedString, Size, StatefulInteractiveElement, Styled,
|
||||||
View, WeakModel, WeakView,
|
Subscription, Task, Transformation, View, WeakModel, WeakView,
|
||||||
};
|
};
|
||||||
use indexed_docs::IndexedDocsStore;
|
use indexed_docs::IndexedDocsStore;
|
||||||
use language::{language_settings::SoftWrap, BufferSnapshot, LspAdapterDelegate, ToOffset};
|
use language::{language_settings::SoftWrap, BufferSnapshot, LspAdapterDelegate, ToOffset};
|
||||||
|
@ -61,10 +56,34 @@ use workspace::{
|
||||||
Workspace,
|
Workspace,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
actions!(
|
||||||
|
assistant,
|
||||||
|
[
|
||||||
|
Assist,
|
||||||
|
ConfirmCommand,
|
||||||
|
CopyCode,
|
||||||
|
CycleMessageRole,
|
||||||
|
Edit,
|
||||||
|
InsertIntoEditor,
|
||||||
|
QuoteSelection,
|
||||||
|
Split,
|
||||||
|
ToggleModelSelector,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive(PartialEq, Clone)]
|
||||||
|
pub enum InsertDraggedFiles {
|
||||||
|
ProjectPaths(Vec<PathBuf>),
|
||||||
|
ExternalFiles(Vec<PathBuf>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_internal_actions!(assistant, [InsertDraggedFiles]);
|
||||||
|
|
||||||
|
use crate::{slash_command::SlashCommandCompletionProvider, slash_command_picker};
|
||||||
use crate::{
|
use crate::{
|
||||||
humanize_token_count, slash_command::SlashCommandCompletionProvider, slash_command_picker,
|
AssistantPatch, AssistantPatchStatus, CacheStatus, Content, Context, ContextEvent, ContextId,
|
||||||
Assist, AssistantPanel, ConfirmCommand, CopyCode, CycleMessageRole, Edit, InsertDraggedFiles,
|
InvokedSlashCommandId, InvokedSlashCommandStatus, Message, MessageId, MessageMetadata,
|
||||||
InsertIntoEditor, QuoteSelection, Split, ToggleModelSelector,
|
MessageStatus, ParsedSlashCommand, PendingSlashCommandStatus, RequestType,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
|
@ -94,15 +113,54 @@ enum AssistError {
|
||||||
Message(SharedString),
|
Message(SharedString),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait AssistantPanelDelegate {
|
||||||
|
fn active_context_editor(
|
||||||
|
&self,
|
||||||
|
workspace: &mut Workspace,
|
||||||
|
cx: &mut ViewContext<Workspace>,
|
||||||
|
) -> Option<View<ContextEditor>>;
|
||||||
|
|
||||||
|
fn open_remote_context(
|
||||||
|
&self,
|
||||||
|
workspace: &mut Workspace,
|
||||||
|
context_id: ContextId,
|
||||||
|
cx: &mut ViewContext<Workspace>,
|
||||||
|
) -> Task<Result<View<ContextEditor>>>;
|
||||||
|
|
||||||
|
fn quote_selection(
|
||||||
|
&self,
|
||||||
|
workspace: &mut Workspace,
|
||||||
|
creases: Vec<(String, String)>,
|
||||||
|
cx: &mut ViewContext<Workspace>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl dyn AssistantPanelDelegate {
|
||||||
|
/// Returns the global [`AssistantPanelDelegate`], if it exists.
|
||||||
|
pub fn try_global(cx: &AppContext) -> Option<Arc<Self>> {
|
||||||
|
cx.try_global::<GlobalAssistantPanelDelegate>()
|
||||||
|
.map(|global| global.0.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the global [`AssistantPanelDelegate`].
|
||||||
|
pub fn set_global(delegate: Arc<Self>, cx: &mut AppContext) {
|
||||||
|
cx.set_global(GlobalAssistantPanelDelegate(delegate));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GlobalAssistantPanelDelegate(Arc<dyn AssistantPanelDelegate>);
|
||||||
|
|
||||||
|
impl Global for GlobalAssistantPanelDelegate {}
|
||||||
|
|
||||||
pub struct ContextEditor {
|
pub struct ContextEditor {
|
||||||
pub(crate) context: Model<Context>,
|
context: Model<Context>,
|
||||||
fs: Arc<dyn Fs>,
|
fs: Arc<dyn Fs>,
|
||||||
slash_commands: Arc<SlashCommandWorkingSet>,
|
slash_commands: Arc<SlashCommandWorkingSet>,
|
||||||
tools: Arc<ToolWorkingSet>,
|
tools: Arc<ToolWorkingSet>,
|
||||||
workspace: WeakView<Workspace>,
|
workspace: WeakView<Workspace>,
|
||||||
project: Model<Project>,
|
project: Model<Project>,
|
||||||
lsp_adapter_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
lsp_adapter_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||||
pub(crate) editor: View<Editor>,
|
editor: View<Editor>,
|
||||||
blocks: HashMap<MessageId, (MessageHeader, CustomBlockId)>,
|
blocks: HashMap<MessageId, (MessageHeader, CustomBlockId)>,
|
||||||
image_blocks: HashSet<CustomBlockId>,
|
image_blocks: HashSet<CustomBlockId>,
|
||||||
scroll_position: Option<ScrollPosition>,
|
scroll_position: Option<ScrollPosition>,
|
||||||
|
@ -113,7 +171,6 @@ pub struct ContextEditor {
|
||||||
_subscriptions: Vec<Subscription>,
|
_subscriptions: Vec<Subscription>,
|
||||||
patches: HashMap<Range<language::Anchor>, PatchViewState>,
|
patches: HashMap<Range<language::Anchor>, PatchViewState>,
|
||||||
active_patch: Option<Range<language::Anchor>>,
|
active_patch: Option<Range<language::Anchor>>,
|
||||||
assistant_panel: WeakView<AssistantPanel>,
|
|
||||||
last_error: Option<AssistError>,
|
last_error: Option<AssistError>,
|
||||||
show_accept_terms: bool,
|
show_accept_terms: bool,
|
||||||
pub(crate) slash_menu_handle:
|
pub(crate) slash_menu_handle:
|
||||||
|
@ -130,13 +187,12 @@ pub const DEFAULT_TAB_TITLE: &str = "New Chat";
|
||||||
const MAX_TAB_TITLE_LEN: usize = 16;
|
const MAX_TAB_TITLE_LEN: usize = 16;
|
||||||
|
|
||||||
impl ContextEditor {
|
impl ContextEditor {
|
||||||
pub(crate) fn for_context(
|
pub fn for_context(
|
||||||
context: Model<Context>,
|
context: Model<Context>,
|
||||||
fs: Arc<dyn Fs>,
|
fs: Arc<dyn Fs>,
|
||||||
workspace: WeakView<Workspace>,
|
workspace: WeakView<Workspace>,
|
||||||
project: Model<Project>,
|
project: Model<Project>,
|
||||||
lsp_adapter_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
lsp_adapter_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||||
assistant_panel: WeakView<AssistantPanel>,
|
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let completion_provider = SlashCommandCompletionProvider::new(
|
let completion_provider = SlashCommandCompletionProvider::new(
|
||||||
|
@ -190,7 +246,6 @@ impl ContextEditor {
|
||||||
_subscriptions,
|
_subscriptions,
|
||||||
patches: HashMap::default(),
|
patches: HashMap::default(),
|
||||||
active_patch: None,
|
active_patch: None,
|
||||||
assistant_panel,
|
|
||||||
last_error: None,
|
last_error: None,
|
||||||
show_accept_terms: false,
|
show_accept_terms: false,
|
||||||
slash_menu_handle: Default::default(),
|
slash_menu_handle: Default::default(),
|
||||||
|
@ -203,6 +258,14 @@ impl ContextEditor {
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn context(&self) -> &Model<Context> {
|
||||||
|
&self.context
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn editor(&self) -> &View<Editor> {
|
||||||
|
&self.editor
|
||||||
|
}
|
||||||
|
|
||||||
pub fn insert_default_prompt(&mut self, cx: &mut ViewContext<Self>) {
|
pub fn insert_default_prompt(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
let command_name = DefaultSlashCommand.name();
|
let command_name = DefaultSlashCommand.name();
|
||||||
self.editor.update(cx, |editor, cx| {
|
self.editor.update(cx, |editor, cx| {
|
||||||
|
@ -1523,10 +1586,12 @@ impl ContextEditor {
|
||||||
_: &InsertIntoEditor,
|
_: &InsertIntoEditor,
|
||||||
cx: &mut ViewContext<Workspace>,
|
cx: &mut ViewContext<Workspace>,
|
||||||
) {
|
) {
|
||||||
let Some(panel) = workspace.panel::<AssistantPanel>(cx) else {
|
let Some(assistant_panel_delegate) = <dyn AssistantPanelDelegate>::try_global(cx) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let Some(context_editor_view) = panel.read(cx).active_context_editor(cx) else {
|
let Some(context_editor_view) =
|
||||||
|
assistant_panel_delegate.active_context_editor(workspace, cx)
|
||||||
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let Some(active_editor_view) = workspace
|
let Some(active_editor_view) = workspace
|
||||||
|
@ -1546,8 +1611,9 @@ impl ContextEditor {
|
||||||
|
|
||||||
pub fn copy_code(workspace: &mut Workspace, _: &CopyCode, cx: &mut ViewContext<Workspace>) {
|
pub fn copy_code(workspace: &mut Workspace, _: &CopyCode, cx: &mut ViewContext<Workspace>) {
|
||||||
let result = maybe!({
|
let result = maybe!({
|
||||||
let panel = workspace.panel::<AssistantPanel>(cx)?;
|
let assistant_panel_delegate = <dyn AssistantPanelDelegate>::try_global(cx)?;
|
||||||
let context_editor_view = panel.read(cx).active_context_editor(cx)?;
|
let context_editor_view =
|
||||||
|
assistant_panel_delegate.active_context_editor(workspace, cx)?;
|
||||||
Self::get_selection_or_code_block(&context_editor_view, cx)
|
Self::get_selection_or_code_block(&context_editor_view, cx)
|
||||||
});
|
});
|
||||||
let Some((text, is_code_block)) = result else {
|
let Some((text, is_code_block)) = result else {
|
||||||
|
@ -1579,10 +1645,12 @@ impl ContextEditor {
|
||||||
action: &InsertDraggedFiles,
|
action: &InsertDraggedFiles,
|
||||||
cx: &mut ViewContext<Workspace>,
|
cx: &mut ViewContext<Workspace>,
|
||||||
) {
|
) {
|
||||||
let Some(panel) = workspace.panel::<AssistantPanel>(cx) else {
|
let Some(assistant_panel_delegate) = <dyn AssistantPanelDelegate>::try_global(cx) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let Some(context_editor_view) = panel.read(cx).active_context_editor(cx) else {
|
let Some(context_editor_view) =
|
||||||
|
assistant_panel_delegate.active_context_editor(workspace, cx)
|
||||||
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1653,7 +1721,7 @@ impl ContextEditor {
|
||||||
_: &QuoteSelection,
|
_: &QuoteSelection,
|
||||||
cx: &mut ViewContext<Workspace>,
|
cx: &mut ViewContext<Workspace>,
|
||||||
) {
|
) {
|
||||||
let Some(panel) = workspace.panel::<AssistantPanel>(cx) else {
|
let Some(assistant_panel_delegate) = <dyn AssistantPanelDelegate>::try_global(cx) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1664,61 +1732,46 @@ impl ContextEditor {
|
||||||
if creases.is_empty() {
|
if creases.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Activate the panel
|
|
||||||
if !panel.focus_handle(cx).contains_focused(cx) {
|
|
||||||
workspace.toggle_panel_focus::<AssistantPanel>(cx);
|
|
||||||
}
|
|
||||||
|
|
||||||
panel.update(cx, |_, cx| {
|
assistant_panel_delegate.quote_selection(workspace, creases, cx);
|
||||||
// Wait to create a new context until the workspace is no longer
|
}
|
||||||
// being updated.
|
|
||||||
cx.defer(move |panel, cx| {
|
|
||||||
if let Some(context) = panel
|
|
||||||
.active_context_editor(cx)
|
|
||||||
.or_else(|| panel.new_context(cx))
|
|
||||||
{
|
|
||||||
context.update(cx, |context, cx| {
|
|
||||||
context.editor.update(cx, |editor, cx| {
|
|
||||||
editor.insert("\n", cx);
|
|
||||||
for (text, crease_title) in creases {
|
|
||||||
let point = editor.selections.newest::<Point>(cx).head();
|
|
||||||
let start_row = MultiBufferRow(point.row);
|
|
||||||
|
|
||||||
editor.insert(&text, cx);
|
pub fn quote_creases(&mut self, creases: Vec<(String, String)>, cx: &mut ViewContext<Self>) {
|
||||||
|
self.editor.update(cx, |editor, cx| {
|
||||||
|
editor.insert("\n", cx);
|
||||||
|
for (text, crease_title) in creases {
|
||||||
|
let point = editor.selections.newest::<Point>(cx).head();
|
||||||
|
let start_row = MultiBufferRow(point.row);
|
||||||
|
|
||||||
let snapshot = editor.buffer().read(cx).snapshot(cx);
|
editor.insert(&text, cx);
|
||||||
let anchor_before = snapshot.anchor_after(point);
|
|
||||||
let anchor_after = editor
|
|
||||||
.selections
|
|
||||||
.newest_anchor()
|
|
||||||
.head()
|
|
||||||
.bias_left(&snapshot);
|
|
||||||
|
|
||||||
editor.insert("\n", cx);
|
let snapshot = editor.buffer().read(cx).snapshot(cx);
|
||||||
|
let anchor_before = snapshot.anchor_after(point);
|
||||||
|
let anchor_after = editor
|
||||||
|
.selections
|
||||||
|
.newest_anchor()
|
||||||
|
.head()
|
||||||
|
.bias_left(&snapshot);
|
||||||
|
|
||||||
let fold_placeholder = quote_selection_fold_placeholder(
|
editor.insert("\n", cx);
|
||||||
crease_title,
|
|
||||||
cx.view().downgrade(),
|
let fold_placeholder =
|
||||||
);
|
quote_selection_fold_placeholder(crease_title, cx.view().downgrade());
|
||||||
let crease = Crease::inline(
|
let crease = Crease::inline(
|
||||||
anchor_before..anchor_after,
|
anchor_before..anchor_after,
|
||||||
fold_placeholder,
|
fold_placeholder,
|
||||||
render_quote_selection_output_toggle,
|
render_quote_selection_output_toggle,
|
||||||
|_, _, _| Empty.into_any(),
|
|_, _, _| Empty.into_any(),
|
||||||
);
|
);
|
||||||
editor.insert_creases(vec![crease], cx);
|
editor.insert_creases(vec![crease], cx);
|
||||||
editor.fold_at(
|
editor.fold_at(
|
||||||
&FoldAt {
|
&FoldAt {
|
||||||
buffer_row: start_row,
|
buffer_row: start_row,
|
||||||
},
|
},
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
});
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn copy(&mut self, _: &editor::actions::Copy, cx: &mut ViewContext<Self>) {
|
fn copy(&mut self, _: &editor::actions::Copy, cx: &mut ViewContext<Self>) {
|
||||||
|
@ -2154,10 +2207,10 @@ impl ContextEditor {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_notice(&self, cx: &mut ViewContext<Self>) -> Option<AnyElement> {
|
fn render_notice(&self, cx: &mut ViewContext<Self>) -> Option<AnyElement> {
|
||||||
use feature_flags::FeatureFlagAppExt;
|
// This was previously gated behind the `zed-pro` feature flag. Since we
|
||||||
let nudge = self.assistant_panel.upgrade().map(|assistant_panel| {
|
// aren't planning to ship that right now, we're just hard-coding this
|
||||||
assistant_panel.read(cx).show_zed_ai_notice && cx.has_flag::<feature_flags::ZedPro>()
|
// value to not show the nudge.
|
||||||
});
|
let nudge = Some(false);
|
||||||
|
|
||||||
if nudge.map_or(false, |value| value) {
|
if nudge.map_or(false, |value| value) {
|
||||||
Some(
|
Some(
|
||||||
|
@ -3039,18 +3092,15 @@ impl FollowableItem for ContextEditor {
|
||||||
let context_id = ContextId::from_proto(state.context_id);
|
let context_id = ContextId::from_proto(state.context_id);
|
||||||
let editor_state = state.editor?;
|
let editor_state = state.editor?;
|
||||||
|
|
||||||
let (project, panel) = workspace.update(cx, |workspace, cx| {
|
let project = workspace.read(cx).project().clone();
|
||||||
Some((
|
let assistant_panel_delegate = <dyn AssistantPanelDelegate>::try_global(cx)?;
|
||||||
workspace.project().clone(),
|
|
||||||
workspace.panel::<AssistantPanel>(cx)?,
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let context_editor =
|
let context_editor_task = workspace.update(cx, |workspace, cx| {
|
||||||
panel.update(cx, |panel, cx| panel.open_remote_context(context_id, cx));
|
assistant_panel_delegate.open_remote_context(workspace, context_id, cx)
|
||||||
|
});
|
||||||
|
|
||||||
Some(cx.spawn(|mut cx| async move {
|
Some(cx.spawn(|mut cx| async move {
|
||||||
let context_editor = context_editor.await?;
|
let context_editor = context_editor_task.await?;
|
||||||
context_editor
|
context_editor
|
||||||
.update(&mut cx, |context_editor, cx| {
|
.update(&mut cx, |context_editor, cx| {
|
||||||
context_editor.remote_id = Some(id);
|
context_editor.remote_id = Some(id);
|
||||||
|
@ -3466,6 +3516,24 @@ fn configuration_error(cx: &AppContext) -> Option<ConfigurationError> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn humanize_token_count(count: usize) -> String {
|
||||||
|
match count {
|
||||||
|
0..=999 => count.to_string(),
|
||||||
|
1000..=9999 => {
|
||||||
|
let thousands = count / 1000;
|
||||||
|
let hundreds = (count % 1000 + 50) / 100;
|
||||||
|
if hundreds == 0 {
|
||||||
|
format!("{}k", thousands)
|
||||||
|
} else if hundreds == 10 {
|
||||||
|
format!("{}k", thousands + 1)
|
||||||
|
} else {
|
||||||
|
format!("{}.{}k", thousands, hundreds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => format!("{}k", (count + 500) / 1000),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
|
@ -20,7 +20,7 @@ use std::{
|
||||||
};
|
};
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
pub(crate) struct SlashCommandCompletionProvider {
|
pub struct SlashCommandCompletionProvider {
|
||||||
cancel_flag: Mutex<Arc<AtomicBool>>,
|
cancel_flag: Mutex<Arc<AtomicBool>>,
|
||||||
slash_commands: Arc<SlashCommandWorkingSet>,
|
slash_commands: Arc<SlashCommandWorkingSet>,
|
||||||
editor: Option<WeakView<ContextEditor>>,
|
editor: Option<WeakView<ContextEditor>>,
|
Loading…
Add table
Add a link
Reference in a new issue