context.rs

   1use crate::{
   2    prompts::PromptBuilder, slash_command::SlashCommandLine, AssistantPanel, InitialInsertion,
   3    InlineAssistId, InlineAssistant, MessageId, MessageStatus,
   4};
   5use anyhow::{anyhow, Context as _, Result};
   6use assistant_slash_command::{
   7    SlashCommandOutput, SlashCommandOutputSection, SlashCommandRegistry,
   8};
   9use client::{self, proto, telemetry::Telemetry};
  10use clock::ReplicaId;
  11use collections::{HashMap, HashSet};
  12use editor::Editor;
  13use fs::{Fs, RemoveOptions};
  14use futures::{
  15    future::{self, Shared},
  16    stream::FuturesUnordered,
  17    FutureExt, StreamExt,
  18};
  19use gpui::{
  20    AppContext, Context as _, EventEmitter, Image, Model, ModelContext, RenderImage, Subscription,
  21    Task, UpdateGlobal, View, WeakView,
  22};
  23
  24use language::{
  25    AnchorRangeExt, Bias, Buffer, BufferSnapshot, LanguageRegistry, OffsetRangeExt, ParseStatus,
  26    Point, ToOffset,
  27};
  28use language_model::{
  29    LanguageModelImage, LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage,
  30    LanguageModelTool, Role,
  31};
  32use open_ai::Model as OpenAiModel;
  33use paths::{context_images_dir, contexts_dir};
  34use project::Project;
  35use schemars::JsonSchema;
  36use serde::{Deserialize, Serialize};
  37use smallvec::SmallVec;
  38use std::{
  39    cmp::{self, Ordering},
  40    collections::hash_map,
  41    fmt::Debug,
  42    iter, mem,
  43    ops::Range,
  44    path::{Path, PathBuf},
  45    sync::Arc,
  46    time::{Duration, Instant},
  47};
  48use telemetry_events::AssistantKind;
  49use ui::{SharedString, WindowContext};
  50use util::{post_inc, ResultExt, TryFutureExt};
  51use uuid::Uuid;
  52use workspace::Workspace;
  53
  54#[derive(Clone, Eq, PartialEq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
  55pub struct ContextId(String);
  56
  57impl ContextId {
  58    pub fn new() -> Self {
  59        Self(Uuid::new_v4().to_string())
  60    }
  61
  62    pub fn from_proto(id: String) -> Self {
  63        Self(id)
  64    }
  65
  66    pub fn to_proto(&self) -> String {
  67        self.0.clone()
  68    }
  69}
  70
  71#[derive(Clone, Debug)]
  72pub enum ContextOperation {
  73    InsertMessage {
  74        anchor: MessageAnchor,
  75        metadata: MessageMetadata,
  76        version: clock::Global,
  77    },
  78    UpdateMessage {
  79        message_id: MessageId,
  80        metadata: MessageMetadata,
  81        version: clock::Global,
  82    },
  83    UpdateSummary {
  84        summary: ContextSummary,
  85        version: clock::Global,
  86    },
  87    SlashCommandFinished {
  88        id: SlashCommandId,
  89        output_range: Range<language::Anchor>,
  90        sections: Vec<SlashCommandOutputSection<language::Anchor>>,
  91        version: clock::Global,
  92    },
  93    BufferOperation(language::Operation),
  94}
  95
  96impl ContextOperation {
  97    pub fn from_proto(op: proto::ContextOperation) -> Result<Self> {
  98        match op.variant.context("invalid variant")? {
  99            proto::context_operation::Variant::InsertMessage(insert) => {
 100                let message = insert.message.context("invalid message")?;
 101                let id = MessageId(language::proto::deserialize_timestamp(
 102                    message.id.context("invalid id")?,
 103                ));
 104                Ok(Self::InsertMessage {
 105                    anchor: MessageAnchor {
 106                        id,
 107                        start: language::proto::deserialize_anchor(
 108                            message.start.context("invalid anchor")?,
 109                        )
 110                        .context("invalid anchor")?,
 111                    },
 112                    metadata: MessageMetadata {
 113                        role: Role::from_proto(message.role),
 114                        status: MessageStatus::from_proto(
 115                            message.status.context("invalid status")?,
 116                        ),
 117                        timestamp: id.0,
 118                    },
 119                    version: language::proto::deserialize_version(&insert.version),
 120                })
 121            }
 122            proto::context_operation::Variant::UpdateMessage(update) => Ok(Self::UpdateMessage {
 123                message_id: MessageId(language::proto::deserialize_timestamp(
 124                    update.message_id.context("invalid message id")?,
 125                )),
 126                metadata: MessageMetadata {
 127                    role: Role::from_proto(update.role),
 128                    status: MessageStatus::from_proto(update.status.context("invalid status")?),
 129                    timestamp: language::proto::deserialize_timestamp(
 130                        update.timestamp.context("invalid timestamp")?,
 131                    ),
 132                },
 133                version: language::proto::deserialize_version(&update.version),
 134            }),
 135            proto::context_operation::Variant::UpdateSummary(update) => Ok(Self::UpdateSummary {
 136                summary: ContextSummary {
 137                    text: update.summary,
 138                    done: update.done,
 139                    timestamp: language::proto::deserialize_timestamp(
 140                        update.timestamp.context("invalid timestamp")?,
 141                    ),
 142                },
 143                version: language::proto::deserialize_version(&update.version),
 144            }),
 145            proto::context_operation::Variant::SlashCommandFinished(finished) => {
 146                Ok(Self::SlashCommandFinished {
 147                    id: SlashCommandId(language::proto::deserialize_timestamp(
 148                        finished.id.context("invalid id")?,
 149                    )),
 150                    output_range: language::proto::deserialize_anchor_range(
 151                        finished.output_range.context("invalid range")?,
 152                    )?,
 153                    sections: finished
 154                        .sections
 155                        .into_iter()
 156                        .map(|section| {
 157                            Ok(SlashCommandOutputSection {
 158                                range: language::proto::deserialize_anchor_range(
 159                                    section.range.context("invalid range")?,
 160                                )?,
 161                                icon: section.icon_name.parse()?,
 162                                label: section.label.into(),
 163                            })
 164                        })
 165                        .collect::<Result<Vec<_>>>()?,
 166                    version: language::proto::deserialize_version(&finished.version),
 167                })
 168            }
 169            proto::context_operation::Variant::BufferOperation(op) => Ok(Self::BufferOperation(
 170                language::proto::deserialize_operation(
 171                    op.operation.context("invalid buffer operation")?,
 172                )?,
 173            )),
 174        }
 175    }
 176
 177    pub fn to_proto(&self) -> proto::ContextOperation {
 178        match self {
 179            Self::InsertMessage {
 180                anchor,
 181                metadata,
 182                version,
 183            } => proto::ContextOperation {
 184                variant: Some(proto::context_operation::Variant::InsertMessage(
 185                    proto::context_operation::InsertMessage {
 186                        message: Some(proto::ContextMessage {
 187                            id: Some(language::proto::serialize_timestamp(anchor.id.0)),
 188                            start: Some(language::proto::serialize_anchor(&anchor.start)),
 189                            role: metadata.role.to_proto() as i32,
 190                            status: Some(metadata.status.to_proto()),
 191                        }),
 192                        version: language::proto::serialize_version(version),
 193                    },
 194                )),
 195            },
 196            Self::UpdateMessage {
 197                message_id,
 198                metadata,
 199                version,
 200            } => proto::ContextOperation {
 201                variant: Some(proto::context_operation::Variant::UpdateMessage(
 202                    proto::context_operation::UpdateMessage {
 203                        message_id: Some(language::proto::serialize_timestamp(message_id.0)),
 204                        role: metadata.role.to_proto() as i32,
 205                        status: Some(metadata.status.to_proto()),
 206                        timestamp: Some(language::proto::serialize_timestamp(metadata.timestamp)),
 207                        version: language::proto::serialize_version(version),
 208                    },
 209                )),
 210            },
 211            Self::UpdateSummary { summary, version } => proto::ContextOperation {
 212                variant: Some(proto::context_operation::Variant::UpdateSummary(
 213                    proto::context_operation::UpdateSummary {
 214                        summary: summary.text.clone(),
 215                        done: summary.done,
 216                        timestamp: Some(language::proto::serialize_timestamp(summary.timestamp)),
 217                        version: language::proto::serialize_version(version),
 218                    },
 219                )),
 220            },
 221            Self::SlashCommandFinished {
 222                id,
 223                output_range,
 224                sections,
 225                version,
 226            } => proto::ContextOperation {
 227                variant: Some(proto::context_operation::Variant::SlashCommandFinished(
 228                    proto::context_operation::SlashCommandFinished {
 229                        id: Some(language::proto::serialize_timestamp(id.0)),
 230                        output_range: Some(language::proto::serialize_anchor_range(
 231                            output_range.clone(),
 232                        )),
 233                        sections: sections
 234                            .iter()
 235                            .map(|section| {
 236                                let icon_name: &'static str = section.icon.into();
 237                                proto::SlashCommandOutputSection {
 238                                    range: Some(language::proto::serialize_anchor_range(
 239                                        section.range.clone(),
 240                                    )),
 241                                    icon_name: icon_name.to_string(),
 242                                    label: section.label.to_string(),
 243                                }
 244                            })
 245                            .collect(),
 246                        version: language::proto::serialize_version(version),
 247                    },
 248                )),
 249            },
 250            Self::BufferOperation(operation) => proto::ContextOperation {
 251                variant: Some(proto::context_operation::Variant::BufferOperation(
 252                    proto::context_operation::BufferOperation {
 253                        operation: Some(language::proto::serialize_operation(operation)),
 254                    },
 255                )),
 256            },
 257        }
 258    }
 259
 260    fn timestamp(&self) -> clock::Lamport {
 261        match self {
 262            Self::InsertMessage { anchor, .. } => anchor.id.0,
 263            Self::UpdateMessage { metadata, .. } => metadata.timestamp,
 264            Self::UpdateSummary { summary, .. } => summary.timestamp,
 265            Self::SlashCommandFinished { id, .. } => id.0,
 266            Self::BufferOperation(_) => {
 267                panic!("reading the timestamp of a buffer operation is not supported")
 268            }
 269        }
 270    }
 271
 272    /// Returns the current version of the context operation.
 273    pub fn version(&self) -> &clock::Global {
 274        match self {
 275            Self::InsertMessage { version, .. }
 276            | Self::UpdateMessage { version, .. }
 277            | Self::UpdateSummary { version, .. }
 278            | Self::SlashCommandFinished { version, .. } => version,
 279            Self::BufferOperation(_) => {
 280                panic!("reading the version of a buffer operation is not supported")
 281            }
 282        }
 283    }
 284}
 285
 286#[derive(Debug, Clone)]
 287pub enum ContextEvent {
 288    AssistError(String),
 289    MessagesEdited,
 290    SummaryChanged,
 291    WorkflowStepsRemoved(Vec<Range<language::Anchor>>),
 292    WorkflowStepUpdated(Range<language::Anchor>),
 293    StreamedCompletion,
 294    PendingSlashCommandsUpdated {
 295        removed: Vec<Range<language::Anchor>>,
 296        updated: Vec<PendingSlashCommand>,
 297    },
 298    SlashCommandFinished {
 299        output_range: Range<language::Anchor>,
 300        sections: Vec<SlashCommandOutputSection<language::Anchor>>,
 301        run_commands_in_output: bool,
 302    },
 303    Operation(ContextOperation),
 304}
 305
 306#[derive(Clone, Default, Debug)]
 307pub struct ContextSummary {
 308    pub text: String,
 309    done: bool,
 310    timestamp: clock::Lamport,
 311}
 312
 313#[derive(Clone, Debug, Eq, PartialEq)]
 314pub struct MessageAnchor {
 315    pub id: MessageId,
 316    pub start: language::Anchor,
 317}
 318
 319#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
 320pub struct MessageMetadata {
 321    pub role: Role,
 322    status: MessageStatus,
 323    timestamp: clock::Lamport,
 324}
 325
 326#[derive(Clone, Debug)]
 327pub struct MessageImage {
 328    image_id: u64,
 329    image: Shared<Task<Option<LanguageModelImage>>>,
 330}
 331
 332impl PartialEq for MessageImage {
 333    fn eq(&self, other: &Self) -> bool {
 334        self.image_id == other.image_id
 335    }
 336}
 337
 338impl Eq for MessageImage {}
 339
 340#[derive(Clone, Debug)]
 341pub struct Message {
 342    pub image_offsets: SmallVec<[(usize, MessageImage); 1]>,
 343    pub offset_range: Range<usize>,
 344    pub index_range: Range<usize>,
 345    pub id: MessageId,
 346    pub anchor: language::Anchor,
 347    pub role: Role,
 348    pub status: MessageStatus,
 349}
 350
 351impl Message {
 352    fn to_request_message(&self, buffer: &Buffer) -> LanguageModelRequestMessage {
 353        let mut content = Vec::new();
 354
 355        let mut range_start = self.offset_range.start;
 356        for (image_offset, message_image) in self.image_offsets.iter() {
 357            if *image_offset != range_start {
 358                content.push(
 359                    buffer
 360                        .text_for_range(range_start..*image_offset)
 361                        .collect::<String>()
 362                        .into(),
 363                )
 364            }
 365
 366            if let Some(image) = message_image.image.clone().now_or_never().flatten() {
 367                content.push(language_model::MessageContent::Image(image));
 368            }
 369
 370            range_start = *image_offset;
 371        }
 372        if range_start != self.offset_range.end {
 373            content.push(
 374                buffer
 375                    .text_for_range(range_start..self.offset_range.end)
 376                    .collect::<String>()
 377                    .into(),
 378            )
 379        }
 380
 381        LanguageModelRequestMessage {
 382            role: self.role,
 383            content,
 384        }
 385    }
 386}
 387
 388#[derive(Clone, Debug)]
 389pub struct ImageAnchor {
 390    pub anchor: language::Anchor,
 391    pub image_id: u64,
 392    pub render_image: Arc<RenderImage>,
 393    pub image: Shared<Task<Option<LanguageModelImage>>>,
 394}
 395
 396impl PartialEq for ImageAnchor {
 397    fn eq(&self, other: &Self) -> bool {
 398        self.image_id == other.image_id
 399    }
 400}
 401
 402struct PendingCompletion {
 403    id: usize,
 404    assistant_message_id: MessageId,
 405    _task: Task<()>,
 406}
 407
 408#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
 409pub struct SlashCommandId(clock::Lamport);
 410
 411#[derive(Debug)]
 412pub struct WorkflowStep {
 413    pub tagged_range: Range<language::Anchor>,
 414    pub status: WorkflowStepStatus,
 415}
 416
 417#[derive(Clone, Debug, Eq, PartialEq)]
 418pub struct ResolvedWorkflowStep {
 419    pub title: String,
 420    pub suggestions: HashMap<Model<Buffer>, Vec<WorkflowSuggestionGroup>>,
 421}
 422
 423pub enum WorkflowStepStatus {
 424    Pending(Task<Option<()>>),
 425    Resolved(ResolvedWorkflowStep),
 426    Error(Arc<anyhow::Error>),
 427}
 428
 429impl WorkflowStepStatus {
 430    pub fn into_resolved(&self) -> Option<Result<ResolvedWorkflowStep, Arc<anyhow::Error>>> {
 431        match self {
 432            WorkflowStepStatus::Resolved(resolved) => Some(Ok(resolved.clone())),
 433            WorkflowStepStatus::Error(error) => Some(Err(error.clone())),
 434            WorkflowStepStatus::Pending(_) => None,
 435        }
 436    }
 437}
 438
 439#[derive(Clone, Debug, Eq, PartialEq)]
 440pub struct WorkflowSuggestionGroup {
 441    pub context_range: Range<language::Anchor>,
 442    pub suggestions: Vec<WorkflowSuggestion>,
 443}
 444
 445#[derive(Clone, Debug, Eq, PartialEq)]
 446pub enum WorkflowSuggestion {
 447    Update {
 448        range: Range<language::Anchor>,
 449        description: String,
 450    },
 451    CreateFile {
 452        description: String,
 453    },
 454    InsertSiblingBefore {
 455        position: language::Anchor,
 456        description: String,
 457    },
 458    InsertSiblingAfter {
 459        position: language::Anchor,
 460        description: String,
 461    },
 462    PrependChild {
 463        position: language::Anchor,
 464        description: String,
 465    },
 466    AppendChild {
 467        position: language::Anchor,
 468        description: String,
 469    },
 470    Delete {
 471        range: Range<language::Anchor>,
 472    },
 473}
 474
 475impl WorkflowSuggestion {
 476    pub fn range(&self) -> Range<language::Anchor> {
 477        match self {
 478            WorkflowSuggestion::Update { range, .. } => range.clone(),
 479            WorkflowSuggestion::CreateFile { .. } => language::Anchor::MIN..language::Anchor::MAX,
 480            WorkflowSuggestion::InsertSiblingBefore { position, .. }
 481            | WorkflowSuggestion::InsertSiblingAfter { position, .. }
 482            | WorkflowSuggestion::PrependChild { position, .. }
 483            | WorkflowSuggestion::AppendChild { position, .. } => *position..*position,
 484            WorkflowSuggestion::Delete { range } => range.clone(),
 485        }
 486    }
 487
 488    pub fn description(&self) -> Option<&str> {
 489        match self {
 490            WorkflowSuggestion::Update { description, .. }
 491            | WorkflowSuggestion::CreateFile { description }
 492            | WorkflowSuggestion::InsertSiblingBefore { description, .. }
 493            | WorkflowSuggestion::InsertSiblingAfter { description, .. }
 494            | WorkflowSuggestion::PrependChild { description, .. }
 495            | WorkflowSuggestion::AppendChild { description, .. } => Some(description),
 496            WorkflowSuggestion::Delete { .. } => None,
 497        }
 498    }
 499
 500    fn description_mut(&mut self) -> Option<&mut String> {
 501        match self {
 502            WorkflowSuggestion::Update { description, .. }
 503            | WorkflowSuggestion::CreateFile { description }
 504            | WorkflowSuggestion::InsertSiblingBefore { description, .. }
 505            | WorkflowSuggestion::InsertSiblingAfter { description, .. }
 506            | WorkflowSuggestion::PrependChild { description, .. }
 507            | WorkflowSuggestion::AppendChild { description, .. } => Some(description),
 508            WorkflowSuggestion::Delete { .. } => None,
 509        }
 510    }
 511
 512    fn try_merge(&mut self, other: &Self, buffer: &BufferSnapshot) -> bool {
 513        let range = self.range();
 514        let other_range = other.range();
 515
 516        // Don't merge if we don't contain the other suggestion.
 517        if range.start.cmp(&other_range.start, buffer).is_gt()
 518            || range.end.cmp(&other_range.end, buffer).is_lt()
 519        {
 520            return false;
 521        }
 522
 523        if let Some(description) = self.description_mut() {
 524            if let Some(other_description) = other.description() {
 525                description.push('\n');
 526                description.push_str(other_description);
 527            }
 528        }
 529        true
 530    }
 531
 532    pub fn show(
 533        &self,
 534        editor: &View<Editor>,
 535        excerpt_id: editor::ExcerptId,
 536        workspace: &WeakView<Workspace>,
 537        assistant_panel: &View<AssistantPanel>,
 538        cx: &mut WindowContext,
 539    ) -> Option<InlineAssistId> {
 540        let mut initial_transaction_id = None;
 541        let initial_prompt;
 542        let suggestion_range;
 543        let buffer = editor.read(cx).buffer().clone();
 544        let snapshot = buffer.read(cx).snapshot(cx);
 545
 546        match self {
 547            WorkflowSuggestion::Update { range, description } => {
 548                initial_prompt = description.clone();
 549                suggestion_range = snapshot.anchor_in_excerpt(excerpt_id, range.start)?
 550                    ..snapshot.anchor_in_excerpt(excerpt_id, range.end)?;
 551            }
 552            WorkflowSuggestion::CreateFile { description } => {
 553                initial_prompt = description.clone();
 554                suggestion_range = editor::Anchor::min()..editor::Anchor::min();
 555            }
 556            WorkflowSuggestion::InsertSiblingBefore {
 557                position,
 558                description,
 559            } => {
 560                let position = snapshot.anchor_in_excerpt(excerpt_id, *position)?;
 561                initial_prompt = description.clone();
 562                suggestion_range = buffer.update(cx, |buffer, cx| {
 563                    buffer.start_transaction(cx);
 564                    let line_start = buffer.insert_empty_line(position, true, true, cx);
 565                    initial_transaction_id = buffer.end_transaction(cx);
 566                    buffer.refresh_preview(cx);
 567
 568                    let line_start = buffer.read(cx).anchor_before(line_start);
 569                    line_start..line_start
 570                });
 571            }
 572            WorkflowSuggestion::InsertSiblingAfter {
 573                position,
 574                description,
 575            } => {
 576                let position = snapshot.anchor_in_excerpt(excerpt_id, *position)?;
 577                initial_prompt = description.clone();
 578                suggestion_range = buffer.update(cx, |buffer, cx| {
 579                    buffer.start_transaction(cx);
 580                    let line_start = buffer.insert_empty_line(position, true, true, cx);
 581                    initial_transaction_id = buffer.end_transaction(cx);
 582                    buffer.refresh_preview(cx);
 583
 584                    let line_start = buffer.read(cx).anchor_before(line_start);
 585                    line_start..line_start
 586                });
 587            }
 588            WorkflowSuggestion::PrependChild {
 589                position,
 590                description,
 591            } => {
 592                let position = snapshot.anchor_in_excerpt(excerpt_id, *position)?;
 593                initial_prompt = description.clone();
 594                suggestion_range = buffer.update(cx, |buffer, cx| {
 595                    buffer.start_transaction(cx);
 596                    let line_start = buffer.insert_empty_line(position, false, true, cx);
 597                    initial_transaction_id = buffer.end_transaction(cx);
 598                    buffer.refresh_preview(cx);
 599
 600                    let line_start = buffer.read(cx).anchor_before(line_start);
 601                    line_start..line_start
 602                });
 603            }
 604            WorkflowSuggestion::AppendChild {
 605                position,
 606                description,
 607            } => {
 608                let position = snapshot.anchor_in_excerpt(excerpt_id, *position)?;
 609                initial_prompt = description.clone();
 610                suggestion_range = buffer.update(cx, |buffer, cx| {
 611                    buffer.start_transaction(cx);
 612                    let line_start = buffer.insert_empty_line(position, true, false, cx);
 613                    initial_transaction_id = buffer.end_transaction(cx);
 614                    buffer.refresh_preview(cx);
 615
 616                    let line_start = buffer.read(cx).anchor_before(line_start);
 617                    line_start..line_start
 618                });
 619            }
 620            WorkflowSuggestion::Delete { range } => {
 621                initial_prompt = "Delete".to_string();
 622                suggestion_range = snapshot.anchor_in_excerpt(excerpt_id, range.start)?
 623                    ..snapshot.anchor_in_excerpt(excerpt_id, range.end)?;
 624            }
 625        }
 626
 627        InlineAssistant::update_global(cx, |inline_assistant, cx| {
 628            Some(inline_assistant.suggest_assist(
 629                editor,
 630                suggestion_range,
 631                initial_prompt,
 632                initial_transaction_id,
 633                Some(workspace.clone()),
 634                Some(assistant_panel),
 635                cx,
 636            ))
 637        })
 638    }
 639}
 640
 641impl Debug for WorkflowStepStatus {
 642    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 643        match self {
 644            WorkflowStepStatus::Pending(_) => write!(f, "WorkflowStepStatus::Pending"),
 645            WorkflowStepStatus::Resolved(ResolvedWorkflowStep { title, suggestions }) => f
 646                .debug_struct("WorkflowStepStatus::Resolved")
 647                .field("title", title)
 648                .field("suggestions", suggestions)
 649                .finish(),
 650            WorkflowStepStatus::Error(error) => f
 651                .debug_tuple("WorkflowStepStatus::Error")
 652                .field(error)
 653                .finish(),
 654        }
 655    }
 656}
 657
 658pub struct Context {
 659    id: ContextId,
 660    timestamp: clock::Lamport,
 661    version: clock::Global,
 662    pending_ops: Vec<ContextOperation>,
 663    operations: Vec<ContextOperation>,
 664    buffer: Model<Buffer>,
 665    pending_slash_commands: Vec<PendingSlashCommand>,
 666    edits_since_last_slash_command_parse: language::Subscription,
 667    finished_slash_commands: HashSet<SlashCommandId>,
 668    slash_command_output_sections: Vec<SlashCommandOutputSection<language::Anchor>>,
 669    message_anchors: Vec<MessageAnchor>,
 670    images: HashMap<u64, (Arc<RenderImage>, Shared<Task<Option<LanguageModelImage>>>)>,
 671    image_anchors: Vec<ImageAnchor>,
 672    messages_metadata: HashMap<MessageId, MessageMetadata>,
 673    summary: Option<ContextSummary>,
 674    pending_summary: Task<Option<()>>,
 675    completion_count: usize,
 676    pending_completions: Vec<PendingCompletion>,
 677    token_count: Option<usize>,
 678    pending_token_count: Task<Option<()>>,
 679    pending_save: Task<Result<()>>,
 680    path: Option<PathBuf>,
 681    _subscriptions: Vec<Subscription>,
 682    telemetry: Option<Arc<Telemetry>>,
 683    language_registry: Arc<LanguageRegistry>,
 684    workflow_steps: Vec<WorkflowStep>,
 685    edits_since_last_workflow_step_prune: language::Subscription,
 686    project: Option<Model<Project>>,
 687    prompt_builder: Arc<PromptBuilder>,
 688}
 689
 690impl EventEmitter<ContextEvent> for Context {}
 691
 692impl Context {
 693    pub fn local(
 694        language_registry: Arc<LanguageRegistry>,
 695        project: Option<Model<Project>>,
 696        telemetry: Option<Arc<Telemetry>>,
 697        prompt_builder: Arc<PromptBuilder>,
 698        cx: &mut ModelContext<Self>,
 699    ) -> Self {
 700        Self::new(
 701            ContextId::new(),
 702            ReplicaId::default(),
 703            language::Capability::ReadWrite,
 704            language_registry,
 705            prompt_builder,
 706            project,
 707            telemetry,
 708            cx,
 709        )
 710    }
 711
 712    #[allow(clippy::too_many_arguments)]
 713    pub fn new(
 714        id: ContextId,
 715        replica_id: ReplicaId,
 716        capability: language::Capability,
 717        language_registry: Arc<LanguageRegistry>,
 718        prompt_builder: Arc<PromptBuilder>,
 719        project: Option<Model<Project>>,
 720        telemetry: Option<Arc<Telemetry>>,
 721        cx: &mut ModelContext<Self>,
 722    ) -> Self {
 723        let buffer = cx.new_model(|_cx| {
 724            let mut buffer = Buffer::remote(
 725                language::BufferId::new(1).unwrap(),
 726                replica_id,
 727                capability,
 728                "",
 729            );
 730            buffer.set_language_registry(language_registry.clone());
 731            buffer
 732        });
 733        let edits_since_last_slash_command_parse =
 734            buffer.update(cx, |buffer, _| buffer.subscribe());
 735        let edits_since_last_workflow_step_prune =
 736            buffer.update(cx, |buffer, _| buffer.subscribe());
 737        let mut this = Self {
 738            id,
 739            timestamp: clock::Lamport::new(replica_id),
 740            version: clock::Global::new(),
 741            pending_ops: Vec::new(),
 742            operations: Vec::new(),
 743            message_anchors: Default::default(),
 744            image_anchors: Default::default(),
 745            images: Default::default(),
 746            messages_metadata: Default::default(),
 747            pending_slash_commands: Vec::new(),
 748            finished_slash_commands: HashSet::default(),
 749            slash_command_output_sections: Vec::new(),
 750            edits_since_last_slash_command_parse,
 751            summary: None,
 752            pending_summary: Task::ready(None),
 753            completion_count: Default::default(),
 754            pending_completions: Default::default(),
 755            token_count: None,
 756            pending_token_count: Task::ready(None),
 757            _subscriptions: vec![cx.subscribe(&buffer, Self::handle_buffer_event)],
 758            pending_save: Task::ready(Ok(())),
 759            path: None,
 760            buffer,
 761            telemetry,
 762            project,
 763            language_registry,
 764            workflow_steps: Vec::new(),
 765            edits_since_last_workflow_step_prune,
 766            prompt_builder,
 767        };
 768
 769        let first_message_id = MessageId(clock::Lamport {
 770            replica_id: 0,
 771            value: 0,
 772        });
 773        let message = MessageAnchor {
 774            id: first_message_id,
 775            start: language::Anchor::MIN,
 776        };
 777        this.messages_metadata.insert(
 778            first_message_id,
 779            MessageMetadata {
 780                role: Role::User,
 781                status: MessageStatus::Done,
 782                timestamp: first_message_id.0,
 783            },
 784        );
 785        this.message_anchors.push(message);
 786
 787        this.set_language(cx);
 788        this.count_remaining_tokens(cx);
 789        this
 790    }
 791
 792    fn serialize(&self, cx: &AppContext) -> SavedContext {
 793        let buffer = self.buffer.read(cx);
 794        SavedContext {
 795            id: Some(self.id.clone()),
 796            zed: "context".into(),
 797            version: SavedContext::VERSION.into(),
 798            text: buffer.text(),
 799            messages: self
 800                .messages(cx)
 801                .map(|message| SavedMessage {
 802                    id: message.id,
 803                    start: message.offset_range.start,
 804                    metadata: self.messages_metadata[&message.id].clone(),
 805                    image_offsets: message
 806                        .image_offsets
 807                        .iter()
 808                        .map(|image_offset| (image_offset.0, image_offset.1.image_id))
 809                        .collect(),
 810                })
 811                .collect(),
 812            summary: self
 813                .summary
 814                .as_ref()
 815                .map(|summary| summary.text.clone())
 816                .unwrap_or_default(),
 817            slash_command_output_sections: self
 818                .slash_command_output_sections
 819                .iter()
 820                .filter_map(|section| {
 821                    let range = section.range.to_offset(buffer);
 822                    if section.range.start.is_valid(buffer) && !range.is_empty() {
 823                        Some(assistant_slash_command::SlashCommandOutputSection {
 824                            range,
 825                            icon: section.icon,
 826                            label: section.label.clone(),
 827                        })
 828                    } else {
 829                        None
 830                    }
 831                })
 832                .collect(),
 833        }
 834    }
 835
 836    #[allow(clippy::too_many_arguments)]
 837    pub fn deserialize(
 838        saved_context: SavedContext,
 839        path: PathBuf,
 840        language_registry: Arc<LanguageRegistry>,
 841        prompt_builder: Arc<PromptBuilder>,
 842        project: Option<Model<Project>>,
 843        telemetry: Option<Arc<Telemetry>>,
 844        cx: &mut ModelContext<Self>,
 845    ) -> Self {
 846        let id = saved_context.id.clone().unwrap_or_else(|| ContextId::new());
 847        let mut this = Self::new(
 848            id,
 849            ReplicaId::default(),
 850            language::Capability::ReadWrite,
 851            language_registry,
 852            prompt_builder,
 853            project,
 854            telemetry,
 855            cx,
 856        );
 857        this.path = Some(path);
 858        this.buffer.update(cx, |buffer, cx| {
 859            buffer.set_text(saved_context.text.as_str(), cx)
 860        });
 861        let operations = saved_context.into_ops(&this.buffer, cx);
 862        this.apply_ops(operations, cx).unwrap();
 863        this
 864    }
 865
 866    pub fn id(&self) -> &ContextId {
 867        &self.id
 868    }
 869
 870    pub fn replica_id(&self) -> ReplicaId {
 871        self.timestamp.replica_id
 872    }
 873
 874    pub fn version(&self, cx: &AppContext) -> ContextVersion {
 875        ContextVersion {
 876            context: self.version.clone(),
 877            buffer: self.buffer.read(cx).version(),
 878        }
 879    }
 880
 881    pub fn set_capability(
 882        &mut self,
 883        capability: language::Capability,
 884        cx: &mut ModelContext<Self>,
 885    ) {
 886        self.buffer
 887            .update(cx, |buffer, cx| buffer.set_capability(capability, cx));
 888    }
 889
 890    fn next_timestamp(&mut self) -> clock::Lamport {
 891        let timestamp = self.timestamp.tick();
 892        self.version.observe(timestamp);
 893        timestamp
 894    }
 895
 896    pub fn serialize_ops(
 897        &self,
 898        since: &ContextVersion,
 899        cx: &AppContext,
 900    ) -> Task<Vec<proto::ContextOperation>> {
 901        let buffer_ops = self
 902            .buffer
 903            .read(cx)
 904            .serialize_ops(Some(since.buffer.clone()), cx);
 905
 906        let mut context_ops = self
 907            .operations
 908            .iter()
 909            .filter(|op| !since.context.observed(op.timestamp()))
 910            .cloned()
 911            .collect::<Vec<_>>();
 912        context_ops.extend(self.pending_ops.iter().cloned());
 913
 914        cx.background_executor().spawn(async move {
 915            let buffer_ops = buffer_ops.await;
 916            context_ops.sort_unstable_by_key(|op| op.timestamp());
 917            buffer_ops
 918                .into_iter()
 919                .map(|op| proto::ContextOperation {
 920                    variant: Some(proto::context_operation::Variant::BufferOperation(
 921                        proto::context_operation::BufferOperation {
 922                            operation: Some(op),
 923                        },
 924                    )),
 925                })
 926                .chain(context_ops.into_iter().map(|op| op.to_proto()))
 927                .collect()
 928        })
 929    }
 930
 931    pub fn apply_ops(
 932        &mut self,
 933        ops: impl IntoIterator<Item = ContextOperation>,
 934        cx: &mut ModelContext<Self>,
 935    ) -> Result<()> {
 936        let mut buffer_ops = Vec::new();
 937        for op in ops {
 938            match op {
 939                ContextOperation::BufferOperation(buffer_op) => buffer_ops.push(buffer_op),
 940                op @ _ => self.pending_ops.push(op),
 941            }
 942        }
 943        self.buffer
 944            .update(cx, |buffer, cx| buffer.apply_ops(buffer_ops, cx))?;
 945        self.flush_ops(cx);
 946
 947        Ok(())
 948    }
 949
 950    fn flush_ops(&mut self, cx: &mut ModelContext<Context>) {
 951        let mut messages_changed = false;
 952        let mut summary_changed = false;
 953
 954        self.pending_ops.sort_unstable_by_key(|op| op.timestamp());
 955        for op in mem::take(&mut self.pending_ops) {
 956            if !self.can_apply_op(&op, cx) {
 957                self.pending_ops.push(op);
 958                continue;
 959            }
 960
 961            let timestamp = op.timestamp();
 962            match op.clone() {
 963                ContextOperation::InsertMessage {
 964                    anchor, metadata, ..
 965                } => {
 966                    if self.messages_metadata.contains_key(&anchor.id) {
 967                        // We already applied this operation.
 968                    } else {
 969                        self.insert_message(anchor, metadata, cx);
 970                        messages_changed = true;
 971                    }
 972                }
 973                ContextOperation::UpdateMessage {
 974                    message_id,
 975                    metadata: new_metadata,
 976                    ..
 977                } => {
 978                    let metadata = self.messages_metadata.get_mut(&message_id).unwrap();
 979                    if new_metadata.timestamp > metadata.timestamp {
 980                        *metadata = new_metadata;
 981                        messages_changed = true;
 982                    }
 983                }
 984                ContextOperation::UpdateSummary {
 985                    summary: new_summary,
 986                    ..
 987                } => {
 988                    if self
 989                        .summary
 990                        .as_ref()
 991                        .map_or(true, |summary| new_summary.timestamp > summary.timestamp)
 992                    {
 993                        self.summary = Some(new_summary);
 994                        summary_changed = true;
 995                    }
 996                }
 997                ContextOperation::SlashCommandFinished {
 998                    id,
 999                    output_range,
1000                    sections,
1001                    ..
1002                } => {
1003                    if self.finished_slash_commands.insert(id) {
1004                        let buffer = self.buffer.read(cx);
1005                        self.slash_command_output_sections
1006                            .extend(sections.iter().cloned());
1007                        self.slash_command_output_sections
1008                            .sort_by(|a, b| a.range.cmp(&b.range, buffer));
1009                        cx.emit(ContextEvent::SlashCommandFinished {
1010                            output_range,
1011                            sections,
1012                            run_commands_in_output: false,
1013                        });
1014                    }
1015                }
1016                ContextOperation::BufferOperation(_) => unreachable!(),
1017            }
1018
1019            self.version.observe(timestamp);
1020            self.timestamp.observe(timestamp);
1021            self.operations.push(op);
1022        }
1023
1024        if messages_changed {
1025            cx.emit(ContextEvent::MessagesEdited);
1026            cx.notify();
1027        }
1028
1029        if summary_changed {
1030            cx.emit(ContextEvent::SummaryChanged);
1031            cx.notify();
1032        }
1033    }
1034
1035    fn can_apply_op(&self, op: &ContextOperation, cx: &AppContext) -> bool {
1036        if !self.version.observed_all(op.version()) {
1037            return false;
1038        }
1039
1040        match op {
1041            ContextOperation::InsertMessage { anchor, .. } => self
1042                .buffer
1043                .read(cx)
1044                .version
1045                .observed(anchor.start.timestamp),
1046            ContextOperation::UpdateMessage { message_id, .. } => {
1047                self.messages_metadata.contains_key(message_id)
1048            }
1049            ContextOperation::UpdateSummary { .. } => true,
1050            ContextOperation::SlashCommandFinished {
1051                output_range,
1052                sections,
1053                ..
1054            } => {
1055                let version = &self.buffer.read(cx).version;
1056                sections
1057                    .iter()
1058                    .map(|section| &section.range)
1059                    .chain([output_range])
1060                    .all(|range| {
1061                        let observed_start = range.start == language::Anchor::MIN
1062                            || range.start == language::Anchor::MAX
1063                            || version.observed(range.start.timestamp);
1064                        let observed_end = range.end == language::Anchor::MIN
1065                            || range.end == language::Anchor::MAX
1066                            || version.observed(range.end.timestamp);
1067                        observed_start && observed_end
1068                    })
1069            }
1070            ContextOperation::BufferOperation(_) => {
1071                panic!("buffer operations should always be applied")
1072            }
1073        }
1074    }
1075
1076    fn push_op(&mut self, op: ContextOperation, cx: &mut ModelContext<Self>) {
1077        self.operations.push(op.clone());
1078        cx.emit(ContextEvent::Operation(op));
1079    }
1080
1081    pub fn buffer(&self) -> &Model<Buffer> {
1082        &self.buffer
1083    }
1084
1085    pub fn path(&self) -> Option<&Path> {
1086        self.path.as_deref()
1087    }
1088
1089    pub fn summary(&self) -> Option<&ContextSummary> {
1090        self.summary.as_ref()
1091    }
1092
1093    pub fn workflow_steps(&self) -> &[WorkflowStep] {
1094        &self.workflow_steps
1095    }
1096
1097    pub fn workflow_step_for_range(&self, range: Range<language::Anchor>) -> Option<&WorkflowStep> {
1098        self.workflow_steps
1099            .iter()
1100            .find(|step| step.tagged_range == range)
1101    }
1102
1103    pub fn pending_slash_commands(&self) -> &[PendingSlashCommand] {
1104        &self.pending_slash_commands
1105    }
1106
1107    pub fn slash_command_output_sections(&self) -> &[SlashCommandOutputSection<language::Anchor>] {
1108        &self.slash_command_output_sections
1109    }
1110
1111    fn set_language(&mut self, cx: &mut ModelContext<Self>) {
1112        let markdown = self.language_registry.language_for_name("Markdown");
1113        cx.spawn(|this, mut cx| async move {
1114            let markdown = markdown.await?;
1115            this.update(&mut cx, |this, cx| {
1116                this.buffer
1117                    .update(cx, |buffer, cx| buffer.set_language(Some(markdown), cx));
1118            })
1119        })
1120        .detach_and_log_err(cx);
1121    }
1122
1123    fn handle_buffer_event(
1124        &mut self,
1125        _: Model<Buffer>,
1126        event: &language::Event,
1127        cx: &mut ModelContext<Self>,
1128    ) {
1129        match event {
1130            language::Event::Operation(operation) => cx.emit(ContextEvent::Operation(
1131                ContextOperation::BufferOperation(operation.clone()),
1132            )),
1133            language::Event::Edited => {
1134                self.count_remaining_tokens(cx);
1135                self.reparse_slash_commands(cx);
1136                // Use `inclusive = true` to invalidate a step when an edit occurs
1137                // at the start/end of a parsed step.
1138                self.prune_invalid_workflow_steps(true, cx);
1139                cx.emit(ContextEvent::MessagesEdited);
1140            }
1141            _ => {}
1142        }
1143    }
1144
1145    pub(crate) fn token_count(&self) -> Option<usize> {
1146        self.token_count
1147    }
1148
1149    pub(crate) fn count_remaining_tokens(&mut self, cx: &mut ModelContext<Self>) {
1150        let request = self.to_completion_request(cx);
1151        let Some(model) = LanguageModelRegistry::read_global(cx).active_model() else {
1152            return;
1153        };
1154        self.pending_token_count = cx.spawn(|this, mut cx| {
1155            async move {
1156                cx.background_executor()
1157                    .timer(Duration::from_millis(200))
1158                    .await;
1159
1160                let token_count = cx.update(|cx| model.count_tokens(request, cx))?.await?;
1161                this.update(&mut cx, |this, cx| {
1162                    this.token_count = Some(token_count);
1163                    cx.notify()
1164                })
1165            }
1166            .log_err()
1167        });
1168    }
1169
1170    pub fn reparse_slash_commands(&mut self, cx: &mut ModelContext<Self>) {
1171        let buffer = self.buffer.read(cx);
1172        let mut row_ranges = self
1173            .edits_since_last_slash_command_parse
1174            .consume()
1175            .into_iter()
1176            .map(|edit| {
1177                let start_row = buffer.offset_to_point(edit.new.start).row;
1178                let end_row = buffer.offset_to_point(edit.new.end).row + 1;
1179                start_row..end_row
1180            })
1181            .peekable();
1182
1183        let mut removed = Vec::new();
1184        let mut updated = Vec::new();
1185        while let Some(mut row_range) = row_ranges.next() {
1186            while let Some(next_row_range) = row_ranges.peek() {
1187                if row_range.end >= next_row_range.start {
1188                    row_range.end = next_row_range.end;
1189                    row_ranges.next();
1190                } else {
1191                    break;
1192                }
1193            }
1194
1195            let start = buffer.anchor_before(Point::new(row_range.start, 0));
1196            let end = buffer.anchor_after(Point::new(
1197                row_range.end - 1,
1198                buffer.line_len(row_range.end - 1),
1199            ));
1200
1201            let old_range = self.pending_command_indices_for_range(start..end, cx);
1202
1203            let mut new_commands = Vec::new();
1204            let mut lines = buffer.text_for_range(start..end).lines();
1205            let mut offset = lines.offset();
1206            while let Some(line) = lines.next() {
1207                if let Some(command_line) = SlashCommandLine::parse(line) {
1208                    let name = &line[command_line.name.clone()];
1209                    let argument = command_line.argument.as_ref().and_then(|argument| {
1210                        (!argument.is_empty()).then_some(&line[argument.clone()])
1211                    });
1212                    if let Some(command) = SlashCommandRegistry::global(cx).command(name) {
1213                        if !command.requires_argument() || argument.is_some() {
1214                            let start_ix = offset + command_line.name.start - 1;
1215                            let end_ix = offset
1216                                + command_line
1217                                    .argument
1218                                    .map_or(command_line.name.end, |argument| argument.end);
1219                            let source_range =
1220                                buffer.anchor_after(start_ix)..buffer.anchor_after(end_ix);
1221                            let pending_command = PendingSlashCommand {
1222                                name: name.to_string(),
1223                                argument: argument.map(ToString::to_string),
1224                                source_range,
1225                                status: PendingSlashCommandStatus::Idle,
1226                            };
1227                            updated.push(pending_command.clone());
1228                            new_commands.push(pending_command);
1229                        }
1230                    }
1231                }
1232
1233                offset = lines.offset();
1234            }
1235
1236            let removed_commands = self.pending_slash_commands.splice(old_range, new_commands);
1237            removed.extend(removed_commands.map(|command| command.source_range));
1238        }
1239
1240        if !updated.is_empty() || !removed.is_empty() {
1241            cx.emit(ContextEvent::PendingSlashCommandsUpdated { removed, updated });
1242        }
1243    }
1244
1245    fn prune_invalid_workflow_steps(&mut self, inclusive: bool, cx: &mut ModelContext<Self>) {
1246        let mut removed = Vec::new();
1247
1248        for edit_range in self.edits_since_last_workflow_step_prune.consume() {
1249            let intersecting_range = self.find_intersecting_steps(edit_range.new, inclusive, cx);
1250            removed.extend(
1251                self.workflow_steps
1252                    .drain(intersecting_range)
1253                    .map(|step| step.tagged_range),
1254            );
1255        }
1256
1257        if !removed.is_empty() {
1258            cx.emit(ContextEvent::WorkflowStepsRemoved(removed));
1259            cx.notify();
1260        }
1261    }
1262
1263    fn find_intersecting_steps(
1264        &self,
1265        range: Range<usize>,
1266        inclusive: bool,
1267        cx: &AppContext,
1268    ) -> Range<usize> {
1269        let buffer = self.buffer.read(cx);
1270        let start_ix = match self.workflow_steps.binary_search_by(|probe| {
1271            probe
1272                .tagged_range
1273                .end
1274                .to_offset(buffer)
1275                .cmp(&range.start)
1276                .then(if inclusive {
1277                    Ordering::Greater
1278                } else {
1279                    Ordering::Less
1280                })
1281        }) {
1282            Ok(ix) | Err(ix) => ix,
1283        };
1284        let end_ix = match self.workflow_steps.binary_search_by(|probe| {
1285            probe
1286                .tagged_range
1287                .start
1288                .to_offset(buffer)
1289                .cmp(&range.end)
1290                .then(if inclusive {
1291                    Ordering::Less
1292                } else {
1293                    Ordering::Greater
1294                })
1295        }) {
1296            Ok(ix) | Err(ix) => ix,
1297        };
1298        start_ix..end_ix
1299    }
1300
1301    fn parse_workflow_steps_in_range(
1302        &mut self,
1303        range: Range<usize>,
1304        project: Model<Project>,
1305        cx: &mut ModelContext<Self>,
1306    ) {
1307        let mut new_edit_steps = Vec::new();
1308        let mut edits = Vec::new();
1309
1310        let buffer = self.buffer.read(cx).snapshot();
1311        let mut message_lines = buffer.as_rope().chunks_in_range(range).lines();
1312        let mut in_step = false;
1313        let mut step_open_tag_start_ix = 0;
1314        let mut line_start_offset = message_lines.offset();
1315
1316        while let Some(line) = message_lines.next() {
1317            if let Some(step_start_index) = line.find("<step>") {
1318                if !in_step {
1319                    in_step = true;
1320                    step_open_tag_start_ix = line_start_offset + step_start_index;
1321                }
1322            }
1323
1324            if let Some(step_end_index) = line.find("</step>") {
1325                if in_step {
1326                    let mut step_open_tag_end_ix = step_open_tag_start_ix + "<step>".len();
1327                    if buffer.chars_at(step_open_tag_end_ix).next() == Some('\n') {
1328                        step_open_tag_end_ix += 1;
1329                    }
1330                    let mut step_end_tag_start_ix = line_start_offset + step_end_index;
1331                    let step_end_tag_end_ix = step_end_tag_start_ix + "</step>".len();
1332                    if buffer.reversed_chars_at(step_end_tag_start_ix).next() == Some('\n') {
1333                        step_end_tag_start_ix -= 1;
1334                    }
1335                    edits.push((step_open_tag_start_ix..step_open_tag_end_ix, ""));
1336                    edits.push((step_end_tag_start_ix..step_end_tag_end_ix, ""));
1337                    let tagged_range = buffer.anchor_after(step_open_tag_end_ix)
1338                        ..buffer.anchor_before(step_end_tag_start_ix);
1339
1340                    // Check if a step with the same range already exists
1341                    let existing_step_index = self
1342                        .workflow_steps
1343                        .binary_search_by(|probe| probe.tagged_range.cmp(&tagged_range, &buffer));
1344
1345                    if let Err(ix) = existing_step_index {
1346                        new_edit_steps.push((
1347                            ix,
1348                            WorkflowStep {
1349                                tagged_range,
1350                                status: WorkflowStepStatus::Pending(Task::ready(None)),
1351                            },
1352                        ));
1353                    }
1354
1355                    in_step = false;
1356                }
1357            }
1358
1359            line_start_offset = message_lines.offset();
1360        }
1361
1362        let mut updated = Vec::new();
1363        for (index, step) in new_edit_steps.into_iter().rev() {
1364            let step_range = step.tagged_range.clone();
1365            updated.push(step_range.clone());
1366            self.workflow_steps.insert(index, step);
1367            self.resolve_workflow_step(step_range, project.clone(), cx);
1368        }
1369
1370        // Delete <step> tags, making sure we don't accidentally invalidate
1371        // the step we just parsed.
1372        self.buffer
1373            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
1374        self.edits_since_last_workflow_step_prune.consume();
1375    }
1376
1377    pub fn resolve_workflow_step(
1378        &mut self,
1379        tagged_range: Range<language::Anchor>,
1380        project: Model<Project>,
1381        cx: &mut ModelContext<Self>,
1382    ) {
1383        let Ok(step_index) = self
1384            .workflow_steps
1385            .binary_search_by(|step| step.tagged_range.cmp(&tagged_range, self.buffer.read(cx)))
1386        else {
1387            return;
1388        };
1389
1390        let mut request = self.to_completion_request(cx);
1391        let Some(edit_step) = self.workflow_steps.get_mut(step_index) else {
1392            return;
1393        };
1394
1395        if let Some(model) = LanguageModelRegistry::read_global(cx).active_model() {
1396            let step_text = self
1397                .buffer
1398                .read(cx)
1399                .text_for_range(tagged_range.clone())
1400                .collect::<String>();
1401
1402            let tagged_range = tagged_range.clone();
1403            edit_step.status = WorkflowStepStatus::Pending(cx.spawn(|this, mut cx| {
1404                async move {
1405                    let result = async {
1406                        let mut prompt = this.update(&mut cx, |this, _| {
1407                            this.prompt_builder.generate_step_resolution_prompt()
1408                        })??;
1409                        prompt.push_str(&step_text);
1410
1411                        request.messages.push(LanguageModelRequestMessage {
1412                            role: Role::User,
1413                            content: vec![prompt.into()],
1414                        });
1415
1416                        // Invoke the model to get its edit suggestions for this workflow step.
1417                        let resolution = model
1418                            .use_tool::<tool::WorkflowStepResolution>(request, &cx)
1419                            .await?;
1420
1421                        // Translate the parsed suggestions to our internal types, which anchor the suggestions to locations in the code.
1422                        let suggestion_tasks: Vec<_> = resolution
1423                            .suggestions
1424                            .iter()
1425                            .map(|suggestion| suggestion.resolve(project.clone(), cx.clone()))
1426                            .collect();
1427
1428                        // Expand the context ranges of each suggestion and group suggestions with overlapping context ranges.
1429                        let suggestions = future::join_all(suggestion_tasks)
1430                            .await
1431                            .into_iter()
1432                            .filter_map(|task| task.log_err())
1433                            .collect::<Vec<_>>();
1434
1435                        let mut suggestions_by_buffer = HashMap::default();
1436                        for (buffer, suggestion) in suggestions {
1437                            suggestions_by_buffer
1438                                .entry(buffer)
1439                                .or_insert_with(Vec::new)
1440                                .push(suggestion);
1441                        }
1442
1443                        let mut suggestion_groups_by_buffer = HashMap::default();
1444                        for (buffer, mut suggestions) in suggestions_by_buffer {
1445                            let mut suggestion_groups = Vec::<WorkflowSuggestionGroup>::new();
1446                            let snapshot = buffer.update(&mut cx, |buffer, _| buffer.snapshot())?;
1447                            // Sort suggestions by their range so that earlier, larger ranges come first
1448                            suggestions.sort_by(|a, b| a.range().cmp(&b.range(), &snapshot));
1449
1450                            // Merge overlapping suggestions
1451                            suggestions.dedup_by(|a, b| b.try_merge(&a, &snapshot));
1452
1453                            // Create context ranges for each suggestion
1454                            for suggestion in suggestions {
1455                                let context_range = {
1456                                    let suggestion_point_range =
1457                                        suggestion.range().to_point(&snapshot);
1458                                    let start_row =
1459                                        suggestion_point_range.start.row.saturating_sub(5);
1460                                    let end_row = cmp::min(
1461                                        suggestion_point_range.end.row + 5,
1462                                        snapshot.max_point().row,
1463                                    );
1464                                    let start = snapshot.anchor_before(Point::new(start_row, 0));
1465                                    let end = snapshot.anchor_after(Point::new(
1466                                        end_row,
1467                                        snapshot.line_len(end_row),
1468                                    ));
1469                                    start..end
1470                                };
1471
1472                                if let Some(last_group) = suggestion_groups.last_mut() {
1473                                    if last_group
1474                                        .context_range
1475                                        .end
1476                                        .cmp(&context_range.start, &snapshot)
1477                                        .is_ge()
1478                                    {
1479                                        // Merge with the previous group if context ranges overlap
1480                                        last_group.context_range.end = context_range.end;
1481                                        last_group.suggestions.push(suggestion);
1482                                    } else {
1483                                        // Create a new group
1484                                        suggestion_groups.push(WorkflowSuggestionGroup {
1485                                            context_range,
1486                                            suggestions: vec![suggestion],
1487                                        });
1488                                    }
1489                                } else {
1490                                    // Create the first group
1491                                    suggestion_groups.push(WorkflowSuggestionGroup {
1492                                        context_range,
1493                                        suggestions: vec![suggestion],
1494                                    });
1495                                }
1496                            }
1497
1498                            suggestion_groups_by_buffer.insert(buffer, suggestion_groups);
1499                        }
1500
1501                        Ok((resolution.step_title, suggestion_groups_by_buffer))
1502                    };
1503
1504                    let result = result.await;
1505                    this.update(&mut cx, |this, cx| {
1506                        let step_index = this
1507                            .workflow_steps
1508                            .binary_search_by(|step| {
1509                                step.tagged_range.cmp(&tagged_range, this.buffer.read(cx))
1510                            })
1511                            .map_err(|_| anyhow!("edit step not found"))?;
1512                        if let Some(edit_step) = this.workflow_steps.get_mut(step_index) {
1513                            edit_step.status = match result {
1514                                Ok((title, suggestions)) => {
1515                                    WorkflowStepStatus::Resolved(ResolvedWorkflowStep {
1516                                        title,
1517                                        suggestions,
1518                                    })
1519                                }
1520                                Err(error) => WorkflowStepStatus::Error(Arc::new(error)),
1521                            };
1522                            cx.emit(ContextEvent::WorkflowStepUpdated(tagged_range));
1523                            cx.notify();
1524                        }
1525                        anyhow::Ok(())
1526                    })?
1527                }
1528                .log_err()
1529            }));
1530        } else {
1531            edit_step.status = WorkflowStepStatus::Error(Arc::new(anyhow!("no active model")));
1532        }
1533
1534        cx.emit(ContextEvent::WorkflowStepUpdated(tagged_range));
1535        cx.notify();
1536    }
1537
1538    pub fn pending_command_for_position(
1539        &mut self,
1540        position: language::Anchor,
1541        cx: &mut ModelContext<Self>,
1542    ) -> Option<&mut PendingSlashCommand> {
1543        let buffer = self.buffer.read(cx);
1544        match self
1545            .pending_slash_commands
1546            .binary_search_by(|probe| probe.source_range.end.cmp(&position, buffer))
1547        {
1548            Ok(ix) => Some(&mut self.pending_slash_commands[ix]),
1549            Err(ix) => {
1550                let cmd = self.pending_slash_commands.get_mut(ix)?;
1551                if position.cmp(&cmd.source_range.start, buffer).is_ge()
1552                    && position.cmp(&cmd.source_range.end, buffer).is_le()
1553                {
1554                    Some(cmd)
1555                } else {
1556                    None
1557                }
1558            }
1559        }
1560    }
1561
1562    pub fn pending_commands_for_range(
1563        &self,
1564        range: Range<language::Anchor>,
1565        cx: &AppContext,
1566    ) -> &[PendingSlashCommand] {
1567        let range = self.pending_command_indices_for_range(range, cx);
1568        &self.pending_slash_commands[range]
1569    }
1570
1571    fn pending_command_indices_for_range(
1572        &self,
1573        range: Range<language::Anchor>,
1574        cx: &AppContext,
1575    ) -> Range<usize> {
1576        let buffer = self.buffer.read(cx);
1577        let start_ix = match self
1578            .pending_slash_commands
1579            .binary_search_by(|probe| probe.source_range.end.cmp(&range.start, &buffer))
1580        {
1581            Ok(ix) | Err(ix) => ix,
1582        };
1583        let end_ix = match self
1584            .pending_slash_commands
1585            .binary_search_by(|probe| probe.source_range.start.cmp(&range.end, &buffer))
1586        {
1587            Ok(ix) => ix + 1,
1588            Err(ix) => ix,
1589        };
1590        start_ix..end_ix
1591    }
1592
1593    pub fn insert_command_output(
1594        &mut self,
1595        command_range: Range<language::Anchor>,
1596        output: Task<Result<SlashCommandOutput>>,
1597        insert_trailing_newline: bool,
1598        cx: &mut ModelContext<Self>,
1599    ) {
1600        self.reparse_slash_commands(cx);
1601
1602        let insert_output_task = cx.spawn(|this, mut cx| {
1603            let command_range = command_range.clone();
1604            async move {
1605                let output = output.await;
1606                this.update(&mut cx, |this, cx| match output {
1607                    Ok(mut output) => {
1608                        if insert_trailing_newline {
1609                            output.text.push('\n');
1610                        }
1611
1612                        let version = this.version.clone();
1613                        let command_id = SlashCommandId(this.next_timestamp());
1614                        let (operation, event) = this.buffer.update(cx, |buffer, cx| {
1615                            let start = command_range.start.to_offset(buffer);
1616                            let old_end = command_range.end.to_offset(buffer);
1617                            let new_end = start + output.text.len();
1618                            buffer.edit([(start..old_end, output.text)], None, cx);
1619
1620                            let mut sections = output
1621                                .sections
1622                                .into_iter()
1623                                .map(|section| SlashCommandOutputSection {
1624                                    range: buffer.anchor_after(start + section.range.start)
1625                                        ..buffer.anchor_before(start + section.range.end),
1626                                    icon: section.icon,
1627                                    label: section.label,
1628                                })
1629                                .collect::<Vec<_>>();
1630                            sections.sort_by(|a, b| a.range.cmp(&b.range, buffer));
1631
1632                            this.slash_command_output_sections
1633                                .extend(sections.iter().cloned());
1634                            this.slash_command_output_sections
1635                                .sort_by(|a, b| a.range.cmp(&b.range, buffer));
1636
1637                            let output_range =
1638                                buffer.anchor_after(start)..buffer.anchor_before(new_end);
1639                            this.finished_slash_commands.insert(command_id);
1640
1641                            (
1642                                ContextOperation::SlashCommandFinished {
1643                                    id: command_id,
1644                                    output_range: output_range.clone(),
1645                                    sections: sections.clone(),
1646                                    version,
1647                                },
1648                                ContextEvent::SlashCommandFinished {
1649                                    output_range,
1650                                    sections,
1651                                    run_commands_in_output: output.run_commands_in_text,
1652                                },
1653                            )
1654                        });
1655
1656                        this.push_op(operation, cx);
1657                        cx.emit(event);
1658                    }
1659                    Err(error) => {
1660                        if let Some(pending_command) =
1661                            this.pending_command_for_position(command_range.start, cx)
1662                        {
1663                            pending_command.status =
1664                                PendingSlashCommandStatus::Error(error.to_string());
1665                            cx.emit(ContextEvent::PendingSlashCommandsUpdated {
1666                                removed: vec![pending_command.source_range.clone()],
1667                                updated: vec![pending_command.clone()],
1668                            });
1669                        }
1670                    }
1671                })
1672                .ok();
1673            }
1674        });
1675
1676        if let Some(pending_command) = self.pending_command_for_position(command_range.start, cx) {
1677            pending_command.status = PendingSlashCommandStatus::Running {
1678                _task: insert_output_task.shared(),
1679            };
1680            cx.emit(ContextEvent::PendingSlashCommandsUpdated {
1681                removed: vec![pending_command.source_range.clone()],
1682                updated: vec![pending_command.clone()],
1683            });
1684        }
1685    }
1686
1687    pub fn completion_provider_changed(&mut self, cx: &mut ModelContext<Self>) {
1688        self.count_remaining_tokens(cx);
1689    }
1690
1691    pub fn assist(&mut self, cx: &mut ModelContext<Self>) -> Option<MessageAnchor> {
1692        let provider = LanguageModelRegistry::read_global(cx).active_provider()?;
1693        let model = LanguageModelRegistry::read_global(cx).active_model()?;
1694        let last_message_id = self.message_anchors.iter().rev().find_map(|message| {
1695            message
1696                .start
1697                .is_valid(self.buffer.read(cx))
1698                .then_some(message.id)
1699        })?;
1700
1701        if !provider.is_authenticated(cx) {
1702            log::info!("completion provider has no credentials");
1703            return None;
1704        }
1705
1706        let request = self.to_completion_request(cx);
1707        let assistant_message = self
1708            .insert_message_after(last_message_id, Role::Assistant, MessageStatus::Pending, cx)
1709            .unwrap();
1710
1711        // Queue up the user's next reply.
1712        let user_message = self
1713            .insert_message_after(assistant_message.id, Role::User, MessageStatus::Done, cx)
1714            .unwrap();
1715
1716        let pending_completion_id = post_inc(&mut self.completion_count);
1717
1718        let task = cx.spawn({
1719            |this, mut cx| async move {
1720                let stream = model.stream_completion(request, &cx);
1721                let assistant_message_id = assistant_message.id;
1722                let mut response_latency = None;
1723                let stream_completion = async {
1724                    let request_start = Instant::now();
1725                    let mut chunks = stream.await?;
1726
1727                    while let Some(chunk) = chunks.next().await {
1728                        if response_latency.is_none() {
1729                            response_latency = Some(request_start.elapsed());
1730                        }
1731                        let chunk = chunk?;
1732
1733                        this.update(&mut cx, |this, cx| {
1734                            let message_ix = this
1735                                .message_anchors
1736                                .iter()
1737                                .position(|message| message.id == assistant_message_id)?;
1738                            let message_range = this.buffer.update(cx, |buffer, cx| {
1739                                let message_start_offset =
1740                                    this.message_anchors[message_ix].start.to_offset(buffer);
1741                                let message_old_end_offset = this.message_anchors[message_ix + 1..]
1742                                    .iter()
1743                                    .find(|message| message.start.is_valid(buffer))
1744                                    .map_or(buffer.len(), |message| {
1745                                        message.start.to_offset(buffer).saturating_sub(1)
1746                                    });
1747                                let message_new_end_offset = message_old_end_offset + chunk.len();
1748                                buffer.edit(
1749                                    [(message_old_end_offset..message_old_end_offset, chunk)],
1750                                    None,
1751                                    cx,
1752                                );
1753                                message_start_offset..message_new_end_offset
1754                            });
1755                            if let Some(project) = this.project.clone() {
1756                                // Use `inclusive = false` as edits might occur at the end of a parsed step.
1757                                this.prune_invalid_workflow_steps(false, cx);
1758                                this.parse_workflow_steps_in_range(message_range, project, cx);
1759                            }
1760                            cx.emit(ContextEvent::StreamedCompletion);
1761
1762                            Some(())
1763                        })?;
1764                        smol::future::yield_now().await;
1765                    }
1766                    this.update(&mut cx, |this, cx| {
1767                        this.pending_completions
1768                            .retain(|completion| completion.id != pending_completion_id);
1769                        this.summarize(false, cx);
1770                    })?;
1771
1772                    anyhow::Ok(())
1773                };
1774
1775                let result = stream_completion.await;
1776
1777                this.update(&mut cx, |this, cx| {
1778                    let error_message = result
1779                        .err()
1780                        .map(|error| error.to_string().trim().to_string());
1781
1782                    if let Some(error_message) = error_message.as_ref() {
1783                        cx.emit(ContextEvent::AssistError(error_message.to_string()));
1784                    }
1785
1786                    this.update_metadata(assistant_message_id, cx, |metadata| {
1787                        if let Some(error_message) = error_message.as_ref() {
1788                            metadata.status =
1789                                MessageStatus::Error(SharedString::from(error_message.clone()));
1790                        } else {
1791                            metadata.status = MessageStatus::Done;
1792                        }
1793                    });
1794
1795                    if let Some(telemetry) = this.telemetry.as_ref() {
1796                        telemetry.report_assistant_event(
1797                            Some(this.id.0.clone()),
1798                            AssistantKind::Panel,
1799                            model.telemetry_id(),
1800                            response_latency,
1801                            error_message,
1802                        );
1803                    }
1804                })
1805                .ok();
1806            }
1807        });
1808
1809        self.pending_completions.push(PendingCompletion {
1810            id: pending_completion_id,
1811            assistant_message_id: assistant_message.id,
1812            _task: task,
1813        });
1814
1815        Some(user_message)
1816    }
1817
1818    pub fn to_completion_request(&self, cx: &AppContext) -> LanguageModelRequest {
1819        let buffer = self.buffer.read(cx);
1820        let request_messages = self
1821            .messages(cx)
1822            .filter(|message| message.status == MessageStatus::Done)
1823            .map(|message| message.to_request_message(&buffer))
1824            .collect();
1825
1826        LanguageModelRequest {
1827            messages: request_messages,
1828            stop: vec![],
1829            temperature: 1.0,
1830        }
1831    }
1832
1833    pub fn cancel_last_assist(&mut self, cx: &mut ModelContext<Self>) -> bool {
1834        if let Some(pending_completion) = self.pending_completions.pop() {
1835            self.update_metadata(pending_completion.assistant_message_id, cx, |metadata| {
1836                metadata.status = MessageStatus::Canceled;
1837            });
1838            true
1839        } else {
1840            false
1841        }
1842    }
1843
1844    pub fn cycle_message_roles(&mut self, ids: HashSet<MessageId>, cx: &mut ModelContext<Self>) {
1845        for id in ids {
1846            if let Some(metadata) = self.messages_metadata.get(&id) {
1847                let role = metadata.role.cycle();
1848                self.update_metadata(id, cx, |metadata| metadata.role = role);
1849            }
1850        }
1851    }
1852
1853    pub fn update_metadata(
1854        &mut self,
1855        id: MessageId,
1856        cx: &mut ModelContext<Self>,
1857        f: impl FnOnce(&mut MessageMetadata),
1858    ) {
1859        let version = self.version.clone();
1860        let timestamp = self.next_timestamp();
1861        if let Some(metadata) = self.messages_metadata.get_mut(&id) {
1862            f(metadata);
1863            metadata.timestamp = timestamp;
1864            let operation = ContextOperation::UpdateMessage {
1865                message_id: id,
1866                metadata: metadata.clone(),
1867                version,
1868            };
1869            self.push_op(operation, cx);
1870            cx.emit(ContextEvent::MessagesEdited);
1871            cx.notify();
1872        }
1873    }
1874
1875    fn insert_message_after(
1876        &mut self,
1877        message_id: MessageId,
1878        role: Role,
1879        status: MessageStatus,
1880        cx: &mut ModelContext<Self>,
1881    ) -> Option<MessageAnchor> {
1882        if let Some(prev_message_ix) = self
1883            .message_anchors
1884            .iter()
1885            .position(|message| message.id == message_id)
1886        {
1887            // Find the next valid message after the one we were given.
1888            let mut next_message_ix = prev_message_ix + 1;
1889            while let Some(next_message) = self.message_anchors.get(next_message_ix) {
1890                if next_message.start.is_valid(self.buffer.read(cx)) {
1891                    break;
1892                }
1893                next_message_ix += 1;
1894            }
1895
1896            let start = self.buffer.update(cx, |buffer, cx| {
1897                let offset = self
1898                    .message_anchors
1899                    .get(next_message_ix)
1900                    .map_or(buffer.len(), |message| {
1901                        buffer.clip_offset(message.start.to_offset(buffer) - 1, Bias::Left)
1902                    });
1903                buffer.edit([(offset..offset, "\n")], None, cx);
1904                buffer.anchor_before(offset + 1)
1905            });
1906
1907            let version = self.version.clone();
1908            let anchor = MessageAnchor {
1909                id: MessageId(self.next_timestamp()),
1910                start,
1911            };
1912            let metadata = MessageMetadata {
1913                role,
1914                status,
1915                timestamp: anchor.id.0,
1916            };
1917            self.insert_message(anchor.clone(), metadata.clone(), cx);
1918            self.push_op(
1919                ContextOperation::InsertMessage {
1920                    anchor: anchor.clone(),
1921                    metadata,
1922                    version,
1923                },
1924                cx,
1925            );
1926            Some(anchor)
1927        } else {
1928            None
1929        }
1930    }
1931
1932    pub fn insert_image(&mut self, image: Image, cx: &mut ModelContext<Self>) -> Option<()> {
1933        if let hash_map::Entry::Vacant(entry) = self.images.entry(image.id()) {
1934            entry.insert((
1935                image.to_image_data(cx).log_err()?,
1936                LanguageModelImage::from_image(image, cx).shared(),
1937            ));
1938        }
1939
1940        Some(())
1941    }
1942
1943    pub fn insert_image_anchor(
1944        &mut self,
1945        image_id: u64,
1946        anchor: language::Anchor,
1947        cx: &mut ModelContext<Self>,
1948    ) -> bool {
1949        cx.emit(ContextEvent::MessagesEdited);
1950
1951        let buffer = self.buffer.read(cx);
1952        let insertion_ix = match self
1953            .image_anchors
1954            .binary_search_by(|existing_anchor| anchor.cmp(&existing_anchor.anchor, buffer))
1955        {
1956            Ok(ix) => ix,
1957            Err(ix) => ix,
1958        };
1959
1960        if let Some((render_image, image)) = self.images.get(&image_id) {
1961            self.image_anchors.insert(
1962                insertion_ix,
1963                ImageAnchor {
1964                    anchor,
1965                    image_id,
1966                    image: image.clone(),
1967                    render_image: render_image.clone(),
1968                },
1969            );
1970
1971            true
1972        } else {
1973            false
1974        }
1975    }
1976
1977    pub fn images<'a>(&'a self, _cx: &'a AppContext) -> impl 'a + Iterator<Item = ImageAnchor> {
1978        self.image_anchors.iter().cloned()
1979    }
1980
1981    pub fn split_message(
1982        &mut self,
1983        range: Range<usize>,
1984        cx: &mut ModelContext<Self>,
1985    ) -> (Option<MessageAnchor>, Option<MessageAnchor>) {
1986        let start_message = self.message_for_offset(range.start, cx);
1987        let end_message = self.message_for_offset(range.end, cx);
1988        if let Some((start_message, end_message)) = start_message.zip(end_message) {
1989            // Prevent splitting when range spans multiple messages.
1990            if start_message.id != end_message.id {
1991                return (None, None);
1992            }
1993
1994            let message = start_message;
1995            let role = message.role;
1996            let mut edited_buffer = false;
1997
1998            let mut suffix_start = None;
1999
2000            // TODO: why did this start panicking?
2001            if range.start > message.offset_range.start
2002                && range.end < message.offset_range.end.saturating_sub(1)
2003            {
2004                if self.buffer.read(cx).chars_at(range.end).next() == Some('\n') {
2005                    suffix_start = Some(range.end + 1);
2006                } else if self.buffer.read(cx).reversed_chars_at(range.end).next() == Some('\n') {
2007                    suffix_start = Some(range.end);
2008                }
2009            }
2010
2011            let version = self.version.clone();
2012            let suffix = if let Some(suffix_start) = suffix_start {
2013                MessageAnchor {
2014                    id: MessageId(self.next_timestamp()),
2015                    start: self.buffer.read(cx).anchor_before(suffix_start),
2016                }
2017            } else {
2018                self.buffer.update(cx, |buffer, cx| {
2019                    buffer.edit([(range.end..range.end, "\n")], None, cx);
2020                });
2021                edited_buffer = true;
2022                MessageAnchor {
2023                    id: MessageId(self.next_timestamp()),
2024                    start: self.buffer.read(cx).anchor_before(range.end + 1),
2025                }
2026            };
2027
2028            let suffix_metadata = MessageMetadata {
2029                role,
2030                status: MessageStatus::Done,
2031                timestamp: suffix.id.0,
2032            };
2033            self.insert_message(suffix.clone(), suffix_metadata.clone(), cx);
2034            self.push_op(
2035                ContextOperation::InsertMessage {
2036                    anchor: suffix.clone(),
2037                    metadata: suffix_metadata,
2038                    version,
2039                },
2040                cx,
2041            );
2042
2043            let new_messages =
2044                if range.start == range.end || range.start == message.offset_range.start {
2045                    (None, Some(suffix))
2046                } else {
2047                    let mut prefix_end = None;
2048                    if range.start > message.offset_range.start
2049                        && range.end < message.offset_range.end - 1
2050                    {
2051                        if self.buffer.read(cx).chars_at(range.start).next() == Some('\n') {
2052                            prefix_end = Some(range.start + 1);
2053                        } else if self.buffer.read(cx).reversed_chars_at(range.start).next()
2054                            == Some('\n')
2055                        {
2056                            prefix_end = Some(range.start);
2057                        }
2058                    }
2059
2060                    let version = self.version.clone();
2061                    let selection = if let Some(prefix_end) = prefix_end {
2062                        MessageAnchor {
2063                            id: MessageId(self.next_timestamp()),
2064                            start: self.buffer.read(cx).anchor_before(prefix_end),
2065                        }
2066                    } else {
2067                        self.buffer.update(cx, |buffer, cx| {
2068                            buffer.edit([(range.start..range.start, "\n")], None, cx)
2069                        });
2070                        edited_buffer = true;
2071                        MessageAnchor {
2072                            id: MessageId(self.next_timestamp()),
2073                            start: self.buffer.read(cx).anchor_before(range.end + 1),
2074                        }
2075                    };
2076
2077                    let selection_metadata = MessageMetadata {
2078                        role,
2079                        status: MessageStatus::Done,
2080                        timestamp: selection.id.0,
2081                    };
2082                    self.insert_message(selection.clone(), selection_metadata.clone(), cx);
2083                    self.push_op(
2084                        ContextOperation::InsertMessage {
2085                            anchor: selection.clone(),
2086                            metadata: selection_metadata,
2087                            version,
2088                        },
2089                        cx,
2090                    );
2091
2092                    (Some(selection), Some(suffix))
2093                };
2094
2095            if !edited_buffer {
2096                cx.emit(ContextEvent::MessagesEdited);
2097            }
2098            new_messages
2099        } else {
2100            (None, None)
2101        }
2102    }
2103
2104    fn insert_message(
2105        &mut self,
2106        new_anchor: MessageAnchor,
2107        new_metadata: MessageMetadata,
2108        cx: &mut ModelContext<Self>,
2109    ) {
2110        cx.emit(ContextEvent::MessagesEdited);
2111
2112        self.messages_metadata.insert(new_anchor.id, new_metadata);
2113
2114        let buffer = self.buffer.read(cx);
2115        let insertion_ix = self
2116            .message_anchors
2117            .iter()
2118            .position(|anchor| {
2119                let comparison = new_anchor.start.cmp(&anchor.start, buffer);
2120                comparison.is_lt() || (comparison.is_eq() && new_anchor.id > anchor.id)
2121            })
2122            .unwrap_or(self.message_anchors.len());
2123        self.message_anchors.insert(insertion_ix, new_anchor);
2124    }
2125
2126    pub(super) fn summarize(&mut self, replace_old: bool, cx: &mut ModelContext<Self>) {
2127        let Some(provider) = LanguageModelRegistry::read_global(cx).active_provider() else {
2128            return;
2129        };
2130        let Some(model) = LanguageModelRegistry::read_global(cx).active_model() else {
2131            return;
2132        };
2133
2134        if replace_old || (self.message_anchors.len() >= 2 && self.summary.is_none()) {
2135            if !provider.is_authenticated(cx) {
2136                return;
2137            }
2138
2139            let messages = self
2140                .messages(cx)
2141                .map(|message| message.to_request_message(self.buffer.read(cx)))
2142                .chain(Some(LanguageModelRequestMessage {
2143                    role: Role::User,
2144                    content: vec![
2145                        "Summarize the context into a short title without punctuation.".into(),
2146                    ],
2147                }));
2148            let request = LanguageModelRequest {
2149                messages: messages.collect(),
2150                stop: vec![],
2151                temperature: 1.0,
2152            };
2153
2154            self.pending_summary = cx.spawn(|this, mut cx| {
2155                async move {
2156                    let stream = model.stream_completion(request, &cx);
2157                    let mut messages = stream.await?;
2158
2159                    let mut replaced = !replace_old;
2160                    while let Some(message) = messages.next().await {
2161                        let text = message?;
2162                        let mut lines = text.lines();
2163                        this.update(&mut cx, |this, cx| {
2164                            let version = this.version.clone();
2165                            let timestamp = this.next_timestamp();
2166                            let summary = this.summary.get_or_insert(ContextSummary::default());
2167                            if !replaced && replace_old {
2168                                summary.text.clear();
2169                                replaced = true;
2170                            }
2171                            summary.text.extend(lines.next());
2172                            summary.timestamp = timestamp;
2173                            let operation = ContextOperation::UpdateSummary {
2174                                summary: summary.clone(),
2175                                version,
2176                            };
2177                            this.push_op(operation, cx);
2178                            cx.emit(ContextEvent::SummaryChanged);
2179                        })?;
2180
2181                        // Stop if the LLM generated multiple lines.
2182                        if lines.next().is_some() {
2183                            break;
2184                        }
2185                    }
2186
2187                    this.update(&mut cx, |this, cx| {
2188                        let version = this.version.clone();
2189                        let timestamp = this.next_timestamp();
2190                        if let Some(summary) = this.summary.as_mut() {
2191                            summary.done = true;
2192                            summary.timestamp = timestamp;
2193                            let operation = ContextOperation::UpdateSummary {
2194                                summary: summary.clone(),
2195                                version,
2196                            };
2197                            this.push_op(operation, cx);
2198                            cx.emit(ContextEvent::SummaryChanged);
2199                        }
2200                    })?;
2201
2202                    anyhow::Ok(())
2203                }
2204                .log_err()
2205            });
2206        }
2207    }
2208
2209    fn message_for_offset(&self, offset: usize, cx: &AppContext) -> Option<Message> {
2210        self.messages_for_offsets([offset], cx).pop()
2211    }
2212
2213    pub fn messages_for_offsets(
2214        &self,
2215        offsets: impl IntoIterator<Item = usize>,
2216        cx: &AppContext,
2217    ) -> Vec<Message> {
2218        let mut result = Vec::new();
2219
2220        let mut messages = self.messages(cx).peekable();
2221        let mut offsets = offsets.into_iter().peekable();
2222        let mut current_message = messages.next();
2223        while let Some(offset) = offsets.next() {
2224            // Locate the message that contains the offset.
2225            while current_message.as_ref().map_or(false, |message| {
2226                !message.offset_range.contains(&offset) && messages.peek().is_some()
2227            }) {
2228                current_message = messages.next();
2229            }
2230            let Some(message) = current_message.as_ref() else {
2231                break;
2232            };
2233
2234            // Skip offsets that are in the same message.
2235            while offsets.peek().map_or(false, |offset| {
2236                message.offset_range.contains(offset) || messages.peek().is_none()
2237            }) {
2238                offsets.next();
2239            }
2240
2241            result.push(message.clone());
2242        }
2243        result
2244    }
2245
2246    pub fn messages<'a>(&'a self, cx: &'a AppContext) -> impl 'a + Iterator<Item = Message> {
2247        let buffer = self.buffer.read(cx);
2248        let messages = self.message_anchors.iter().enumerate();
2249        let images = self.image_anchors.iter();
2250
2251        Self::messages_from_iters(buffer, &self.messages_metadata, messages, images)
2252    }
2253
2254    pub fn messages_from_iters<'a>(
2255        buffer: &'a Buffer,
2256        metadata: &'a HashMap<MessageId, MessageMetadata>,
2257        messages: impl Iterator<Item = (usize, &'a MessageAnchor)> + 'a,
2258        images: impl Iterator<Item = &'a ImageAnchor> + 'a,
2259    ) -> impl 'a + Iterator<Item = Message> {
2260        let mut messages = messages.peekable();
2261        let mut images = images.peekable();
2262
2263        iter::from_fn(move || {
2264            if let Some((start_ix, message_anchor)) = messages.next() {
2265                let metadata = metadata.get(&message_anchor.id)?;
2266
2267                let message_start = message_anchor.start.to_offset(buffer);
2268                let mut message_end = None;
2269                let mut end_ix = start_ix;
2270                while let Some((_, next_message)) = messages.peek() {
2271                    if next_message.start.is_valid(buffer) {
2272                        message_end = Some(next_message.start);
2273                        break;
2274                    } else {
2275                        end_ix += 1;
2276                        messages.next();
2277                    }
2278                }
2279                let message_end_anchor = message_end.unwrap_or(language::Anchor::MAX);
2280                let message_end = message_end_anchor.to_offset(buffer);
2281
2282                let mut image_offsets = SmallVec::new();
2283                while let Some(image_anchor) = images.peek() {
2284                    if image_anchor.anchor.cmp(&message_end_anchor, buffer).is_lt() {
2285                        image_offsets.push((
2286                            image_anchor.anchor.to_offset(buffer),
2287                            MessageImage {
2288                                image_id: image_anchor.image_id,
2289                                image: image_anchor.image.clone(),
2290                            },
2291                        ));
2292                        images.next();
2293                    } else {
2294                        break;
2295                    }
2296                }
2297
2298                return Some(Message {
2299                    index_range: start_ix..end_ix,
2300                    offset_range: message_start..message_end,
2301                    id: message_anchor.id,
2302                    anchor: message_anchor.start,
2303                    role: metadata.role,
2304                    status: metadata.status.clone(),
2305                    image_offsets,
2306                });
2307            }
2308            None
2309        })
2310    }
2311
2312    pub fn save(
2313        &mut self,
2314        debounce: Option<Duration>,
2315        fs: Arc<dyn Fs>,
2316        cx: &mut ModelContext<Context>,
2317    ) {
2318        if self.replica_id() != ReplicaId::default() {
2319            // Prevent saving a remote context for now.
2320            return;
2321        }
2322
2323        self.pending_save = cx.spawn(|this, mut cx| async move {
2324            if let Some(debounce) = debounce {
2325                cx.background_executor().timer(debounce).await;
2326            }
2327
2328            let (old_path, summary) = this.read_with(&cx, |this, _| {
2329                let path = this.path.clone();
2330                let summary = if let Some(summary) = this.summary.as_ref() {
2331                    if summary.done {
2332                        Some(summary.text.clone())
2333                    } else {
2334                        None
2335                    }
2336                } else {
2337                    None
2338                };
2339                (path, summary)
2340            })?;
2341
2342            if let Some(summary) = summary {
2343                this.read_with(&cx, |this, cx| this.serialize_images(fs.clone(), cx))?
2344                    .await;
2345
2346                let context = this.read_with(&cx, |this, cx| this.serialize(cx))?;
2347                let mut discriminant = 1;
2348                let mut new_path;
2349                loop {
2350                    new_path = contexts_dir().join(&format!(
2351                        "{} - {}.zed.json",
2352                        summary.trim(),
2353                        discriminant
2354                    ));
2355                    if fs.is_file(&new_path).await {
2356                        discriminant += 1;
2357                    } else {
2358                        break;
2359                    }
2360                }
2361
2362                fs.create_dir(contexts_dir().as_ref()).await?;
2363                fs.atomic_write(new_path.clone(), serde_json::to_string(&context).unwrap())
2364                    .await?;
2365                if let Some(old_path) = old_path {
2366                    if new_path != old_path {
2367                        fs.remove_file(
2368                            &old_path,
2369                            RemoveOptions {
2370                                recursive: false,
2371                                ignore_if_not_exists: true,
2372                            },
2373                        )
2374                        .await?;
2375                    }
2376                }
2377
2378                this.update(&mut cx, |this, _| this.path = Some(new_path))?;
2379            }
2380
2381            Ok(())
2382        });
2383    }
2384
2385    pub fn serialize_images(&self, fs: Arc<dyn Fs>, cx: &AppContext) -> Task<()> {
2386        let mut images_to_save = self
2387            .images
2388            .iter()
2389            .map(|(id, (_, llm_image))| {
2390                let fs = fs.clone();
2391                let llm_image = llm_image.clone();
2392                let id = *id;
2393                async move {
2394                    if let Some(llm_image) = llm_image.await {
2395                        let path: PathBuf =
2396                            context_images_dir().join(&format!("{}.png.base64", id));
2397                        if fs
2398                            .metadata(path.as_path())
2399                            .await
2400                            .log_err()
2401                            .flatten()
2402                            .is_none()
2403                        {
2404                            fs.atomic_write(path, llm_image.source.to_string())
2405                                .await
2406                                .log_err();
2407                        }
2408                    }
2409                }
2410            })
2411            .collect::<FuturesUnordered<_>>();
2412        cx.background_executor().spawn(async move {
2413            if fs
2414                .create_dir(context_images_dir().as_ref())
2415                .await
2416                .log_err()
2417                .is_some()
2418            {
2419                while let Some(_) = images_to_save.next().await {}
2420            }
2421        })
2422    }
2423
2424    pub(crate) fn custom_summary(&mut self, custom_summary: String, cx: &mut ModelContext<Self>) {
2425        let timestamp = self.next_timestamp();
2426        let summary = self.summary.get_or_insert(ContextSummary::default());
2427        summary.timestamp = timestamp;
2428        summary.done = true;
2429        summary.text = custom_summary;
2430        cx.emit(ContextEvent::SummaryChanged);
2431    }
2432}
2433
2434#[derive(Debug, Default)]
2435pub struct ContextVersion {
2436    context: clock::Global,
2437    buffer: clock::Global,
2438}
2439
2440impl ContextVersion {
2441    pub fn from_proto(proto: &proto::ContextVersion) -> Self {
2442        Self {
2443            context: language::proto::deserialize_version(&proto.context_version),
2444            buffer: language::proto::deserialize_version(&proto.buffer_version),
2445        }
2446    }
2447
2448    pub fn to_proto(&self, context_id: ContextId) -> proto::ContextVersion {
2449        proto::ContextVersion {
2450            context_id: context_id.to_proto(),
2451            context_version: language::proto::serialize_version(&self.context),
2452            buffer_version: language::proto::serialize_version(&self.buffer),
2453        }
2454    }
2455}
2456
2457#[derive(Debug, Clone)]
2458pub struct PendingSlashCommand {
2459    pub name: String,
2460    pub argument: Option<String>,
2461    pub status: PendingSlashCommandStatus,
2462    pub source_range: Range<language::Anchor>,
2463}
2464
2465#[derive(Debug, Clone)]
2466pub enum PendingSlashCommandStatus {
2467    Idle,
2468    Running { _task: Shared<Task<()>> },
2469    Error(String),
2470}
2471
2472#[derive(Serialize, Deserialize)]
2473pub struct SavedMessage {
2474    pub id: MessageId,
2475    pub start: usize,
2476    pub metadata: MessageMetadata,
2477    #[serde(default)]
2478    // This is defaulted for backwards compatibility with JSON files created before August 2024. We didn't always have this field.
2479    pub image_offsets: Vec<(usize, u64)>,
2480}
2481
2482#[derive(Serialize, Deserialize)]
2483pub struct SavedContext {
2484    pub id: Option<ContextId>,
2485    pub zed: String,
2486    pub version: String,
2487    pub text: String,
2488    pub messages: Vec<SavedMessage>,
2489    pub summary: String,
2490    pub slash_command_output_sections:
2491        Vec<assistant_slash_command::SlashCommandOutputSection<usize>>,
2492}
2493
2494impl SavedContext {
2495    pub const VERSION: &'static str = "0.4.0";
2496
2497    pub fn from_json(json: &str) -> Result<Self> {
2498        let saved_context_json = serde_json::from_str::<serde_json::Value>(json)?;
2499        match saved_context_json
2500            .get("version")
2501            .ok_or_else(|| anyhow!("version not found"))?
2502        {
2503            serde_json::Value::String(version) => match version.as_str() {
2504                SavedContext::VERSION => {
2505                    Ok(serde_json::from_value::<SavedContext>(saved_context_json)?)
2506                }
2507                SavedContextV0_3_0::VERSION => {
2508                    let saved_context =
2509                        serde_json::from_value::<SavedContextV0_3_0>(saved_context_json)?;
2510                    Ok(saved_context.upgrade())
2511                }
2512                SavedContextV0_2_0::VERSION => {
2513                    let saved_context =
2514                        serde_json::from_value::<SavedContextV0_2_0>(saved_context_json)?;
2515                    Ok(saved_context.upgrade())
2516                }
2517                SavedContextV0_1_0::VERSION => {
2518                    let saved_context =
2519                        serde_json::from_value::<SavedContextV0_1_0>(saved_context_json)?;
2520                    Ok(saved_context.upgrade())
2521                }
2522                _ => Err(anyhow!("unrecognized saved context version: {}", version)),
2523            },
2524            _ => Err(anyhow!("version not found on saved context")),
2525        }
2526    }
2527
2528    fn into_ops(
2529        self,
2530        buffer: &Model<Buffer>,
2531        cx: &mut ModelContext<Context>,
2532    ) -> Vec<ContextOperation> {
2533        let mut operations = Vec::new();
2534        let mut version = clock::Global::new();
2535        let mut next_timestamp = clock::Lamport::new(ReplicaId::default());
2536
2537        let mut first_message_metadata = None;
2538        for message in self.messages {
2539            if message.id == MessageId(clock::Lamport::default()) {
2540                first_message_metadata = Some(message.metadata);
2541            } else {
2542                operations.push(ContextOperation::InsertMessage {
2543                    anchor: MessageAnchor {
2544                        id: message.id,
2545                        start: buffer.read(cx).anchor_before(message.start),
2546                    },
2547                    metadata: MessageMetadata {
2548                        role: message.metadata.role,
2549                        status: message.metadata.status,
2550                        timestamp: message.metadata.timestamp,
2551                    },
2552                    version: version.clone(),
2553                });
2554                version.observe(message.id.0);
2555                next_timestamp.observe(message.id.0);
2556            }
2557        }
2558
2559        if let Some(metadata) = first_message_metadata {
2560            let timestamp = next_timestamp.tick();
2561            operations.push(ContextOperation::UpdateMessage {
2562                message_id: MessageId(clock::Lamport::default()),
2563                metadata: MessageMetadata {
2564                    role: metadata.role,
2565                    status: metadata.status,
2566                    timestamp,
2567                },
2568                version: version.clone(),
2569            });
2570            version.observe(timestamp);
2571        }
2572
2573        let timestamp = next_timestamp.tick();
2574        operations.push(ContextOperation::SlashCommandFinished {
2575            id: SlashCommandId(timestamp),
2576            output_range: language::Anchor::MIN..language::Anchor::MAX,
2577            sections: self
2578                .slash_command_output_sections
2579                .into_iter()
2580                .map(|section| {
2581                    let buffer = buffer.read(cx);
2582                    SlashCommandOutputSection {
2583                        range: buffer.anchor_after(section.range.start)
2584                            ..buffer.anchor_before(section.range.end),
2585                        icon: section.icon,
2586                        label: section.label,
2587                    }
2588                })
2589                .collect(),
2590            version: version.clone(),
2591        });
2592        version.observe(timestamp);
2593
2594        let timestamp = next_timestamp.tick();
2595        operations.push(ContextOperation::UpdateSummary {
2596            summary: ContextSummary {
2597                text: self.summary,
2598                done: true,
2599                timestamp,
2600            },
2601            version: version.clone(),
2602        });
2603        version.observe(timestamp);
2604
2605        operations
2606    }
2607}
2608
2609#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
2610struct SavedMessageIdPreV0_4_0(usize);
2611
2612#[derive(Serialize, Deserialize)]
2613struct SavedMessagePreV0_4_0 {
2614    id: SavedMessageIdPreV0_4_0,
2615    start: usize,
2616}
2617
2618#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
2619struct SavedMessageMetadataPreV0_4_0 {
2620    role: Role,
2621    status: MessageStatus,
2622}
2623
2624#[derive(Serialize, Deserialize)]
2625struct SavedContextV0_3_0 {
2626    id: Option<ContextId>,
2627    zed: String,
2628    version: String,
2629    text: String,
2630    messages: Vec<SavedMessagePreV0_4_0>,
2631    message_metadata: HashMap<SavedMessageIdPreV0_4_0, SavedMessageMetadataPreV0_4_0>,
2632    summary: String,
2633    slash_command_output_sections: Vec<assistant_slash_command::SlashCommandOutputSection<usize>>,
2634}
2635
2636impl SavedContextV0_3_0 {
2637    const VERSION: &'static str = "0.3.0";
2638
2639    fn upgrade(self) -> SavedContext {
2640        SavedContext {
2641            id: self.id,
2642            zed: self.zed,
2643            version: SavedContext::VERSION.into(),
2644            text: self.text,
2645            messages: self
2646                .messages
2647                .into_iter()
2648                .filter_map(|message| {
2649                    let metadata = self.message_metadata.get(&message.id)?;
2650                    let timestamp = clock::Lamport {
2651                        replica_id: ReplicaId::default(),
2652                        value: message.id.0 as u32,
2653                    };
2654                    Some(SavedMessage {
2655                        id: MessageId(timestamp),
2656                        start: message.start,
2657                        metadata: MessageMetadata {
2658                            role: metadata.role,
2659                            status: metadata.status.clone(),
2660                            timestamp,
2661                        },
2662                        image_offsets: Vec::new(),
2663                    })
2664                })
2665                .collect(),
2666            summary: self.summary,
2667            slash_command_output_sections: self.slash_command_output_sections,
2668        }
2669    }
2670}
2671
2672#[derive(Serialize, Deserialize)]
2673struct SavedContextV0_2_0 {
2674    id: Option<ContextId>,
2675    zed: String,
2676    version: String,
2677    text: String,
2678    messages: Vec<SavedMessagePreV0_4_0>,
2679    message_metadata: HashMap<SavedMessageIdPreV0_4_0, SavedMessageMetadataPreV0_4_0>,
2680    summary: String,
2681}
2682
2683impl SavedContextV0_2_0 {
2684    const VERSION: &'static str = "0.2.0";
2685
2686    fn upgrade(self) -> SavedContext {
2687        SavedContextV0_3_0 {
2688            id: self.id,
2689            zed: self.zed,
2690            version: SavedContextV0_3_0::VERSION.to_string(),
2691            text: self.text,
2692            messages: self.messages,
2693            message_metadata: self.message_metadata,
2694            summary: self.summary,
2695            slash_command_output_sections: Vec::new(),
2696        }
2697        .upgrade()
2698    }
2699}
2700
2701#[derive(Serialize, Deserialize)]
2702struct SavedContextV0_1_0 {
2703    id: Option<ContextId>,
2704    zed: String,
2705    version: String,
2706    text: String,
2707    messages: Vec<SavedMessagePreV0_4_0>,
2708    message_metadata: HashMap<SavedMessageIdPreV0_4_0, SavedMessageMetadataPreV0_4_0>,
2709    summary: String,
2710    api_url: Option<String>,
2711    model: OpenAiModel,
2712}
2713
2714impl SavedContextV0_1_0 {
2715    const VERSION: &'static str = "0.1.0";
2716
2717    fn upgrade(self) -> SavedContext {
2718        SavedContextV0_2_0 {
2719            id: self.id,
2720            zed: self.zed,
2721            version: SavedContextV0_2_0::VERSION.to_string(),
2722            text: self.text,
2723            messages: self.messages,
2724            message_metadata: self.message_metadata,
2725            summary: self.summary,
2726        }
2727        .upgrade()
2728    }
2729}
2730
2731#[derive(Clone)]
2732pub struct SavedContextMetadata {
2733    pub title: String,
2734    pub path: PathBuf,
2735    pub mtime: chrono::DateTime<chrono::Local>,
2736}
2737
2738#[cfg(test)]
2739mod tests {
2740    use super::*;
2741    use crate::{assistant_panel, prompt_library, slash_command::file_command, MessageId};
2742    use assistant_slash_command::{ArgumentCompletion, SlashCommand};
2743    use fs::FakeFs;
2744    use gpui::{AppContext, TestAppContext, WeakView};
2745    use indoc::indoc;
2746    use language::LspAdapterDelegate;
2747    use parking_lot::Mutex;
2748    use project::Project;
2749    use rand::prelude::*;
2750    use serde_json::json;
2751    use settings::SettingsStore;
2752    use std::{cell::RefCell, env, rc::Rc, sync::atomic::AtomicBool};
2753    use text::{network::Network, ToPoint};
2754    use ui::WindowContext;
2755    use unindent::Unindent;
2756    use util::{test::marked_text_ranges, RandomCharIter};
2757    use workspace::Workspace;
2758
2759    #[gpui::test]
2760    fn test_inserting_and_removing_messages(cx: &mut AppContext) {
2761        let settings_store = SettingsStore::test(cx);
2762        LanguageModelRegistry::test(cx);
2763        cx.set_global(settings_store);
2764        assistant_panel::init(cx);
2765        let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
2766        let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
2767        let context =
2768            cx.new_model(|cx| Context::local(registry, None, None, prompt_builder.clone(), cx));
2769        let buffer = context.read(cx).buffer.clone();
2770
2771        let message_1 = context.read(cx).message_anchors[0].clone();
2772        assert_eq!(
2773            messages(&context, cx),
2774            vec![(message_1.id, Role::User, 0..0)]
2775        );
2776
2777        let message_2 = context.update(cx, |context, cx| {
2778            context
2779                .insert_message_after(message_1.id, Role::Assistant, MessageStatus::Done, cx)
2780                .unwrap()
2781        });
2782        assert_eq!(
2783            messages(&context, cx),
2784            vec![
2785                (message_1.id, Role::User, 0..1),
2786                (message_2.id, Role::Assistant, 1..1)
2787            ]
2788        );
2789
2790        buffer.update(cx, |buffer, cx| {
2791            buffer.edit([(0..0, "1"), (1..1, "2")], None, cx)
2792        });
2793        assert_eq!(
2794            messages(&context, cx),
2795            vec![
2796                (message_1.id, Role::User, 0..2),
2797                (message_2.id, Role::Assistant, 2..3)
2798            ]
2799        );
2800
2801        let message_3 = context.update(cx, |context, cx| {
2802            context
2803                .insert_message_after(message_2.id, Role::User, MessageStatus::Done, cx)
2804                .unwrap()
2805        });
2806        assert_eq!(
2807            messages(&context, cx),
2808            vec![
2809                (message_1.id, Role::User, 0..2),
2810                (message_2.id, Role::Assistant, 2..4),
2811                (message_3.id, Role::User, 4..4)
2812            ]
2813        );
2814
2815        let message_4 = context.update(cx, |context, cx| {
2816            context
2817                .insert_message_after(message_2.id, Role::User, MessageStatus::Done, cx)
2818                .unwrap()
2819        });
2820        assert_eq!(
2821            messages(&context, cx),
2822            vec![
2823                (message_1.id, Role::User, 0..2),
2824                (message_2.id, Role::Assistant, 2..4),
2825                (message_4.id, Role::User, 4..5),
2826                (message_3.id, Role::User, 5..5),
2827            ]
2828        );
2829
2830        buffer.update(cx, |buffer, cx| {
2831            buffer.edit([(4..4, "C"), (5..5, "D")], None, cx)
2832        });
2833        assert_eq!(
2834            messages(&context, cx),
2835            vec![
2836                (message_1.id, Role::User, 0..2),
2837                (message_2.id, Role::Assistant, 2..4),
2838                (message_4.id, Role::User, 4..6),
2839                (message_3.id, Role::User, 6..7),
2840            ]
2841        );
2842
2843        // Deleting across message boundaries merges the messages.
2844        buffer.update(cx, |buffer, cx| buffer.edit([(1..4, "")], None, cx));
2845        assert_eq!(
2846            messages(&context, cx),
2847            vec![
2848                (message_1.id, Role::User, 0..3),
2849                (message_3.id, Role::User, 3..4),
2850            ]
2851        );
2852
2853        // Undoing the deletion should also undo the merge.
2854        buffer.update(cx, |buffer, cx| buffer.undo(cx));
2855        assert_eq!(
2856            messages(&context, cx),
2857            vec![
2858                (message_1.id, Role::User, 0..2),
2859                (message_2.id, Role::Assistant, 2..4),
2860                (message_4.id, Role::User, 4..6),
2861                (message_3.id, Role::User, 6..7),
2862            ]
2863        );
2864
2865        // Redoing the deletion should also redo the merge.
2866        buffer.update(cx, |buffer, cx| buffer.redo(cx));
2867        assert_eq!(
2868            messages(&context, cx),
2869            vec![
2870                (message_1.id, Role::User, 0..3),
2871                (message_3.id, Role::User, 3..4),
2872            ]
2873        );
2874
2875        // Ensure we can still insert after a merged message.
2876        let message_5 = context.update(cx, |context, cx| {
2877            context
2878                .insert_message_after(message_1.id, Role::System, MessageStatus::Done, cx)
2879                .unwrap()
2880        });
2881        assert_eq!(
2882            messages(&context, cx),
2883            vec![
2884                (message_1.id, Role::User, 0..3),
2885                (message_5.id, Role::System, 3..4),
2886                (message_3.id, Role::User, 4..5)
2887            ]
2888        );
2889    }
2890
2891    #[gpui::test]
2892    fn test_message_splitting(cx: &mut AppContext) {
2893        let settings_store = SettingsStore::test(cx);
2894        cx.set_global(settings_store);
2895        LanguageModelRegistry::test(cx);
2896        assistant_panel::init(cx);
2897        let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
2898
2899        let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
2900        let context =
2901            cx.new_model(|cx| Context::local(registry, None, None, prompt_builder.clone(), cx));
2902        let buffer = context.read(cx).buffer.clone();
2903
2904        let message_1 = context.read(cx).message_anchors[0].clone();
2905        assert_eq!(
2906            messages(&context, cx),
2907            vec![(message_1.id, Role::User, 0..0)]
2908        );
2909
2910        buffer.update(cx, |buffer, cx| {
2911            buffer.edit([(0..0, "aaa\nbbb\nccc\nddd\n")], None, cx)
2912        });
2913
2914        let (_, message_2) = context.update(cx, |context, cx| context.split_message(3..3, cx));
2915        let message_2 = message_2.unwrap();
2916
2917        // We recycle newlines in the middle of a split message
2918        assert_eq!(buffer.read(cx).text(), "aaa\nbbb\nccc\nddd\n");
2919        assert_eq!(
2920            messages(&context, cx),
2921            vec![
2922                (message_1.id, Role::User, 0..4),
2923                (message_2.id, Role::User, 4..16),
2924            ]
2925        );
2926
2927        let (_, message_3) = context.update(cx, |context, cx| context.split_message(3..3, cx));
2928        let message_3 = message_3.unwrap();
2929
2930        // We don't recycle newlines at the end of a split message
2931        assert_eq!(buffer.read(cx).text(), "aaa\n\nbbb\nccc\nddd\n");
2932        assert_eq!(
2933            messages(&context, cx),
2934            vec![
2935                (message_1.id, Role::User, 0..4),
2936                (message_3.id, Role::User, 4..5),
2937                (message_2.id, Role::User, 5..17),
2938            ]
2939        );
2940
2941        let (_, message_4) = context.update(cx, |context, cx| context.split_message(9..9, cx));
2942        let message_4 = message_4.unwrap();
2943        assert_eq!(buffer.read(cx).text(), "aaa\n\nbbb\nccc\nddd\n");
2944        assert_eq!(
2945            messages(&context, cx),
2946            vec![
2947                (message_1.id, Role::User, 0..4),
2948                (message_3.id, Role::User, 4..5),
2949                (message_2.id, Role::User, 5..9),
2950                (message_4.id, Role::User, 9..17),
2951            ]
2952        );
2953
2954        let (_, message_5) = context.update(cx, |context, cx| context.split_message(9..9, cx));
2955        let message_5 = message_5.unwrap();
2956        assert_eq!(buffer.read(cx).text(), "aaa\n\nbbb\n\nccc\nddd\n");
2957        assert_eq!(
2958            messages(&context, cx),
2959            vec![
2960                (message_1.id, Role::User, 0..4),
2961                (message_3.id, Role::User, 4..5),
2962                (message_2.id, Role::User, 5..9),
2963                (message_4.id, Role::User, 9..10),
2964                (message_5.id, Role::User, 10..18),
2965            ]
2966        );
2967
2968        let (message_6, message_7) =
2969            context.update(cx, |context, cx| context.split_message(14..16, cx));
2970        let message_6 = message_6.unwrap();
2971        let message_7 = message_7.unwrap();
2972        assert_eq!(buffer.read(cx).text(), "aaa\n\nbbb\n\nccc\ndd\nd\n");
2973        assert_eq!(
2974            messages(&context, cx),
2975            vec![
2976                (message_1.id, Role::User, 0..4),
2977                (message_3.id, Role::User, 4..5),
2978                (message_2.id, Role::User, 5..9),
2979                (message_4.id, Role::User, 9..10),
2980                (message_5.id, Role::User, 10..14),
2981                (message_6.id, Role::User, 14..17),
2982                (message_7.id, Role::User, 17..19),
2983            ]
2984        );
2985    }
2986
2987    #[gpui::test]
2988    fn test_messages_for_offsets(cx: &mut AppContext) {
2989        let settings_store = SettingsStore::test(cx);
2990        LanguageModelRegistry::test(cx);
2991        cx.set_global(settings_store);
2992        assistant_panel::init(cx);
2993        let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
2994        let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
2995        let context =
2996            cx.new_model(|cx| Context::local(registry, None, None, prompt_builder.clone(), cx));
2997        let buffer = context.read(cx).buffer.clone();
2998
2999        let message_1 = context.read(cx).message_anchors[0].clone();
3000        assert_eq!(
3001            messages(&context, cx),
3002            vec![(message_1.id, Role::User, 0..0)]
3003        );
3004
3005        buffer.update(cx, |buffer, cx| buffer.edit([(0..0, "aaa")], None, cx));
3006        let message_2 = context
3007            .update(cx, |context, cx| {
3008                context.insert_message_after(message_1.id, Role::User, MessageStatus::Done, cx)
3009            })
3010            .unwrap();
3011        buffer.update(cx, |buffer, cx| buffer.edit([(4..4, "bbb")], None, cx));
3012
3013        let message_3 = context
3014            .update(cx, |context, cx| {
3015                context.insert_message_after(message_2.id, Role::User, MessageStatus::Done, cx)
3016            })
3017            .unwrap();
3018        buffer.update(cx, |buffer, cx| buffer.edit([(8..8, "ccc")], None, cx));
3019
3020        assert_eq!(buffer.read(cx).text(), "aaa\nbbb\nccc");
3021        assert_eq!(
3022            messages(&context, cx),
3023            vec![
3024                (message_1.id, Role::User, 0..4),
3025                (message_2.id, Role::User, 4..8),
3026                (message_3.id, Role::User, 8..11)
3027            ]
3028        );
3029
3030        assert_eq!(
3031            message_ids_for_offsets(&context, &[0, 4, 9], cx),
3032            [message_1.id, message_2.id, message_3.id]
3033        );
3034        assert_eq!(
3035            message_ids_for_offsets(&context, &[0, 1, 11], cx),
3036            [message_1.id, message_3.id]
3037        );
3038
3039        let message_4 = context
3040            .update(cx, |context, cx| {
3041                context.insert_message_after(message_3.id, Role::User, MessageStatus::Done, cx)
3042            })
3043            .unwrap();
3044        assert_eq!(buffer.read(cx).text(), "aaa\nbbb\nccc\n");
3045        assert_eq!(
3046            messages(&context, cx),
3047            vec![
3048                (message_1.id, Role::User, 0..4),
3049                (message_2.id, Role::User, 4..8),
3050                (message_3.id, Role::User, 8..12),
3051                (message_4.id, Role::User, 12..12)
3052            ]
3053        );
3054        assert_eq!(
3055            message_ids_for_offsets(&context, &[0, 4, 8, 12], cx),
3056            [message_1.id, message_2.id, message_3.id, message_4.id]
3057        );
3058
3059        fn message_ids_for_offsets(
3060            context: &Model<Context>,
3061            offsets: &[usize],
3062            cx: &AppContext,
3063        ) -> Vec<MessageId> {
3064            context
3065                .read(cx)
3066                .messages_for_offsets(offsets.iter().copied(), cx)
3067                .into_iter()
3068                .map(|message| message.id)
3069                .collect()
3070        }
3071    }
3072
3073    #[gpui::test]
3074    async fn test_slash_commands(cx: &mut TestAppContext) {
3075        let settings_store = cx.update(SettingsStore::test);
3076        cx.set_global(settings_store);
3077        cx.update(LanguageModelRegistry::test);
3078        cx.update(Project::init_settings);
3079        cx.update(assistant_panel::init);
3080        let fs = FakeFs::new(cx.background_executor.clone());
3081
3082        fs.insert_tree(
3083            "/test",
3084            json!({
3085                "src": {
3086                    "lib.rs": "fn one() -> usize { 1 }",
3087                    "main.rs": "
3088                        use crate::one;
3089                        fn main() { one(); }
3090                    ".unindent(),
3091                }
3092            }),
3093        )
3094        .await;
3095
3096        let slash_command_registry = cx.update(SlashCommandRegistry::default_global);
3097        slash_command_registry.register_command(file_command::FileSlashCommand, false);
3098
3099        let registry = Arc::new(LanguageRegistry::test(cx.executor()));
3100        let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
3101        let context = cx.new_model(|cx| {
3102            Context::local(registry.clone(), None, None, prompt_builder.clone(), cx)
3103        });
3104
3105        let output_ranges = Rc::new(RefCell::new(HashSet::default()));
3106        context.update(cx, |_, cx| {
3107            cx.subscribe(&context, {
3108                let ranges = output_ranges.clone();
3109                move |_, _, event, _| match event {
3110                    ContextEvent::PendingSlashCommandsUpdated { removed, updated } => {
3111                        for range in removed {
3112                            ranges.borrow_mut().remove(range);
3113                        }
3114                        for command in updated {
3115                            ranges.borrow_mut().insert(command.source_range.clone());
3116                        }
3117                    }
3118                    _ => {}
3119                }
3120            })
3121            .detach();
3122        });
3123
3124        let buffer = context.read_with(cx, |context, _| context.buffer.clone());
3125
3126        // Insert a slash command
3127        buffer.update(cx, |buffer, cx| {
3128            buffer.edit([(0..0, "/file src/lib.rs")], None, cx);
3129        });
3130        assert_text_and_output_ranges(
3131            &buffer,
3132            &output_ranges.borrow(),
3133            "
3134            «/file src/lib.rs»
3135            "
3136            .unindent()
3137            .trim_end(),
3138            cx,
3139        );
3140
3141        // Edit the argument of the slash command.
3142        buffer.update(cx, |buffer, cx| {
3143            let edit_offset = buffer.text().find("lib.rs").unwrap();
3144            buffer.edit([(edit_offset..edit_offset + "lib".len(), "main")], None, cx);
3145        });
3146        assert_text_and_output_ranges(
3147            &buffer,
3148            &output_ranges.borrow(),
3149            "
3150            «/file src/main.rs»
3151            "
3152            .unindent()
3153            .trim_end(),
3154            cx,
3155        );
3156
3157        // Edit the name of the slash command, using one that doesn't exist.
3158        buffer.update(cx, |buffer, cx| {
3159            let edit_offset = buffer.text().find("/file").unwrap();
3160            buffer.edit(
3161                [(edit_offset..edit_offset + "/file".len(), "/unknown")],
3162                None,
3163                cx,
3164            );
3165        });
3166        assert_text_and_output_ranges(
3167            &buffer,
3168            &output_ranges.borrow(),
3169            "
3170            /unknown src/main.rs
3171            "
3172            .unindent()
3173            .trim_end(),
3174            cx,
3175        );
3176
3177        #[track_caller]
3178        fn assert_text_and_output_ranges(
3179            buffer: &Model<Buffer>,
3180            ranges: &HashSet<Range<language::Anchor>>,
3181            expected_marked_text: &str,
3182            cx: &mut TestAppContext,
3183        ) {
3184            let (expected_text, expected_ranges) = marked_text_ranges(expected_marked_text, false);
3185            let (actual_text, actual_ranges) = buffer.update(cx, |buffer, _| {
3186                let mut ranges = ranges
3187                    .iter()
3188                    .map(|range| range.to_offset(buffer))
3189                    .collect::<Vec<_>>();
3190                ranges.sort_by_key(|a| a.start);
3191                (buffer.text(), ranges)
3192            });
3193
3194            assert_eq!(actual_text, expected_text);
3195            assert_eq!(actual_ranges, expected_ranges);
3196        }
3197    }
3198
3199    #[gpui::test]
3200    async fn test_edit_step_parsing(cx: &mut TestAppContext) {
3201        cx.update(prompt_library::init);
3202        let settings_store = cx.update(SettingsStore::test);
3203        cx.set_global(settings_store);
3204        cx.update(Project::init_settings);
3205        let fs = FakeFs::new(cx.executor());
3206        fs.as_fake()
3207            .insert_tree(
3208                "/root",
3209                json!({
3210                    "hello.rs": r#"
3211                    fn hello() {
3212                        println!("Hello, World!");
3213                    }
3214                "#.unindent()
3215                }),
3216            )
3217            .await;
3218        let project = Project::test(fs, [Path::new("/root")], cx).await;
3219        cx.update(LanguageModelRegistry::test);
3220
3221        let model = cx.read(|cx| {
3222            LanguageModelRegistry::read_global(cx)
3223                .active_model()
3224                .unwrap()
3225        });
3226        cx.update(assistant_panel::init);
3227        let registry = Arc::new(LanguageRegistry::test(cx.executor()));
3228
3229        // Create a new context
3230        let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
3231        let context = cx.new_model(|cx| {
3232            Context::local(
3233                registry.clone(),
3234                Some(project),
3235                None,
3236                prompt_builder.clone(),
3237                cx,
3238            )
3239        });
3240        let buffer = context.read_with(cx, |context, _| context.buffer.clone());
3241
3242        // Simulate user input
3243        let user_message = indoc! {r#"
3244            Please add unnecessary complexity to this code:
3245
3246            ```hello.rs
3247            fn main() {
3248                println!("Hello, World!");
3249            }
3250            ```
3251        "#};
3252        buffer.update(cx, |buffer, cx| {
3253            buffer.edit([(0..0, user_message)], None, cx);
3254        });
3255
3256        // Simulate LLM response with edit steps
3257        let llm_response = indoc! {r#"
3258            Sure, I can help you with that. Here's a step-by-step process:
3259
3260            <step>
3261            First, let's extract the greeting into a separate function:
3262
3263            ```rust
3264            fn greet() {
3265                println!("Hello, World!");
3266            }
3267
3268            fn main() {
3269                greet();
3270            }
3271            ```
3272            </step>
3273
3274            <step>
3275            Now, let's make the greeting customizable:
3276
3277            ```rust
3278            fn greet(name: &str) {
3279                println!("Hello, {}!", name);
3280            }
3281
3282            fn main() {
3283                greet("World");
3284            }
3285            ```
3286            </step>
3287
3288            These changes make the code more modular and flexible.
3289        "#};
3290
3291        // Simulate the assist method to trigger the LLM response
3292        context.update(cx, |context, cx| context.assist(cx));
3293        cx.run_until_parked();
3294
3295        // Retrieve the assistant response message's start from the context
3296        let response_start_row = context.read_with(cx, |context, cx| {
3297            let buffer = context.buffer.read(cx);
3298            context.message_anchors[1].start.to_point(buffer).row
3299        });
3300
3301        // Simulate the LLM completion
3302        model
3303            .as_fake()
3304            .stream_last_completion_response(llm_response.to_string());
3305        model.as_fake().end_last_completion_stream();
3306
3307        // Wait for the completion to be processed
3308        cx.run_until_parked();
3309
3310        // Verify that the edit steps were parsed correctly
3311        context.read_with(cx, |context, cx| {
3312            assert_eq!(
3313                workflow_steps(context, cx),
3314                vec![
3315                    (
3316                        Point::new(response_start_row + 2, 0)
3317                            ..Point::new(response_start_row + 12, 3),
3318                        WorkflowStepTestStatus::Pending
3319                    ),
3320                    (
3321                        Point::new(response_start_row + 14, 0)
3322                            ..Point::new(response_start_row + 24, 3),
3323                        WorkflowStepTestStatus::Pending
3324                    ),
3325                ]
3326            );
3327        });
3328
3329        model
3330            .as_fake()
3331            .respond_to_last_tool_use(Ok(serde_json::to_value(tool::WorkflowStepResolution {
3332                step_title: "Title".into(),
3333                suggestions: vec![tool::WorkflowSuggestion {
3334                    path: "/root/hello.rs".into(),
3335                    // Simulate a symbol name that's slightly different than our outline query
3336                    kind: tool::WorkflowSuggestionKind::Update {
3337                        symbol: "fn main()".into(),
3338                        description: "Extract a greeting function".into(),
3339                    },
3340                }],
3341            })
3342            .unwrap()));
3343
3344        // Wait for tool use to be processed.
3345        cx.run_until_parked();
3346
3347        // Verify that the first edit step is not pending anymore.
3348        context.read_with(cx, |context, cx| {
3349            assert_eq!(
3350                workflow_steps(context, cx),
3351                vec![
3352                    (
3353                        Point::new(response_start_row + 2, 0)
3354                            ..Point::new(response_start_row + 12, 3),
3355                        WorkflowStepTestStatus::Resolved
3356                    ),
3357                    (
3358                        Point::new(response_start_row + 14, 0)
3359                            ..Point::new(response_start_row + 24, 3),
3360                        WorkflowStepTestStatus::Pending
3361                    ),
3362                ]
3363            );
3364        });
3365
3366        #[derive(Copy, Clone, Debug, Eq, PartialEq)]
3367        enum WorkflowStepTestStatus {
3368            Pending,
3369            Resolved,
3370            Error,
3371        }
3372
3373        fn workflow_steps(
3374            context: &Context,
3375            cx: &AppContext,
3376        ) -> Vec<(Range<Point>, WorkflowStepTestStatus)> {
3377            context
3378                .workflow_steps
3379                .iter()
3380                .map(|step| {
3381                    let buffer = context.buffer.read(cx);
3382                    let status = match &step.status {
3383                        WorkflowStepStatus::Pending(_) => WorkflowStepTestStatus::Pending,
3384                        WorkflowStepStatus::Resolved { .. } => WorkflowStepTestStatus::Resolved,
3385                        WorkflowStepStatus::Error(_) => WorkflowStepTestStatus::Error,
3386                    };
3387                    (step.tagged_range.to_point(buffer), status)
3388                })
3389                .collect()
3390        }
3391    }
3392
3393    #[gpui::test]
3394    async fn test_serialization(cx: &mut TestAppContext) {
3395        let settings_store = cx.update(SettingsStore::test);
3396        cx.set_global(settings_store);
3397        cx.update(LanguageModelRegistry::test);
3398        cx.update(assistant_panel::init);
3399        let registry = Arc::new(LanguageRegistry::test(cx.executor()));
3400        let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
3401        let context = cx.new_model(|cx| {
3402            Context::local(registry.clone(), None, None, prompt_builder.clone(), cx)
3403        });
3404        let buffer = context.read_with(cx, |context, _| context.buffer.clone());
3405        let message_0 = context.read_with(cx, |context, _| context.message_anchors[0].id);
3406        let message_1 = context.update(cx, |context, cx| {
3407            context
3408                .insert_message_after(message_0, Role::Assistant, MessageStatus::Done, cx)
3409                .unwrap()
3410        });
3411        let message_2 = context.update(cx, |context, cx| {
3412            context
3413                .insert_message_after(message_1.id, Role::System, MessageStatus::Done, cx)
3414                .unwrap()
3415        });
3416        buffer.update(cx, |buffer, cx| {
3417            buffer.edit([(0..0, "a"), (1..1, "b\nc")], None, cx);
3418            buffer.finalize_last_transaction();
3419        });
3420        let _message_3 = context.update(cx, |context, cx| {
3421            context
3422                .insert_message_after(message_2.id, Role::System, MessageStatus::Done, cx)
3423                .unwrap()
3424        });
3425        buffer.update(cx, |buffer, cx| buffer.undo(cx));
3426        assert_eq!(buffer.read_with(cx, |buffer, _| buffer.text()), "a\nb\nc\n");
3427        assert_eq!(
3428            cx.read(|cx| messages(&context, cx)),
3429            [
3430                (message_0, Role::User, 0..2),
3431                (message_1.id, Role::Assistant, 2..6),
3432                (message_2.id, Role::System, 6..6),
3433            ]
3434        );
3435
3436        let serialized_context = context.read_with(cx, |context, cx| context.serialize(cx));
3437        let deserialized_context = cx.new_model(|cx| {
3438            Context::deserialize(
3439                serialized_context,
3440                Default::default(),
3441                registry.clone(),
3442                prompt_builder.clone(),
3443                None,
3444                None,
3445                cx,
3446            )
3447        });
3448        let deserialized_buffer =
3449            deserialized_context.read_with(cx, |context, _| context.buffer.clone());
3450        assert_eq!(
3451            deserialized_buffer.read_with(cx, |buffer, _| buffer.text()),
3452            "a\nb\nc\n"
3453        );
3454        assert_eq!(
3455            cx.read(|cx| messages(&deserialized_context, cx)),
3456            [
3457                (message_0, Role::User, 0..2),
3458                (message_1.id, Role::Assistant, 2..6),
3459                (message_2.id, Role::System, 6..6),
3460            ]
3461        );
3462    }
3463
3464    #[gpui::test(iterations = 100)]
3465    async fn test_random_context_collaboration(cx: &mut TestAppContext, mut rng: StdRng) {
3466        let min_peers = env::var("MIN_PEERS")
3467            .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
3468            .unwrap_or(2);
3469        let max_peers = env::var("MAX_PEERS")
3470            .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
3471            .unwrap_or(5);
3472        let operations = env::var("OPERATIONS")
3473            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
3474            .unwrap_or(50);
3475
3476        let settings_store = cx.update(SettingsStore::test);
3477        cx.set_global(settings_store);
3478        cx.update(LanguageModelRegistry::test);
3479
3480        cx.update(assistant_panel::init);
3481        let slash_commands = cx.update(SlashCommandRegistry::default_global);
3482        slash_commands.register_command(FakeSlashCommand("cmd-1".into()), false);
3483        slash_commands.register_command(FakeSlashCommand("cmd-2".into()), false);
3484        slash_commands.register_command(FakeSlashCommand("cmd-3".into()), false);
3485
3486        let registry = Arc::new(LanguageRegistry::test(cx.background_executor.clone()));
3487        let network = Arc::new(Mutex::new(Network::new(rng.clone())));
3488        let mut contexts = Vec::new();
3489
3490        let num_peers = rng.gen_range(min_peers..=max_peers);
3491        let context_id = ContextId::new();
3492        let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
3493        for i in 0..num_peers {
3494            let context = cx.new_model(|cx| {
3495                Context::new(
3496                    context_id.clone(),
3497                    i as ReplicaId,
3498                    language::Capability::ReadWrite,
3499                    registry.clone(),
3500                    prompt_builder.clone(),
3501                    None,
3502                    None,
3503                    cx,
3504                )
3505            });
3506
3507            cx.update(|cx| {
3508                cx.subscribe(&context, {
3509                    let network = network.clone();
3510                    move |_, event, _| {
3511                        if let ContextEvent::Operation(op) = event {
3512                            network
3513                                .lock()
3514                                .broadcast(i as ReplicaId, vec![op.to_proto()]);
3515                        }
3516                    }
3517                })
3518                .detach();
3519            });
3520
3521            contexts.push(context);
3522            network.lock().add_peer(i as ReplicaId);
3523        }
3524
3525        let mut mutation_count = operations;
3526
3527        while mutation_count > 0
3528            || !network.lock().is_idle()
3529            || network.lock().contains_disconnected_peers()
3530        {
3531            let context_index = rng.gen_range(0..contexts.len());
3532            let context = &contexts[context_index];
3533
3534            match rng.gen_range(0..100) {
3535                0..=29 if mutation_count > 0 => {
3536                    log::info!("Context {}: edit buffer", context_index);
3537                    context.update(cx, |context, cx| {
3538                        context
3539                            .buffer
3540                            .update(cx, |buffer, cx| buffer.randomly_edit(&mut rng, 1, cx));
3541                    });
3542                    mutation_count -= 1;
3543                }
3544                30..=44 if mutation_count > 0 => {
3545                    context.update(cx, |context, cx| {
3546                        let range = context.buffer.read(cx).random_byte_range(0, &mut rng);
3547                        log::info!("Context {}: split message at {:?}", context_index, range);
3548                        context.split_message(range, cx);
3549                    });
3550                    mutation_count -= 1;
3551                }
3552                45..=59 if mutation_count > 0 => {
3553                    context.update(cx, |context, cx| {
3554                        if let Some(message) = context.messages(cx).choose(&mut rng) {
3555                            let role = *[Role::User, Role::Assistant, Role::System]
3556                                .choose(&mut rng)
3557                                .unwrap();
3558                            log::info!(
3559                                "Context {}: insert message after {:?} with {:?}",
3560                                context_index,
3561                                message.id,
3562                                role
3563                            );
3564                            context.insert_message_after(message.id, role, MessageStatus::Done, cx);
3565                        }
3566                    });
3567                    mutation_count -= 1;
3568                }
3569                60..=74 if mutation_count > 0 => {
3570                    context.update(cx, |context, cx| {
3571                        let command_text = "/".to_string()
3572                            + slash_commands
3573                                .command_names()
3574                                .choose(&mut rng)
3575                                .unwrap()
3576                                .clone()
3577                                .as_ref();
3578
3579                        let command_range = context.buffer.update(cx, |buffer, cx| {
3580                            let offset = buffer.random_byte_range(0, &mut rng).start;
3581                            buffer.edit(
3582                                [(offset..offset, format!("\n{}\n", command_text))],
3583                                None,
3584                                cx,
3585                            );
3586                            offset + 1..offset + 1 + command_text.len()
3587                        });
3588
3589                        let output_len = rng.gen_range(1..=10);
3590                        let output_text = RandomCharIter::new(&mut rng)
3591                            .filter(|c| *c != '\r')
3592                            .take(output_len)
3593                            .collect::<String>();
3594
3595                        let num_sections = rng.gen_range(0..=3);
3596                        let mut sections = Vec::with_capacity(num_sections);
3597                        for _ in 0..num_sections {
3598                            let section_start = rng.gen_range(0..output_len);
3599                            let section_end = rng.gen_range(section_start..=output_len);
3600                            sections.push(SlashCommandOutputSection {
3601                                range: section_start..section_end,
3602                                icon: ui::IconName::Ai,
3603                                label: "section".into(),
3604                            });
3605                        }
3606
3607                        log::info!(
3608                            "Context {}: insert slash command output at {:?} with {:?}",
3609                            context_index,
3610                            command_range,
3611                            sections
3612                        );
3613
3614                        let command_range =
3615                            context.buffer.read(cx).anchor_after(command_range.start)
3616                                ..context.buffer.read(cx).anchor_after(command_range.end);
3617                        context.insert_command_output(
3618                            command_range,
3619                            Task::ready(Ok(SlashCommandOutput {
3620                                text: output_text,
3621                                sections,
3622                                run_commands_in_text: false,
3623                            })),
3624                            true,
3625                            cx,
3626                        );
3627                    });
3628                    cx.run_until_parked();
3629                    mutation_count -= 1;
3630                }
3631                75..=84 if mutation_count > 0 => {
3632                    context.update(cx, |context, cx| {
3633                        if let Some(message) = context.messages(cx).choose(&mut rng) {
3634                            let new_status = match rng.gen_range(0..3) {
3635                                0 => MessageStatus::Done,
3636                                1 => MessageStatus::Pending,
3637                                _ => MessageStatus::Error(SharedString::from("Random error")),
3638                            };
3639                            log::info!(
3640                                "Context {}: update message {:?} status to {:?}",
3641                                context_index,
3642                                message.id,
3643                                new_status
3644                            );
3645                            context.update_metadata(message.id, cx, |metadata| {
3646                                metadata.status = new_status;
3647                            });
3648                        }
3649                    });
3650                    mutation_count -= 1;
3651                }
3652                _ => {
3653                    let replica_id = context_index as ReplicaId;
3654                    if network.lock().is_disconnected(replica_id) {
3655                        network.lock().reconnect_peer(replica_id, 0);
3656
3657                        let (ops_to_send, ops_to_receive) = cx.read(|cx| {
3658                            let host_context = &contexts[0].read(cx);
3659                            let guest_context = context.read(cx);
3660                            (
3661                                guest_context.serialize_ops(&host_context.version(cx), cx),
3662                                host_context.serialize_ops(&guest_context.version(cx), cx),
3663                            )
3664                        });
3665                        let ops_to_send = ops_to_send.await;
3666                        let ops_to_receive = ops_to_receive
3667                            .await
3668                            .into_iter()
3669                            .map(ContextOperation::from_proto)
3670                            .collect::<Result<Vec<_>>>()
3671                            .unwrap();
3672                        log::info!(
3673                            "Context {}: reconnecting. Sent {} operations, received {} operations",
3674                            context_index,
3675                            ops_to_send.len(),
3676                            ops_to_receive.len()
3677                        );
3678
3679                        network.lock().broadcast(replica_id, ops_to_send);
3680                        context
3681                            .update(cx, |context, cx| context.apply_ops(ops_to_receive, cx))
3682                            .unwrap();
3683                    } else if rng.gen_bool(0.1) && replica_id != 0 {
3684                        log::info!("Context {}: disconnecting", context_index);
3685                        network.lock().disconnect_peer(replica_id);
3686                    } else if network.lock().has_unreceived(replica_id) {
3687                        log::info!("Context {}: applying operations", context_index);
3688                        let ops = network.lock().receive(replica_id);
3689                        let ops = ops
3690                            .into_iter()
3691                            .map(ContextOperation::from_proto)
3692                            .collect::<Result<Vec<_>>>()
3693                            .unwrap();
3694                        context
3695                            .update(cx, |context, cx| context.apply_ops(ops, cx))
3696                            .unwrap();
3697                    }
3698                }
3699            }
3700        }
3701
3702        cx.read(|cx| {
3703            let first_context = contexts[0].read(cx);
3704            for context in &contexts[1..] {
3705                let context = context.read(cx);
3706                assert!(context.pending_ops.is_empty());
3707                assert_eq!(
3708                    context.buffer.read(cx).text(),
3709                    first_context.buffer.read(cx).text(),
3710                    "Context {} text != Context 0 text",
3711                    context.buffer.read(cx).replica_id()
3712                );
3713                assert_eq!(
3714                    context.message_anchors,
3715                    first_context.message_anchors,
3716                    "Context {} messages != Context 0 messages",
3717                    context.buffer.read(cx).replica_id()
3718                );
3719                assert_eq!(
3720                    context.messages_metadata,
3721                    first_context.messages_metadata,
3722                    "Context {} message metadata != Context 0 message metadata",
3723                    context.buffer.read(cx).replica_id()
3724                );
3725                assert_eq!(
3726                    context.slash_command_output_sections,
3727                    first_context.slash_command_output_sections,
3728                    "Context {} slash command output sections != Context 0 slash command output sections",
3729                    context.buffer.read(cx).replica_id()
3730                );
3731            }
3732        });
3733    }
3734
3735    fn messages(context: &Model<Context>, cx: &AppContext) -> Vec<(MessageId, Role, Range<usize>)> {
3736        context
3737            .read(cx)
3738            .messages(cx)
3739            .map(|message| (message.id, message.role, message.offset_range))
3740            .collect()
3741    }
3742
3743    #[derive(Clone)]
3744    struct FakeSlashCommand(String);
3745
3746    impl SlashCommand for FakeSlashCommand {
3747        fn name(&self) -> String {
3748            self.0.clone()
3749        }
3750
3751        fn description(&self) -> String {
3752            format!("Fake slash command: {}", self.0)
3753        }
3754
3755        fn menu_text(&self) -> String {
3756            format!("Run fake command: {}", self.0)
3757        }
3758
3759        fn complete_argument(
3760            self: Arc<Self>,
3761            _query: String,
3762            _cancel: Arc<AtomicBool>,
3763            _workspace: Option<WeakView<Workspace>>,
3764            _cx: &mut WindowContext,
3765        ) -> Task<Result<Vec<ArgumentCompletion>>> {
3766            Task::ready(Ok(vec![]))
3767        }
3768
3769        fn requires_argument(&self) -> bool {
3770            false
3771        }
3772
3773        fn run(
3774            self: Arc<Self>,
3775            _argument: Option<&str>,
3776            _workspace: WeakView<Workspace>,
3777            _delegate: Option<Arc<dyn LspAdapterDelegate>>,
3778            _cx: &mut WindowContext,
3779        ) -> Task<Result<SlashCommandOutput>> {
3780            Task::ready(Ok(SlashCommandOutput {
3781                text: format!("Executed fake command: {}", self.0),
3782                sections: vec![],
3783                run_commands_in_text: false,
3784            }))
3785        }
3786    }
3787}
3788
3789mod tool {
3790    use gpui::AsyncAppContext;
3791
3792    use super::*;
3793
3794    #[derive(Debug, Serialize, Deserialize, JsonSchema)]
3795    pub struct WorkflowStepResolution {
3796        /// An extremely short title for the edit step represented by these operations.
3797        pub step_title: String,
3798        /// A sequence of operations to apply to the codebase.
3799        /// When multiple operations are required for a step, be sure to include multiple operations in this list.
3800        pub suggestions: Vec<WorkflowSuggestion>,
3801    }
3802
3803    impl LanguageModelTool for WorkflowStepResolution {
3804        fn name() -> String {
3805            "edit".into()
3806        }
3807
3808        fn description() -> String {
3809            "suggest edits to one or more locations in the codebase".into()
3810        }
3811    }
3812
3813    /// A description of an operation to apply to one location in the codebase.
3814    ///
3815    /// This object represents a single edit operation that can be performed on a specific file
3816    /// in the codebase. It encapsulates both the location (file path) and the nature of the
3817    /// edit to be made.
3818    ///
3819    /// # Fields
3820    ///
3821    /// * `path`: A string representing the file path where the edit operation should be applied.
3822    ///           This path is relative to the root of the project or repository.
3823    ///
3824    /// * `kind`: An enum representing the specific type of edit operation to be performed.
3825    ///
3826    /// # Usage
3827    ///
3828    /// `EditOperation` is used within a code editor to represent and apply
3829    /// programmatic changes to source code. It provides a structured way to describe
3830    /// edits for features like refactoring tools or AI-assisted coding suggestions.
3831    #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
3832    pub struct WorkflowSuggestion {
3833        /// The path to the file containing the relevant operation
3834        pub path: String,
3835        #[serde(flatten)]
3836        pub kind: WorkflowSuggestionKind,
3837    }
3838
3839    impl WorkflowSuggestion {
3840        pub(super) async fn resolve(
3841            &self,
3842            project: Model<Project>,
3843            mut cx: AsyncAppContext,
3844        ) -> Result<(Model<Buffer>, super::WorkflowSuggestion)> {
3845            let path = self.path.clone();
3846            let kind = self.kind.clone();
3847            let buffer = project
3848                .update(&mut cx, |project, cx| {
3849                    let project_path = project
3850                        .find_project_path(Path::new(&path), cx)
3851                        .with_context(|| format!("worktree not found for {:?}", path))?;
3852                    anyhow::Ok(project.open_buffer(project_path, cx))
3853                })??
3854                .await?;
3855
3856            let mut parse_status = buffer.read_with(&cx, |buffer, _cx| buffer.parse_status())?;
3857            while *parse_status.borrow() != ParseStatus::Idle {
3858                parse_status.changed().await?;
3859            }
3860
3861            let snapshot = buffer.update(&mut cx, |buffer, _| buffer.snapshot())?;
3862            let outline = snapshot.outline(None).context("no outline for buffer")?;
3863
3864            let suggestion;
3865            match kind {
3866                WorkflowSuggestionKind::Update {
3867                    symbol,
3868                    description,
3869                } => {
3870                    let symbol = outline
3871                        .find_most_similar(&symbol)
3872                        .with_context(|| format!("symbol not found: {:?}", symbol))?
3873                        .to_point(&snapshot);
3874                    let start = symbol
3875                        .annotation_range
3876                        .map_or(symbol.range.start, |range| range.start);
3877                    let start = Point::new(start.row, 0);
3878                    let end = Point::new(
3879                        symbol.range.end.row,
3880                        snapshot.line_len(symbol.range.end.row),
3881                    );
3882                    let range = snapshot.anchor_before(start)..snapshot.anchor_after(end);
3883                    suggestion = super::WorkflowSuggestion::Update { range, description };
3884                }
3885                WorkflowSuggestionKind::Create { description } => {
3886                    suggestion = super::WorkflowSuggestion::CreateFile { description };
3887                }
3888                WorkflowSuggestionKind::InsertSiblingBefore {
3889                    symbol,
3890                    description,
3891                } => {
3892                    let symbol = outline
3893                        .find_most_similar(&symbol)
3894                        .with_context(|| format!("symbol not found: {:?}", symbol))?
3895                        .to_point(&snapshot);
3896                    let position = snapshot.anchor_before(
3897                        symbol
3898                            .annotation_range
3899                            .map_or(symbol.range.start, |annotation_range| {
3900                                annotation_range.start
3901                            }),
3902                    );
3903                    suggestion = super::WorkflowSuggestion::InsertSiblingBefore {
3904                        position,
3905                        description,
3906                    };
3907                }
3908                WorkflowSuggestionKind::InsertSiblingAfter {
3909                    symbol,
3910                    description,
3911                } => {
3912                    let symbol = outline
3913                        .find_most_similar(&symbol)
3914                        .with_context(|| format!("symbol not found: {:?}", symbol))?
3915                        .to_point(&snapshot);
3916                    let position = snapshot.anchor_after(symbol.range.end);
3917                    suggestion = super::WorkflowSuggestion::InsertSiblingAfter {
3918                        position,
3919                        description,
3920                    };
3921                }
3922                WorkflowSuggestionKind::PrependChild {
3923                    symbol,
3924                    description,
3925                } => {
3926                    if let Some(symbol) = symbol {
3927                        let symbol = outline
3928                            .find_most_similar(&symbol)
3929                            .with_context(|| format!("symbol not found: {:?}", symbol))?
3930                            .to_point(&snapshot);
3931
3932                        let position = snapshot.anchor_after(
3933                            symbol
3934                                .body_range
3935                                .map_or(symbol.range.start, |body_range| body_range.start),
3936                        );
3937                        suggestion = super::WorkflowSuggestion::PrependChild {
3938                            position,
3939                            description,
3940                        };
3941                    } else {
3942                        suggestion = super::WorkflowSuggestion::PrependChild {
3943                            position: language::Anchor::MIN,
3944                            description,
3945                        };
3946                    }
3947                }
3948                WorkflowSuggestionKind::AppendChild {
3949                    symbol,
3950                    description,
3951                } => {
3952                    if let Some(symbol) = symbol {
3953                        let symbol = outline
3954                            .find_most_similar(&symbol)
3955                            .with_context(|| format!("symbol not found: {:?}", symbol))?
3956                            .to_point(&snapshot);
3957
3958                        let position = snapshot.anchor_before(
3959                            symbol
3960                                .body_range
3961                                .map_or(symbol.range.end, |body_range| body_range.end),
3962                        );
3963                        suggestion = super::WorkflowSuggestion::AppendChild {
3964                            position,
3965                            description,
3966                        };
3967                    } else {
3968                        suggestion = super::WorkflowSuggestion::PrependChild {
3969                            position: language::Anchor::MAX,
3970                            description,
3971                        };
3972                    }
3973                }
3974                WorkflowSuggestionKind::Delete { symbol } => {
3975                    let symbol = outline
3976                        .find_most_similar(&symbol)
3977                        .with_context(|| format!("symbol not found: {:?}", symbol))?
3978                        .to_point(&snapshot);
3979                    let start = symbol
3980                        .annotation_range
3981                        .map_or(symbol.range.start, |range| range.start);
3982                    let start = Point::new(start.row, 0);
3983                    let end = Point::new(
3984                        symbol.range.end.row,
3985                        snapshot.line_len(symbol.range.end.row),
3986                    );
3987                    let range = snapshot.anchor_before(start)..snapshot.anchor_after(end);
3988                    suggestion = super::WorkflowSuggestion::Delete { range };
3989                }
3990            }
3991
3992            Ok((buffer, suggestion))
3993        }
3994    }
3995
3996    #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
3997    #[serde(tag = "kind")]
3998    pub enum WorkflowSuggestionKind {
3999        /// Rewrites the specified symbol entirely based on the given description.
4000        /// This operation completely replaces the existing symbol with new content.
4001        Update {
4002            /// A fully-qualified reference to the symbol, e.g. `mod foo impl Bar pub fn baz` instead of just `fn baz`.
4003            /// The path should uniquely identify the symbol within the containing file.
4004            symbol: String,
4005            /// A brief description of the transformation to apply to the symbol.
4006            description: String,
4007        },
4008        /// Creates a new file with the given path based on the provided description.
4009        /// This operation adds a new file to the codebase.
4010        Create {
4011            /// A brief description of the file to be created.
4012            description: String,
4013        },
4014        /// Inserts a new symbol based on the given description before the specified symbol.
4015        /// This operation adds new content immediately preceding an existing symbol.
4016        InsertSiblingBefore {
4017            /// A fully-qualified reference to the symbol, e.g. `mod foo impl Bar pub fn baz` instead of just `fn baz`.
4018            /// The new content will be inserted immediately before this symbol.
4019            symbol: String,
4020            /// A brief description of the new symbol to be inserted.
4021            description: String,
4022        },
4023        /// Inserts a new symbol based on the given description after the specified symbol.
4024        /// This operation adds new content immediately following an existing symbol.
4025        InsertSiblingAfter {
4026            /// A fully-qualified reference to the symbol, e.g. `mod foo impl Bar pub fn baz` instead of just `fn baz`.
4027            /// The new content will be inserted immediately after this symbol.
4028            symbol: String,
4029            /// A brief description of the new symbol to be inserted.
4030            description: String,
4031        },
4032        /// Inserts a new symbol as a child of the specified symbol at the start.
4033        /// This operation adds new content as the first child of an existing symbol (or file if no symbol is provided).
4034        PrependChild {
4035            /// An optional fully-qualified reference to the symbol after the code you want to insert, e.g. `mod foo impl Bar pub fn baz` instead of just `fn baz`.
4036            /// If provided, the new content will be inserted as the first child of this symbol.
4037            /// If not provided, the new content will be inserted at the top of the file.
4038            symbol: Option<String>,
4039            /// A brief description of the new symbol to be inserted.
4040            description: String,
4041        },
4042        /// Inserts a new symbol as a child of the specified symbol at the end.
4043        /// This operation adds new content as the last child of an existing symbol (or file if no symbol is provided).
4044        AppendChild {
4045            /// An optional fully-qualified reference to the symbol before the code you want to insert, e.g. `mod foo impl Bar pub fn baz` instead of just `fn baz`.
4046            /// If provided, the new content will be inserted as the last child of this symbol.
4047            /// If not provided, the new content will be applied at the bottom of the file.
4048            symbol: Option<String>,
4049            /// A brief description of the new symbol to be inserted.
4050            description: String,
4051        },
4052        /// Deletes the specified symbol from the containing file.
4053        Delete {
4054            /// An fully-qualified reference to the symbol to be deleted, e.g. `mod foo impl Bar pub fn baz` instead of just `fn baz`.
4055            symbol: String,
4056        },
4057    }
4058
4059    impl WorkflowSuggestionKind {
4060        pub fn symbol(&self) -> Option<&str> {
4061            match self {
4062                Self::Update { symbol, .. } => Some(symbol),
4063                Self::InsertSiblingBefore { symbol, .. } => Some(symbol),
4064                Self::InsertSiblingAfter { symbol, .. } => Some(symbol),
4065                Self::PrependChild { symbol, .. } => symbol.as_deref(),
4066                Self::AppendChild { symbol, .. } => symbol.as_deref(),
4067                Self::Delete { symbol } => Some(symbol),
4068                Self::Create { .. } => None,
4069            }
4070        }
4071
4072        pub fn description(&self) -> Option<&str> {
4073            match self {
4074                Self::Update { description, .. } => Some(description),
4075                Self::Create { description } => Some(description),
4076                Self::InsertSiblingBefore { description, .. } => Some(description),
4077                Self::InsertSiblingAfter { description, .. } => Some(description),
4078                Self::PrependChild { description, .. } => Some(description),
4079                Self::AppendChild { description, .. } => Some(description),
4080                Self::Delete { .. } => None,
4081            }
4082        }
4083
4084        pub fn initial_insertion(&self) -> Option<InitialInsertion> {
4085            match self {
4086                WorkflowSuggestionKind::InsertSiblingBefore { .. } => {
4087                    Some(InitialInsertion::NewlineAfter)
4088                }
4089                WorkflowSuggestionKind::InsertSiblingAfter { .. } => {
4090                    Some(InitialInsertion::NewlineBefore)
4091                }
4092                WorkflowSuggestionKind::PrependChild { .. } => Some(InitialInsertion::NewlineAfter),
4093                WorkflowSuggestionKind::AppendChild { .. } => Some(InitialInsertion::NewlineBefore),
4094                _ => None,
4095            }
4096        }
4097    }
4098}