Add assistant_context_editor crate (#23429)

This PR adds a new `assistant_context_editor` crate.

This will ultimately house the `ContextEditor` so that it can be
consumed by both `assistant` and `assistant2`.

For the purposes of this PR, we just introduce the crate and move some
supporting constructs to it, such as the `ContextStore`.

Release Notes:

- N/A
This commit is contained in:
Marshall Bowers 2025-01-21 16:22:59 -05:00 committed by GitHub
parent c450cd51ea
commit 9a7f1d1de4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 304 additions and 211 deletions

58
Cargo.lock generated
View file

@ -372,14 +372,13 @@ name = "assistant"
version = "0.1.0"
dependencies = [
"anyhow",
"assistant_context_editor",
"assistant_settings",
"assistant_slash_command",
"assistant_slash_commands",
"assistant_tool",
"async-watch",
"chrono",
"client",
"clock",
"collections",
"command_palette_hooks",
"context_server",
@ -403,7 +402,6 @@ dependencies = [
"lsp",
"menu",
"multi_buffer",
"open_ai",
"parking_lot",
"paths",
"picker",
@ -412,21 +410,16 @@ dependencies = [
"prompt_library",
"proto",
"rand 0.8.5",
"regex",
"rope",
"rpc",
"schemars",
"search",
"semantic_index",
"serde",
"serde_json",
"serde_json_lenient",
"settings",
"similar",
"smallvec",
"smol",
"streaming_diff",
"strum",
"telemetry",
"telemetry_events",
"terminal",
@ -437,7 +430,6 @@ dependencies = [
"ui",
"unindent",
"util",
"uuid",
"workspace",
"zed_actions",
]
@ -505,6 +497,53 @@ dependencies = [
"zed_actions",
]
[[package]]
name = "assistant_context_editor"
version = "0.1.0"
dependencies = [
"anyhow",
"assistant_slash_command",
"assistant_slash_commands",
"assistant_tool",
"chrono",
"client",
"clock",
"collections",
"context_server",
"editor",
"feature_flags",
"fs",
"futures 0.3.31",
"fuzzy",
"gpui",
"language",
"language_model",
"language_models",
"log",
"open_ai",
"parking_lot",
"paths",
"pretty_assertions",
"project",
"prompt_library",
"rand 0.8.5",
"regex",
"rpc",
"serde",
"serde_json",
"settings",
"smallvec",
"smol",
"strum",
"telemetry_events",
"text",
"ui",
"unindent",
"util",
"uuid",
"workspace",
]
[[package]]
name = "assistant_settings"
version = "0.1.0"
@ -2640,6 +2679,7 @@ dependencies = [
"anthropic",
"anyhow",
"assistant",
"assistant_context_editor",
"assistant_slash_command",
"assistant_tool",
"async-stripe",

View file

@ -7,6 +7,7 @@ members = [
"crates/assets",
"crates/assistant",
"crates/assistant2",
"crates/assistant_context_editor",
"crates/assistant_settings",
"crates/assistant_slash_command",
"crates/assistant_slash_commands",
@ -204,6 +205,7 @@ anthropic = { path = "crates/anthropic" }
assets = { path = "crates/assets" }
assistant = { path = "crates/assistant" }
assistant2 = { path = "crates/assistant2" }
assistant_context_editor = { path = "crates/assistant_context_editor" }
assistant_settings = { path = "crates/assistant_settings" }
assistant_slash_command = { path = "crates/assistant_slash_command" }
assistant_slash_commands = { path = "crates/assistant_slash_commands" }

View file

@ -22,14 +22,13 @@ test-support = [
[dependencies]
anyhow.workspace = true
assistant_context_editor.workspace = true
assistant_settings.workspace = true
assistant_slash_command.workspace = true
assistant_slash_commands.workspace = true
assistant_tool.workspace = true
async-watch.workspace = true
chrono.workspace = true
client.workspace = true
clock.workspace = true
collections.workspace = true
command_palette_hooks.workspace = true
context_server.workspace = true
@ -50,27 +49,21 @@ log.workspace = true
lsp.workspace = true
menu.workspace = true
multi_buffer.workspace = true
open_ai = { workspace = true, features = ["schemars"] }
parking_lot.workspace = true
paths.workspace = true
picker.workspace = true
project.workspace = true
prompt_library.workspace = true
proto.workspace = true
regex.workspace = true
rope.workspace = true
rpc.workspace = true
schemars.workspace = true
search.workspace = true
semantic_index.workspace = true
serde.workspace = true
serde_json.workspace = true
settings.workspace = true
similar.workspace = true
smallvec.workspace = true
smol.workspace = true
streaming_diff.workspace = true
strum.workspace = true
telemetry.workspace = true
telemetry_events.workspace = true
terminal.workspace = true
@ -79,7 +72,6 @@ text.workspace = true
theme.workspace = true
ui.workspace = true
util.workspace = true
uuid.workspace = true
workspace.workspace = true
zed_actions.workspace = true

View file

@ -1,12 +1,9 @@
#![cfg_attr(target_os = "windows", allow(unused, dead_code))]
pub mod assistant_panel;
mod context;
mod context_editor;
mod context_history;
pub mod context_store;
mod inline_assistant;
mod patch;
mod slash_command;
pub(crate) mod slash_command_picker;
pub mod slash_command_settings;
@ -18,26 +15,23 @@ use std::sync::Arc;
use assistant_settings::AssistantSettings;
use assistant_slash_command::SlashCommandRegistry;
use assistant_slash_commands::{ProjectSlashCommandFeatureFlag, SearchSlashCommandFeatureFlag};
use client::{proto, Client};
use client::Client;
use command_palette_hooks::CommandPaletteFilter;
use feature_flags::FeatureFlagAppExt;
use fs::Fs;
use gpui::impl_internal_actions;
use gpui::{actions, AppContext, Global, SharedString, UpdateGlobal};
use gpui::{actions, AppContext, Global, UpdateGlobal};
use language_model::{
LanguageModelId, LanguageModelProviderId, LanguageModelRegistry, LanguageModelResponseMessage,
};
use prompt_library::{PromptBuilder, PromptLoadingParams};
use semantic_index::{CloudEmbeddingProvider, SemanticDb};
use serde::{Deserialize, Serialize};
use serde::Deserialize;
use settings::{Settings, SettingsStore};
use util::ResultExt;
pub use crate::assistant_panel::{AssistantPanel, AssistantPanelEvent};
pub use crate::context::*;
pub use crate::context_store::*;
pub(crate) use crate::inline_assistant::*;
pub use crate::patch::*;
use crate::slash_command_settings::SlashCommandSettings;
actions!(
@ -72,15 +66,6 @@ impl_internal_actions!(assistant, [InsertDraggedFiles]);
const DEFAULT_CONTEXT_LINES: usize = 50;
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct MessageId(clock::Lamport);
impl MessageId {
pub fn as_u64(self) -> u64 {
self.0.as_u64()
}
}
#[derive(Deserialize, Debug)]
pub struct LanguageModelUsage {
pub prompt_tokens: u32,
@ -95,55 +80,6 @@ pub struct LanguageModelChoiceDelta {
pub finish_reason: Option<String>,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum MessageStatus {
Pending,
Done,
Error(SharedString),
Canceled,
}
impl MessageStatus {
pub fn from_proto(status: proto::ContextMessageStatus) -> MessageStatus {
match status.variant {
Some(proto::context_message_status::Variant::Pending(_)) => MessageStatus::Pending,
Some(proto::context_message_status::Variant::Done(_)) => MessageStatus::Done,
Some(proto::context_message_status::Variant::Error(error)) => {
MessageStatus::Error(error.message.into())
}
Some(proto::context_message_status::Variant::Canceled(_)) => MessageStatus::Canceled,
None => MessageStatus::Pending,
}
}
pub fn to_proto(&self) -> proto::ContextMessageStatus {
match self {
MessageStatus::Pending => proto::ContextMessageStatus {
variant: Some(proto::context_message_status::Variant::Pending(
proto::context_message_status::Pending {},
)),
},
MessageStatus::Done => proto::ContextMessageStatus {
variant: Some(proto::context_message_status::Variant::Done(
proto::context_message_status::Done {},
)),
},
MessageStatus::Error(message) => proto::ContextMessageStatus {
variant: Some(proto::context_message_status::Variant::Error(
proto::context_message_status::Error {
message: message.to_string(),
},
)),
},
MessageStatus::Canceled => proto::ContextMessageStatus {
variant: Some(proto::context_message_status::Variant::Canceled(
proto::context_message_status::Canceled {},
)),
},
}
}
}
/// The state pertaining to the Assistant.
#[derive(Default)]
struct Assistant {
@ -214,7 +150,7 @@ pub fn init(
})
.detach();
context_store::init(&client.clone().into());
assistant_context_editor::init(client.clone(), cx);
prompt_library::init(cx);
init_language_model_settings(cx);
assistant_slash_command::init(cx);

View file

@ -4,11 +4,11 @@ use crate::context_editor::{
use crate::context_history::ContextHistory;
use crate::{
slash_command::SlashCommandCompletionProvider,
terminal_inline_assistant::TerminalInlineAssistant, Context, ContextId, ContextStore,
ContextStoreEvent, DeployHistory, DeployPromptLibrary, InlineAssistant, InsertDraggedFiles,
NewContext, ToggleFocus, ToggleModelSelector,
terminal_inline_assistant::TerminalInlineAssistant, DeployHistory, DeployPromptLibrary,
InlineAssistant, InsertDraggedFiles, NewContext, ToggleFocus, ToggleModelSelector,
};
use anyhow::Result;
use assistant_context_editor::{Context, ContextId, ContextStore, ContextStoreEvent};
use assistant_settings::{AssistantDockPosition, AssistantSettings};
use assistant_slash_command::SlashCommandWorkingSet;
use assistant_tool::ToolWorkingSet;

View file

@ -1,4 +1,9 @@
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_slash_command::{SlashCommand, SlashCommandOutputSection, SlashCommandWorkingSet};
use assistant_slash_commands::{
@ -58,11 +63,8 @@ use workspace::{
use crate::{
humanize_token_count, slash_command::SlashCommandCompletionProvider, slash_command_picker,
Assist, AssistantPanel, AssistantPatch, AssistantPatchStatus, CacheStatus, ConfirmCommand,
Content, Context, ContextEvent, ContextId, CopyCode, CycleMessageRole, Edit,
InsertDraggedFiles, InsertIntoEditor, InvokedSlashCommandId, InvokedSlashCommandStatus,
Message, MessageId, MessageMetadata, MessageStatus, ParsedSlashCommand,
PendingSlashCommandStatus, QuoteSelection, RequestType, Split, ToggleModelSelector,
Assist, AssistantPanel, ConfirmCommand, CopyCode, CycleMessageRole, Edit, InsertDraggedFiles,
InsertIntoEditor, QuoteSelection, Split, ToggleModelSelector,
};
#[derive(Copy, Clone, Debug, PartialEq)]
@ -138,7 +140,7 @@ impl ContextEditor {
cx: &mut ViewContext<Self>,
) -> Self {
let completion_provider = SlashCommandCompletionProvider::new(
context.read(cx).slash_commands.clone(),
context.read(cx).slash_commands().clone(),
Some(cx.view().downgrade()),
Some(workspace.clone()),
);
@ -167,8 +169,8 @@ impl ContextEditor {
let sections = context.read(cx).slash_command_output_sections().to_vec();
let patch_ranges = context.read(cx).patch_ranges().collect::<Vec<_>>();
let slash_commands = context.read(cx).slash_commands.clone();
let tools = context.read(cx).tools.clone();
let slash_commands = context.read(cx).slash_commands().clone();
let tools = context.read(cx).tools().clone();
let mut this = Self {
context,
slash_commands,

View file

@ -1,5 +1,6 @@
use std::sync::Arc;
use assistant_context_editor::{ContextStore, RemoteContextMetadata, SavedContextMetadata};
use gpui::{
AppContext, EventEmitter, FocusHandle, FocusableView, Model, Subscription, Task, View, WeakView,
};
@ -10,7 +11,7 @@ use ui::{prelude::*, Avatar, ListItem, ListItemSpacing};
use workspace::Item;
use crate::context_editor::DEFAULT_TAB_TITLE;
use crate::{AssistantPanel, ContextStore, RemoteContextMetadata, SavedContextMetadata};
use crate::AssistantPanel;
#[derive(Clone)]
pub enum ContextMetadata {

View file

@ -1,8 +1,9 @@
use crate::{
humanize_token_count, AssistantPanel, AssistantPanelEvent, CycleNextInlineAssist,
CyclePreviousInlineAssist, RequestType,
CyclePreviousInlineAssist,
};
use anyhow::{anyhow, Context as _, Result};
use assistant_context_editor::RequestType;
use assistant_settings::AssistantSettings;
use client::{telemetry::Telemetry, ErrorExt};
use collections::{hash_map, HashMap, HashSet, VecDeque};

View file

@ -1,8 +1,7 @@
use crate::context_editor::ContextEditor;
use anyhow::Result;
use assistant_slash_command::AfterCompletion;
pub use assistant_slash_command::SlashCommand;
use assistant_slash_command::SlashCommandWorkingSet;
use assistant_slash_command::{AfterCompletion, SlashCommandLine, SlashCommandWorkingSet};
use editor::{CompletionProvider, Editor};
use fuzzy::{match_strings, StringMatchCandidate};
use gpui::{Model, Task, ViewContext, WeakView, WindowContext};
@ -28,13 +27,6 @@ pub(crate) struct SlashCommandCompletionProvider {
workspace: Option<WeakView<Workspace>>,
}
pub(crate) struct SlashCommandLine {
/// The range within the line containing the command name.
pub name: Range<usize>,
/// Ranges within the line containing the command arguments.
pub arguments: Vec<Range<usize>>,
}
impl SlashCommandCompletionProvider {
pub fn new(
slash_commands: Arc<SlashCommandWorkingSet>,
@ -336,57 +328,3 @@ impl CompletionProvider for SlashCommandCompletionProvider {
false
}
}
impl SlashCommandLine {
pub(crate) fn parse(line: &str) -> Option<Self> {
let mut call: Option<Self> = None;
let mut ix = 0;
for c in line.chars() {
let next_ix = ix + c.len_utf8();
if let Some(call) = &mut call {
// The command arguments start at the first non-whitespace character
// after the command name, and continue until the end of the line.
if let Some(argument) = call.arguments.last_mut() {
if c.is_whitespace() {
if (*argument).is_empty() {
argument.start = next_ix;
argument.end = next_ix;
} else {
argument.end = ix;
call.arguments.push(next_ix..next_ix);
}
} else {
argument.end = next_ix;
}
}
// The command name ends at the first whitespace character.
else if !call.name.is_empty() {
if c.is_whitespace() {
call.arguments = vec![next_ix..next_ix];
} else {
call.name.end = next_ix;
}
}
// The command name must begin with a letter.
else if c.is_alphabetic() {
call.name.end = next_ix;
} else {
return None;
}
}
// Commands start with a slash.
else if c == '/' {
call = Some(SlashCommandLine {
name: next_ix..next_ix,
arguments: Vec::new(),
});
}
// The line can't contain anything before the slash except for whitespace.
else if !c.is_whitespace() {
return None;
}
ix = next_ix;
}
call
}
}

View file

@ -1,7 +1,6 @@
use crate::{
humanize_token_count, AssistantPanel, AssistantPanelEvent, RequestType, DEFAULT_CONTEXT_LINES,
};
use crate::{humanize_token_count, AssistantPanel, AssistantPanelEvent, DEFAULT_CONTEXT_LINES};
use anyhow::{Context as _, Result};
use assistant_context_editor::RequestType;
use assistant_settings::AssistantSettings;
use client::telemetry::Telemetry;
use collections::{HashMap, VecDeque};

View file

@ -0,0 +1,58 @@
[package]
name = "assistant_context_editor"
version = "0.1.0"
edition.workspace = true
publish.workspace = true
license = "GPL-3.0-or-later"
[lints]
workspace = true
[lib]
path = "src/assistant_context_editor.rs"
[dependencies]
anyhow.workspace = true
assistant_slash_command.workspace = true
assistant_slash_commands.workspace = true
assistant_tool.workspace = true
chrono.workspace = true
client.workspace = true
clock.workspace = true
collections.workspace = true
context_server.workspace = true
editor.workspace = true
feature_flags.workspace = true
fs.workspace = true
futures.workspace = true
fuzzy.workspace = true
gpui.workspace = true
language.workspace = true
language_model.workspace = true
language_models.workspace = true
log.workspace = true
open_ai.workspace = true
paths.workspace = true
project.workspace = true
prompt_library.workspace = true
regex.workspace = true
rpc.workspace = true
serde.workspace = true
serde_json.workspace = true
smallvec.workspace = true
smol.workspace = true
strum.workspace = true
telemetry_events.workspace = true
text.workspace = true
ui.workspace = true
util.workspace = true
uuid.workspace = true
[dev-dependencies]
language_model = { workspace = true, features = ["test-support"] }
parking_lot.workspace = true
pretty_assertions.workspace = true
rand.workspace = true
settings.workspace = true
unindent.workspace = true
workspace = { workspace = true, features = ["test-support"] }

View file

@ -0,0 +1 @@
../../LICENSE-GPL

View file

@ -0,0 +1,16 @@
mod context;
mod context_store;
mod patch;
use std::sync::Arc;
use client::Client;
use gpui::AppContext;
pub use crate::context::*;
pub use crate::context_store::*;
pub use crate::patch::*;
pub fn init(client: Arc<Client>, _cx: &mut AppContext) {
context_store::init(&client.into());
}

View file

@ -1,14 +1,11 @@
#[cfg(test)]
mod context_tests;
use crate::{
slash_command::SlashCommandLine, AssistantEdit, AssistantPatch, AssistantPatchStatus,
MessageId, MessageStatus,
};
use crate::patch::{AssistantEdit, AssistantPatch, AssistantPatchStatus};
use anyhow::{anyhow, Context as _, Result};
use assistant_slash_command::{
SlashCommandContent, SlashCommandEvent, SlashCommandOutputSection, SlashCommandResult,
SlashCommandWorkingSet,
SlashCommandContent, SlashCommandEvent, SlashCommandLine, SlashCommandOutputSection,
SlashCommandResult, SlashCommandWorkingSet,
};
use assistant_slash_commands::FileCommandMetadata;
use assistant_tool::ToolWorkingSet;
@ -22,8 +19,6 @@ use gpui::{
AppContext, Context as _, EventEmitter, Model, ModelContext, RenderImage, SharedString,
Subscription, Task,
};
use prompt_library::PromptBuilder;
use language::{AnchorRangeExt, Bias, Buffer, LanguageRegistry, OffsetRangeExt, Point, ToOffset};
use language_model::{
LanguageModel, LanguageModelCacheConfiguration, LanguageModelCompletionEvent,
@ -38,6 +33,7 @@ use language_models::{
use open_ai::Model as OpenAiModel;
use paths::contexts_dir;
use project::Project;
use prompt_library::PromptBuilder;
use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
use std::{
@ -52,9 +48,9 @@ use std::{
};
use telemetry_events::{AssistantEvent, AssistantKind, AssistantPhase};
use text::{BufferSnapshot, ToPoint};
use ui::IconName;
use util::{post_inc, ResultExt, TryFutureExt};
use uuid::Uuid;
use workspace::ui::IconName;
#[derive(Clone, Eq, PartialEq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
pub struct ContextId(String);
@ -73,6 +69,64 @@ impl ContextId {
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct MessageId(pub clock::Lamport);
impl MessageId {
pub fn as_u64(self) -> u64 {
self.0.as_u64()
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum MessageStatus {
Pending,
Done,
Error(SharedString),
Canceled,
}
impl MessageStatus {
pub fn from_proto(status: proto::ContextMessageStatus) -> MessageStatus {
match status.variant {
Some(proto::context_message_status::Variant::Pending(_)) => MessageStatus::Pending,
Some(proto::context_message_status::Variant::Done(_)) => MessageStatus::Done,
Some(proto::context_message_status::Variant::Error(error)) => {
MessageStatus::Error(error.message.into())
}
Some(proto::context_message_status::Variant::Canceled(_)) => MessageStatus::Canceled,
None => MessageStatus::Pending,
}
}
pub fn to_proto(&self) -> proto::ContextMessageStatus {
match self {
MessageStatus::Pending => proto::ContextMessageStatus {
variant: Some(proto::context_message_status::Variant::Pending(
proto::context_message_status::Pending {},
)),
},
MessageStatus::Done => proto::ContextMessageStatus {
variant: Some(proto::context_message_status::Variant::Done(
proto::context_message_status::Done {},
)),
},
MessageStatus::Error(message) => proto::ContextMessageStatus {
variant: Some(proto::context_message_status::Variant::Error(
proto::context_message_status::Error {
message: message.to_string(),
},
)),
},
MessageStatus::Canceled => proto::ContextMessageStatus {
variant: Some(proto::context_message_status::Variant::Canceled(
proto::context_message_status::Canceled {},
)),
},
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RequestType {
/// Request a normal chat response from the model.
@ -423,7 +477,7 @@ pub struct MessageCacheMetadata {
pub struct MessageMetadata {
pub role: Role,
pub status: MessageStatus,
pub(crate) timestamp: clock::Lamport,
pub timestamp: clock::Lamport,
#[serde(skip)]
pub cache: Option<MessageCacheMetadata>,
}
@ -544,8 +598,8 @@ pub struct Context {
parsed_slash_commands: Vec<ParsedSlashCommand>,
invoked_slash_commands: HashMap<InvokedSlashCommandId, InvokedSlashCommand>,
edits_since_last_parse: language::Subscription,
pub(crate) slash_commands: Arc<SlashCommandWorkingSet>,
pub(crate) tools: Arc<ToolWorkingSet>,
slash_commands: Arc<SlashCommandWorkingSet>,
tools: Arc<ToolWorkingSet>,
slash_command_output_sections: Vec<SlashCommandOutputSection<language::Anchor>>,
pending_tool_uses_by_id: HashMap<LanguageModelToolUseId, PendingToolUse>,
message_anchors: Vec<MessageAnchor>,
@ -790,6 +844,14 @@ impl Context {
}
}
pub fn slash_commands(&self) -> &Arc<SlashCommandWorkingSet> {
&self.slash_commands
}
pub fn tools(&self) -> &Arc<ToolWorkingSet> {
&self.tools
}
pub fn set_capability(
&mut self,
capability: language::Capability,
@ -1048,11 +1110,7 @@ impl Context {
self.summary.as_ref()
}
pub(crate) fn patch_containing(
&self,
position: Point,
cx: &AppContext,
) -> Option<&AssistantPatch> {
pub fn patch_containing(&self, position: Point, cx: &AppContext) -> Option<&AssistantPatch> {
let buffer = self.buffer.read(cx);
let index = self.patches.binary_search_by(|patch| {
let patch_range = patch.range.to_point(&buffer);
@ -1075,7 +1133,7 @@ impl Context {
self.patches.iter().map(|patch| patch.range.clone())
}
pub(crate) fn patch_for_range(
pub fn patch_for_range(
&self,
range: &Range<language::Anchor>,
cx: &AppContext,
@ -1165,7 +1223,7 @@ impl Context {
}
}
pub(crate) fn token_count(&self) -> Option<usize> {
pub fn token_count(&self) -> Option<usize> {
self.token_count
}
@ -2879,7 +2937,7 @@ impl Context {
self.message_anchors.insert(insertion_ix, new_anchor);
}
pub(super) fn summarize(&mut self, replace_old: bool, cx: &mut ModelContext<Self>) {
pub fn summarize(&mut self, replace_old: bool, cx: &mut ModelContext<Self>) {
let Some(provider) = LanguageModelRegistry::read_global(cx).active_provider() else {
return;
};
@ -3118,7 +3176,7 @@ impl Context {
});
}
pub(crate) fn custom_summary(&mut self, custom_summary: String, cx: &mut ModelContext<Self>) {
pub fn custom_summary(&mut self, custom_summary: String, cx: &mut ModelContext<Self>) {
let timestamp = self.next_timestamp();
let summary = self.summary.get_or_insert(ContextSummary::default());
summary.timestamp = timestamp;

View file

@ -1,7 +1,6 @@
use super::{AssistantEdit, MessageCacheMetadata};
use crate::{
assistant_panel, AssistantEditKind, CacheStatus, Context, ContextEvent, ContextId,
ContextOperation, InvokedSlashCommandId, MessageId, MessageStatus,
AssistantEdit, AssistantEditKind, CacheStatus, Context, ContextEvent, ContextId,
ContextOperation, InvokedSlashCommandId, MessageCacheMetadata, MessageId, MessageStatus,
};
use anyhow::Result;
use assistant_slash_command::{
@ -48,7 +47,6 @@ fn test_inserting_and_removing_messages(cx: &mut AppContext) {
let settings_store = SettingsStore::test(cx);
LanguageModelRegistry::test(cx);
cx.set_global(settings_store);
assistant_panel::init(cx);
let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
let context = cx.new_model(|cx| {
@ -189,7 +187,6 @@ fn test_message_splitting(cx: &mut AppContext) {
let settings_store = SettingsStore::test(cx);
cx.set_global(settings_store);
LanguageModelRegistry::test(cx);
assistant_panel::init(cx);
let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
@ -294,7 +291,6 @@ fn test_messages_for_offsets(cx: &mut AppContext) {
let settings_store = SettingsStore::test(cx);
LanguageModelRegistry::test(cx);
cx.set_global(settings_store);
assistant_panel::init(cx);
let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
let context = cx.new_model(|cx| {
@ -390,7 +386,6 @@ async fn test_slash_commands(cx: &mut TestAppContext) {
cx.set_global(settings_store);
cx.update(LanguageModelRegistry::test);
cx.update(Project::init_settings);
cx.update(assistant_panel::init);
let fs = FakeFs::new(cx.background_executor.clone());
fs.insert_tree(
@ -698,7 +693,6 @@ async fn test_workflow_step_parsing(cx: &mut TestAppContext) {
let project = Project::test(fs, [Path::new("/root")], cx).await;
cx.update(LanguageModelRegistry::test);
cx.update(assistant_panel::init);
let registry = Arc::new(LanguageRegistry::test(cx.executor()));
// Create a new context
@ -1081,7 +1075,6 @@ async fn test_serialization(cx: &mut TestAppContext) {
let settings_store = cx.update(SettingsStore::test);
cx.set_global(settings_store);
cx.update(LanguageModelRegistry::test);
cx.update(assistant_panel::init);
let registry = Arc::new(LanguageRegistry::test(cx.executor()));
let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
let context = cx.new_model(|cx| {
@ -1173,7 +1166,6 @@ async fn test_random_context_collaboration(cx: &mut TestAppContext, mut rng: Std
cx.set_global(settings_store);
cx.update(LanguageModelRegistry::test);
cx.update(assistant_panel::init);
let slash_commands = cx.update(SlashCommandRegistry::default_global);
slash_commands.register_command(FakeSlashCommand("cmd-1".into()), false);
slash_commands.register_command(FakeSlashCommand("cmd-2".into()), false);
@ -1446,7 +1438,6 @@ fn test_mark_cache_anchors(cx: &mut AppContext) {
let settings_store = SettingsStore::test(cx);
LanguageModelRegistry::test(cx);
cx.set_global(settings_store);
assistant_panel::init(cx);
let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
let context = cx.new_model(|cx| {

View file

@ -33,7 +33,7 @@ use std::{
};
use util::{ResultExt, TryFutureExt};
pub fn init(client: &AnyProtoClient) {
pub(crate) fn init(client: &AnyProtoClient) {
client.add_model_message_handler(ContextStore::handle_advertise_contexts);
client.add_model_request_handler(ContextStore::handle_open_context);
client.add_model_request_handler(ContextStore::handle_create_context);
@ -497,11 +497,7 @@ impl ContextStore {
})
}
pub(super) fn loaded_context_for_id(
&self,
id: &ContextId,
cx: &AppContext,
) -> Option<Model<Context>> {
pub fn loaded_context_for_id(&self, id: &ContextId, cx: &AppContext) -> Option<Model<Context>> {
self.contexts.iter().find_map(|context| {
let context = context.upgrade()?;
if context.read(cx).id() == id {

View file

@ -9,7 +9,7 @@ use std::{cmp, ops::Range, path::Path, sync::Arc};
use text::{AnchorRangeExt as _, Bias, OffsetRangeExt as _, Point};
#[derive(Clone, Debug)]
pub(crate) struct AssistantPatch {
pub struct AssistantPatch {
pub range: Range<language::Anchor>,
pub title: SharedString,
pub edits: Arc<[Result<AssistantEdit>]>,
@ -17,13 +17,13 @@ pub(crate) struct AssistantPatch {
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) enum AssistantPatchStatus {
pub enum AssistantPatchStatus {
Pending,
Ready,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct AssistantEdit {
pub struct AssistantEdit {
pub path: String,
pub kind: AssistantEditKind,
}
@ -55,7 +55,7 @@ pub enum AssistantEditKind {
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct ResolvedPatch {
pub struct ResolvedPatch {
pub edit_groups: HashMap<Model<Buffer>, Vec<ResolvedEditGroup>>,
pub errors: Vec<AssistantPatchResolutionError>,
}
@ -74,7 +74,7 @@ pub struct ResolvedEdit {
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct AssistantPatchResolutionError {
pub struct AssistantPatchResolutionError {
pub edit_ix: usize,
pub message: String,
}
@ -425,7 +425,7 @@ impl AssistantEditKind {
}
impl AssistantPatch {
pub(crate) async fn resolve(
pub async fn resolve(
&self,
project: Model<Project>,
cx: &mut AsyncAppContext,

View file

@ -262,6 +262,67 @@ impl SlashCommandOutputSection<language::Anchor> {
}
}
pub struct SlashCommandLine {
/// The range within the line containing the command name.
pub name: Range<usize>,
/// Ranges within the line containing the command arguments.
pub arguments: Vec<Range<usize>>,
}
impl SlashCommandLine {
pub fn parse(line: &str) -> Option<Self> {
let mut call: Option<Self> = None;
let mut ix = 0;
for c in line.chars() {
let next_ix = ix + c.len_utf8();
if let Some(call) = &mut call {
// The command arguments start at the first non-whitespace character
// after the command name, and continue until the end of the line.
if let Some(argument) = call.arguments.last_mut() {
if c.is_whitespace() {
if (*argument).is_empty() {
argument.start = next_ix;
argument.end = next_ix;
} else {
argument.end = ix;
call.arguments.push(next_ix..next_ix);
}
} else {
argument.end = next_ix;
}
}
// The command name ends at the first whitespace character.
else if !call.name.is_empty() {
if c.is_whitespace() {
call.arguments = vec![next_ix..next_ix];
} else {
call.name.end = next_ix;
}
}
// The command name must begin with a letter.
else if c.is_alphabetic() {
call.name.end = next_ix;
} else {
return None;
}
}
// Commands start with a slash.
else if c == '/' {
call = Some(SlashCommandLine {
name: next_ix..next_ix,
arguments: Vec::new(),
});
}
// The line can't contain anything before the slash except for whitespace.
else if !c.is_whitespace() {
return None;
}
ix = next_ix;
}
call
}
}
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;

View file

@ -79,6 +79,7 @@ uuid.workspace = true
[dev-dependencies]
assistant = { workspace = true, features = ["test-support"] }
assistant_context_editor.workspace = true
assistant_slash_command.workspace = true
assistant_tool.workspace = true
async-trait.workspace = true

View file

@ -6,7 +6,7 @@ use crate::{
},
};
use anyhow::{anyhow, Result};
use assistant::ContextStore;
use assistant_context_editor::ContextStore;
use assistant_slash_command::SlashCommandWorkingSet;
use assistant_tool::ToolWorkingSet;
use call::{room, ActiveCall, ParticipantLocation, Room};

View file

@ -308,7 +308,7 @@ impl TestServer {
settings::KeymapFile::load_asset_allow_partial_failure(os_keymap, cx).unwrap(),
);
language_model::LanguageModelRegistry::test(cx);
assistant::context_store::init(&client.clone().into());
assistant_context_editor::init(client.clone(), cx);
});
client