Restructure workflow step resolution and fix inserting newlines (#15720)
Release Notes: - N/A --------- Co-authored-by: Nathan <nathan@zed.dev>
This commit is contained in:
parent
49e736d8ef
commit
0ec29d6866
18 changed files with 1316 additions and 815 deletions
|
@ -9,10 +9,11 @@ use crate::{
|
|||
},
|
||||
terminal_inline_assistant::TerminalInlineAssistant,
|
||||
Assist, ConfirmCommand, Context, ContextEvent, ContextId, ContextStore, CycleMessageRole,
|
||||
DebugEditSteps, DeployHistory, DeployPromptLibrary, EditStep, EditStepState,
|
||||
EditStepSuggestions, InlineAssist, InlineAssistId, InlineAssistant, InsertIntoEditor,
|
||||
MessageStatus, ModelSelector, PendingSlashCommand, PendingSlashCommandStatus, QuoteSelection,
|
||||
RemoteContextMetadata, SavedContextMetadata, Split, ToggleFocus, ToggleModelSelector,
|
||||
DebugEditSteps, DeployHistory, DeployPromptLibrary, EditSuggestionGroup, InlineAssist,
|
||||
InlineAssistId, InlineAssistant, InsertIntoEditor, MessageStatus, ModelSelector,
|
||||
PendingSlashCommand, PendingSlashCommandStatus, QuoteSelection, RemoteContextMetadata,
|
||||
SavedContextMetadata, Split, ToggleFocus, ToggleModelSelector, WorkflowStep,
|
||||
WorkflowStepEditSuggestions,
|
||||
};
|
||||
use crate::{ContextStoreEvent, ShowConfiguration};
|
||||
use anyhow::{anyhow, Result};
|
||||
|
@ -39,7 +40,8 @@ use gpui::{
|
|||
};
|
||||
use indexed_docs::IndexedDocsStore;
|
||||
use language::{
|
||||
language_settings::SoftWrap, Capability, LanguageRegistry, LspAdapterDelegate, Point, ToOffset,
|
||||
language_settings::SoftWrap, Buffer, Capability, LanguageRegistry, LspAdapterDelegate, Point,
|
||||
ToOffset,
|
||||
};
|
||||
use language_model::{
|
||||
provider::cloud::PROVIDER_ID, LanguageModelProvider, LanguageModelProviderId,
|
||||
|
@ -1284,7 +1286,6 @@ struct ActiveEditStep {
|
|||
start: language::Anchor,
|
||||
assist_ids: Vec<InlineAssistId>,
|
||||
editor: Option<WeakView<Editor>>,
|
||||
_open_editor: Task<Result<()>>,
|
||||
}
|
||||
|
||||
pub struct ContextEditor {
|
||||
|
@ -1452,23 +1453,21 @@ impl ContextEditor {
|
|||
.read(cx)
|
||||
.buffer()
|
||||
.read(cx)
|
||||
.text_for_range(step.source_range.clone())
|
||||
.text_for_range(step.tagged_range.clone())
|
||||
.collect::<String>()
|
||||
));
|
||||
match &step.state {
|
||||
Some(EditStepState::Resolved(resolution)) => {
|
||||
match &step.edit_suggestions {
|
||||
WorkflowStepEditSuggestions::Resolved {
|
||||
title,
|
||||
edit_suggestions,
|
||||
} => {
|
||||
output.push_str("Resolution:\n");
|
||||
output.push_str(&format!(" {:?}\n", resolution.step_title));
|
||||
for op in &resolution.operations {
|
||||
output.push_str(&format!(" {:?}\n", op));
|
||||
}
|
||||
output.push_str(&format!(" {:?}\n", title));
|
||||
output.push_str(&format!(" {:?}\n", edit_suggestions));
|
||||
}
|
||||
Some(EditStepState::Pending(_)) => {
|
||||
WorkflowStepEditSuggestions::Pending(_) => {
|
||||
output.push_str("Resolution: Pending\n");
|
||||
}
|
||||
None => {
|
||||
output.push_str("Resolution: None\n");
|
||||
}
|
||||
}
|
||||
output.push('\n');
|
||||
}
|
||||
|
@ -1875,222 +1874,165 @@ impl ContextEditor {
|
|||
}
|
||||
EditorEvent::SelectionsChanged { .. } => {
|
||||
self.scroll_position = self.cursor_scroll_position(cx);
|
||||
if self
|
||||
.edit_step_for_cursor(cx)
|
||||
.map(|step| step.source_range.start)
|
||||
!= self.active_edit_step.as_ref().map(|step| step.start)
|
||||
{
|
||||
if let Some(old_active_edit_step) = self.active_edit_step.take() {
|
||||
if let Some(editor) = old_active_edit_step
|
||||
.editor
|
||||
.and_then(|editor| editor.upgrade())
|
||||
{
|
||||
self.workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
if let Some(pane) = workspace.pane_for(&editor) {
|
||||
pane.update(cx, |pane, cx| {
|
||||
let item_id = editor.entity_id();
|
||||
if pane.is_active_preview_item(item_id) {
|
||||
pane.close_item_by_id(
|
||||
item_id,
|
||||
SaveIntent::Skip,
|
||||
cx,
|
||||
)
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(new_active_step) = self.edit_step_for_cursor(cx) {
|
||||
let start = new_active_step.source_range.start;
|
||||
let open_editor = new_active_step
|
||||
.edit_suggestions(&self.project, cx)
|
||||
.map(|suggestions| {
|
||||
self.open_editor_for_edit_suggestions(suggestions, cx)
|
||||
})
|
||||
.unwrap_or_else(|| Task::ready(Ok(())));
|
||||
self.active_edit_step = Some(ActiveEditStep {
|
||||
start,
|
||||
assist_ids: Vec::new(),
|
||||
editor: None,
|
||||
_open_editor: open_editor,
|
||||
});
|
||||
}
|
||||
}
|
||||
self.update_active_workflow_step(cx);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
cx.emit(event.clone());
|
||||
}
|
||||
|
||||
fn open_editor_for_edit_suggestions(
|
||||
&mut self,
|
||||
edit_step_suggestions: Task<EditStepSuggestions>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
let workspace = self.workspace.clone();
|
||||
let project = self.project.clone();
|
||||
let assistant_panel = self.assistant_panel.clone();
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
let edit_step_suggestions = edit_step_suggestions.await;
|
||||
fn update_active_workflow_step(&mut self, cx: &mut ViewContext<Self>) {
|
||||
if self
|
||||
.workflow_step_for_cursor(cx)
|
||||
.map(|step| step.tagged_range.start)
|
||||
!= self.active_edit_step.as_ref().map(|step| step.start)
|
||||
{
|
||||
if let Some(old_active_edit_step) = self.active_edit_step.take() {
|
||||
if let Some(editor) = old_active_edit_step
|
||||
.editor
|
||||
.and_then(|editor| editor.upgrade())
|
||||
{
|
||||
self.workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
if let Some(pane) = workspace.pane_for(&editor) {
|
||||
pane.update(cx, |pane, cx| {
|
||||
let item_id = editor.entity_id();
|
||||
if pane.is_active_preview_item(item_id) {
|
||||
pane.close_item_by_id(item_id, SaveIntent::Skip, cx)
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
let mut assist_ids = Vec::new();
|
||||
let editor = if edit_step_suggestions.suggestions.is_empty() {
|
||||
return Ok(());
|
||||
} else if edit_step_suggestions.suggestions.len() == 1
|
||||
&& edit_step_suggestions
|
||||
.suggestions
|
||||
.values()
|
||||
.next()
|
||||
.unwrap()
|
||||
.len()
|
||||
== 1
|
||||
{
|
||||
// If there's only one buffer and one suggestion group, open it directly
|
||||
let (buffer, suggestion_groups) = edit_step_suggestions
|
||||
.suggestions
|
||||
.into_iter()
|
||||
.next()
|
||||
.unwrap();
|
||||
let suggestion_group = suggestion_groups.into_iter().next().unwrap();
|
||||
let editor = workspace.update(&mut cx, |workspace, cx| {
|
||||
if let Some(new_active_step) = self.workflow_step_for_cursor(cx) {
|
||||
let start = new_active_step.tagged_range.start;
|
||||
|
||||
let mut editor = None;
|
||||
let mut assist_ids = Vec::new();
|
||||
if let WorkflowStepEditSuggestions::Resolved {
|
||||
title,
|
||||
edit_suggestions,
|
||||
} = &new_active_step.edit_suggestions
|
||||
{
|
||||
if let Some((opened_editor, inline_assist_ids)) =
|
||||
self.suggest_edits(title.clone(), edit_suggestions.clone(), cx)
|
||||
{
|
||||
editor = Some(opened_editor.downgrade());
|
||||
assist_ids = inline_assist_ids;
|
||||
}
|
||||
}
|
||||
|
||||
self.active_edit_step = Some(ActiveEditStep {
|
||||
start,
|
||||
assist_ids,
|
||||
editor,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn suggest_edits(
|
||||
&mut self,
|
||||
title: String,
|
||||
edit_suggestions: HashMap<Model<Buffer>, Vec<EditSuggestionGroup>>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<(View<Editor>, Vec<InlineAssistId>)> {
|
||||
let assistant_panel = self.assistant_panel.upgrade()?;
|
||||
if edit_suggestions.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let editor;
|
||||
let mut suggestion_groups = Vec::new();
|
||||
if edit_suggestions.len() == 1 && edit_suggestions.values().next().unwrap().len() == 1 {
|
||||
// If there's only one buffer and one suggestion group, open it directly
|
||||
let (buffer, groups) = edit_suggestions.into_iter().next().unwrap();
|
||||
let group = groups.into_iter().next().unwrap();
|
||||
editor = self
|
||||
.workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
let active_pane = workspace.active_pane().clone();
|
||||
workspace.open_project_item::<Editor>(active_pane, buffer, false, false, cx)
|
||||
})?;
|
||||
})
|
||||
.log_err()?;
|
||||
|
||||
cx.update(|cx| {
|
||||
for suggestion in suggestion_group.suggestions {
|
||||
let description = suggestion.description.unwrap_or_else(|| "Delete".into());
|
||||
let (&excerpt_id, _, _) = editor
|
||||
.read(cx)
|
||||
.buffer()
|
||||
.read(cx)
|
||||
.read(cx)
|
||||
.as_singleton()
|
||||
.unwrap();
|
||||
|
||||
let range = {
|
||||
let multibuffer = editor.read(cx).buffer().read(cx).read(cx);
|
||||
let (&excerpt_id, _, _) = multibuffer.as_singleton().unwrap();
|
||||
multibuffer
|
||||
.anchor_in_excerpt(excerpt_id, suggestion.range.start)
|
||||
.unwrap()
|
||||
..multibuffer
|
||||
.anchor_in_excerpt(excerpt_id, suggestion.range.end)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
InlineAssistant::update_global(cx, |assistant, cx| {
|
||||
let suggestion_id = assistant.suggest_assist(
|
||||
&editor,
|
||||
range,
|
||||
description,
|
||||
suggestion.initial_insertion,
|
||||
Some(workspace.clone()),
|
||||
assistant_panel.upgrade().as_ref(),
|
||||
cx,
|
||||
);
|
||||
assist_ids.push(suggestion_id);
|
||||
});
|
||||
}
|
||||
|
||||
// Scroll the editor to the suggested assist
|
||||
editor.update(cx, |editor, cx| {
|
||||
let multibuffer = editor.buffer().read(cx).snapshot(cx);
|
||||
let (&excerpt_id, _, buffer) = multibuffer.as_singleton().unwrap();
|
||||
let anchor = if suggestion_group.context_range.start.to_offset(buffer) == 0
|
||||
{
|
||||
Anchor::min()
|
||||
} else {
|
||||
multibuffer
|
||||
.anchor_in_excerpt(excerpt_id, suggestion_group.context_range.start)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
editor.set_scroll_anchor(
|
||||
ScrollAnchor {
|
||||
offset: gpui::Point::default(),
|
||||
anchor,
|
||||
},
|
||||
cx,
|
||||
);
|
||||
});
|
||||
})?;
|
||||
|
||||
editor
|
||||
} else {
|
||||
// If there are multiple buffers or suggestion groups, create a multibuffer
|
||||
let mut inline_assist_suggestions = Vec::new();
|
||||
let multibuffer = cx.new_model(|cx| {
|
||||
let replica_id = project.read(cx).replica_id();
|
||||
let mut multibuffer = MultiBuffer::new(replica_id, Capability::ReadWrite)
|
||||
.with_title(edit_step_suggestions.title);
|
||||
for (buffer, suggestion_groups) in edit_step_suggestions.suggestions {
|
||||
let excerpt_ids = multibuffer.push_excerpts(
|
||||
buffer,
|
||||
suggestion_groups
|
||||
.iter()
|
||||
.map(|suggestion_group| ExcerptRange {
|
||||
context: suggestion_group.context_range.clone(),
|
||||
primary: None,
|
||||
}),
|
||||
cx,
|
||||
);
|
||||
|
||||
for (excerpt_id, suggestion_group) in
|
||||
excerpt_ids.into_iter().zip(suggestion_groups)
|
||||
{
|
||||
for suggestion in suggestion_group.suggestions {
|
||||
let description =
|
||||
suggestion.description.unwrap_or_else(|| "Delete".into());
|
||||
let range = {
|
||||
let multibuffer = multibuffer.read(cx);
|
||||
multibuffer
|
||||
.anchor_in_excerpt(excerpt_id, suggestion.range.start)
|
||||
.unwrap()
|
||||
..multibuffer
|
||||
.anchor_in_excerpt(excerpt_id, suggestion.range.end)
|
||||
.unwrap()
|
||||
};
|
||||
inline_assist_suggestions.push((
|
||||
range,
|
||||
description,
|
||||
suggestion.initial_insertion,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Scroll the editor to the suggested assist
|
||||
editor.update(cx, |editor, cx| {
|
||||
let multibuffer = editor.buffer().read(cx).snapshot(cx);
|
||||
let (&excerpt_id, _, buffer) = multibuffer.as_singleton().unwrap();
|
||||
let anchor = if group.context_range.start.to_offset(buffer) == 0 {
|
||||
Anchor::min()
|
||||
} else {
|
||||
multibuffer
|
||||
})?;
|
||||
.anchor_in_excerpt(excerpt_id, group.context_range.start)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let editor = cx
|
||||
.new_view(|cx| Editor::for_multibuffer(multibuffer, Some(project), true, cx))?;
|
||||
cx.update(|cx| {
|
||||
InlineAssistant::update_global(cx, |assistant, cx| {
|
||||
for (range, description, initial_insertion) in inline_assist_suggestions {
|
||||
assist_ids.push(assistant.suggest_assist(
|
||||
&editor,
|
||||
range,
|
||||
description,
|
||||
initial_insertion,
|
||||
Some(workspace.clone()),
|
||||
assistant_panel.upgrade().as_ref(),
|
||||
cx,
|
||||
));
|
||||
}
|
||||
})
|
||||
})?;
|
||||
workspace.update(&mut cx, |workspace, cx| {
|
||||
workspace.add_item_to_active_pane(Box::new(editor.clone()), None, false, cx)
|
||||
})?;
|
||||
editor.set_scroll_anchor(
|
||||
ScrollAnchor {
|
||||
offset: gpui::Point::default(),
|
||||
anchor,
|
||||
},
|
||||
cx,
|
||||
);
|
||||
});
|
||||
|
||||
editor
|
||||
};
|
||||
|
||||
this.update(&mut cx, |this, _cx| {
|
||||
if let Some(step) = this.active_edit_step.as_mut() {
|
||||
step.assist_ids = assist_ids;
|
||||
step.editor = Some(editor.downgrade());
|
||||
suggestion_groups.push((excerpt_id, group));
|
||||
} else {
|
||||
// If there are multiple buffers or suggestion groups, create a multibuffer
|
||||
let multibuffer = cx.new_model(|cx| {
|
||||
let replica_id = self.project.read(cx).replica_id();
|
||||
let mut multibuffer =
|
||||
MultiBuffer::new(replica_id, Capability::ReadWrite).with_title(title);
|
||||
for (buffer, groups) in edit_suggestions {
|
||||
let excerpt_ids = multibuffer.push_excerpts(
|
||||
buffer,
|
||||
groups.iter().map(|suggestion_group| ExcerptRange {
|
||||
context: suggestion_group.context_range.clone(),
|
||||
primary: None,
|
||||
}),
|
||||
cx,
|
||||
);
|
||||
suggestion_groups.extend(excerpt_ids.into_iter().zip(groups));
|
||||
}
|
||||
})
|
||||
})
|
||||
multibuffer
|
||||
});
|
||||
|
||||
editor = cx.new_view(|cx| {
|
||||
Editor::for_multibuffer(multibuffer, Some(self.project.clone()), true, cx)
|
||||
});
|
||||
self.workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
workspace.add_item_to_active_pane(Box::new(editor.clone()), None, false, cx)
|
||||
})
|
||||
.log_err()?;
|
||||
}
|
||||
|
||||
let mut assist_ids = Vec::new();
|
||||
for (excerpt_id, suggestion_group) in suggestion_groups {
|
||||
for suggestion in suggestion_group.suggestions {
|
||||
assist_ids.extend(suggestion.show(
|
||||
&editor,
|
||||
excerpt_id,
|
||||
&self.workspace,
|
||||
&assistant_panel,
|
||||
cx,
|
||||
));
|
||||
}
|
||||
}
|
||||
Some((editor, assist_ids))
|
||||
}
|
||||
|
||||
fn handle_editor_search_event(
|
||||
|
@ -2374,11 +2316,10 @@ impl ContextEditor {
|
|||
|
||||
fn render_send_button(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
let focus_handle = self.focus_handle(cx).clone();
|
||||
let button_text = match self.edit_step_for_cursor(cx) {
|
||||
Some(edit_step) => match &edit_step.state {
|
||||
Some(EditStepState::Pending(_)) => "Computing Changes...",
|
||||
Some(EditStepState::Resolved(_)) => "Apply Changes",
|
||||
None => "Send",
|
||||
let button_text = match self.workflow_step_for_cursor(cx) {
|
||||
Some(edit_step) => match &edit_step.edit_suggestions {
|
||||
WorkflowStepEditSuggestions::Pending(_) => "Computing Changes...",
|
||||
WorkflowStepEditSuggestions::Resolved { .. } => "Apply Changes",
|
||||
},
|
||||
None => "Send",
|
||||
};
|
||||
|
@ -2421,7 +2362,7 @@ impl ContextEditor {
|
|||
})
|
||||
}
|
||||
|
||||
fn edit_step_for_cursor<'a>(&'a self, cx: &'a AppContext) -> Option<&'a EditStep> {
|
||||
fn workflow_step_for_cursor<'a>(&'a self, cx: &'a AppContext) -> Option<&'a WorkflowStep> {
|
||||
let newest_cursor = self
|
||||
.editor
|
||||
.read(cx)
|
||||
|
@ -2435,7 +2376,7 @@ impl ContextEditor {
|
|||
let edit_steps = context.edit_steps();
|
||||
edit_steps
|
||||
.binary_search_by(|step| {
|
||||
let step_range = step.source_range.clone();
|
||||
let step_range = step.tagged_range.clone();
|
||||
if newest_cursor.cmp(&step_range.start, buffer).is_lt() {
|
||||
Ordering::Greater
|
||||
} else if newest_cursor.cmp(&step_range.end, buffer).is_gt() {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -330,7 +330,12 @@ impl ContextStore {
|
|||
|
||||
pub fn create(&mut self, cx: &mut ModelContext<Self>) -> Model<Context> {
|
||||
let context = cx.new_model(|cx| {
|
||||
Context::local(self.languages.clone(), Some(self.telemetry.clone()), cx)
|
||||
Context::local(
|
||||
self.languages.clone(),
|
||||
Some(self.project.clone()),
|
||||
Some(self.telemetry.clone()),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
self.register_context(&context, cx);
|
||||
context
|
||||
|
@ -351,6 +356,7 @@ impl ContextStore {
|
|||
let replica_id = project.replica_id();
|
||||
let capability = project.capability();
|
||||
let language_registry = self.languages.clone();
|
||||
let project = self.project.clone();
|
||||
let telemetry = self.telemetry.clone();
|
||||
let request = self.client.request(proto::CreateContext { project_id });
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
|
@ -363,6 +369,7 @@ impl ContextStore {
|
|||
replica_id,
|
||||
capability,
|
||||
language_registry,
|
||||
Some(project),
|
||||
Some(telemetry),
|
||||
cx,
|
||||
)
|
||||
|
@ -401,6 +408,7 @@ impl ContextStore {
|
|||
|
||||
let fs = self.fs.clone();
|
||||
let languages = self.languages.clone();
|
||||
let project = self.project.clone();
|
||||
let telemetry = self.telemetry.clone();
|
||||
let load = cx.background_executor().spawn({
|
||||
let path = path.clone();
|
||||
|
@ -413,7 +421,14 @@ impl ContextStore {
|
|||
cx.spawn(|this, mut cx| async move {
|
||||
let saved_context = load.await?;
|
||||
let context = cx.new_model(|cx| {
|
||||
Context::deserialize(saved_context, path.clone(), languages, Some(telemetry), cx)
|
||||
Context::deserialize(
|
||||
saved_context,
|
||||
path.clone(),
|
||||
languages,
|
||||
Some(project),
|
||||
Some(telemetry),
|
||||
cx,
|
||||
)
|
||||
})?;
|
||||
this.update(&mut cx, |this, cx| {
|
||||
if let Some(existing_context) = this.loaded_context_for_path(&path, cx) {
|
||||
|
@ -472,6 +487,7 @@ impl ContextStore {
|
|||
let replica_id = project.replica_id();
|
||||
let capability = project.capability();
|
||||
let language_registry = self.languages.clone();
|
||||
let project = self.project.clone();
|
||||
let telemetry = self.telemetry.clone();
|
||||
let request = self.client.request(proto::OpenContext {
|
||||
project_id,
|
||||
|
@ -486,6 +502,7 @@ impl ContextStore {
|
|||
replica_id,
|
||||
capability,
|
||||
language_registry,
|
||||
Some(project),
|
||||
Some(telemetry),
|
||||
cx,
|
||||
)
|
||||
|
|
|
@ -237,7 +237,7 @@ impl InlineAssistant {
|
|||
editor: &View<Editor>,
|
||||
mut range: Range<Anchor>,
|
||||
initial_prompt: String,
|
||||
initial_insertion: Option<InitialInsertion>,
|
||||
initial_transaction_id: Option<TransactionId>,
|
||||
workspace: Option<WeakView<Workspace>>,
|
||||
assistant_panel: Option<&View<AssistantPanel>>,
|
||||
cx: &mut WindowContext,
|
||||
|
@ -251,28 +251,15 @@ impl InlineAssistant {
|
|||
let buffer = editor.read(cx).buffer().clone();
|
||||
{
|
||||
let snapshot = buffer.read(cx).read(cx);
|
||||
|
||||
let mut point_range = range.to_point(&snapshot);
|
||||
if point_range.is_empty() {
|
||||
point_range.start.column = 0;
|
||||
point_range.end.column = 0;
|
||||
} else {
|
||||
point_range.start.column = 0;
|
||||
if point_range.end.row > point_range.start.row && point_range.end.column == 0 {
|
||||
point_range.end.row -= 1;
|
||||
}
|
||||
point_range.end.column = snapshot.line_len(MultiBufferRow(point_range.end.row));
|
||||
}
|
||||
|
||||
range.start = snapshot.anchor_before(point_range.start);
|
||||
range.end = snapshot.anchor_after(point_range.end);
|
||||
range.start = range.start.bias_left(&snapshot);
|
||||
range.end = range.end.bias_right(&snapshot);
|
||||
}
|
||||
|
||||
let codegen = cx.new_model(|cx| {
|
||||
Codegen::new(
|
||||
editor.read(cx).buffer().clone(),
|
||||
range.clone(),
|
||||
initial_insertion,
|
||||
initial_transaction_id,
|
||||
self.telemetry.clone(),
|
||||
cx,
|
||||
)
|
||||
|
@ -873,13 +860,20 @@ impl InlineAssistant {
|
|||
for assist_id in assist_ids {
|
||||
if let Some(assist) = self.assists.get(assist_id) {
|
||||
let codegen = assist.codegen.read(cx);
|
||||
let buffer = codegen.buffer.read(cx).read(cx);
|
||||
foreground_ranges.extend(codegen.last_equal_ranges().iter().cloned());
|
||||
|
||||
gutter_pending_ranges
|
||||
.push(codegen.edit_position.unwrap_or(assist.range.start)..assist.range.end);
|
||||
let pending_range =
|
||||
codegen.edit_position.unwrap_or(assist.range.start)..assist.range.end;
|
||||
if pending_range.end.to_offset(&buffer) > pending_range.start.to_offset(&buffer) {
|
||||
gutter_pending_ranges.push(pending_range);
|
||||
}
|
||||
|
||||
if let Some(edit_position) = codegen.edit_position {
|
||||
gutter_transformed_ranges.push(assist.range.start..edit_position);
|
||||
let edited_range = assist.range.start..edit_position;
|
||||
if edited_range.end.to_offset(&buffer) > edited_range.start.to_offset(&buffer) {
|
||||
gutter_transformed_ranges.push(edited_range);
|
||||
}
|
||||
}
|
||||
|
||||
if assist.decorations.is_some() {
|
||||
|
@ -1997,13 +1991,13 @@ pub struct Codegen {
|
|||
snapshot: MultiBufferSnapshot,
|
||||
edit_position: Option<Anchor>,
|
||||
last_equal_ranges: Vec<Range<Anchor>>,
|
||||
transaction_id: Option<TransactionId>,
|
||||
initial_transaction_id: Option<TransactionId>,
|
||||
transformation_transaction_id: Option<TransactionId>,
|
||||
status: CodegenStatus,
|
||||
generation: Task<()>,
|
||||
diff: Diff,
|
||||
telemetry: Option<Arc<Telemetry>>,
|
||||
_subscription: gpui::Subscription,
|
||||
initial_insertion: Option<InitialInsertion>,
|
||||
}
|
||||
|
||||
enum CodegenStatus {
|
||||
|
@ -2027,7 +2021,7 @@ impl Codegen {
|
|||
pub fn new(
|
||||
buffer: Model<MultiBuffer>,
|
||||
range: Range<Anchor>,
|
||||
initial_insertion: Option<InitialInsertion>,
|
||||
initial_transaction_id: Option<TransactionId>,
|
||||
telemetry: Option<Arc<Telemetry>>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Self {
|
||||
|
@ -2059,13 +2053,13 @@ impl Codegen {
|
|||
edit_position: None,
|
||||
snapshot,
|
||||
last_equal_ranges: Default::default(),
|
||||
transaction_id: None,
|
||||
transformation_transaction_id: None,
|
||||
status: CodegenStatus::Idle,
|
||||
generation: Task::ready(()),
|
||||
diff: Diff::default(),
|
||||
telemetry,
|
||||
_subscription: cx.subscribe(&buffer, Self::handle_buffer_event),
|
||||
initial_insertion,
|
||||
initial_transaction_id,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2076,8 +2070,8 @@ impl Codegen {
|
|||
cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
if let multi_buffer::Event::TransactionUndone { transaction_id } = event {
|
||||
if self.transaction_id == Some(*transaction_id) {
|
||||
self.transaction_id = None;
|
||||
if self.transformation_transaction_id == Some(*transaction_id) {
|
||||
self.transformation_transaction_id = None;
|
||||
self.generation = Task::ready(());
|
||||
cx.emit(CodegenEvent::Undone);
|
||||
}
|
||||
|
@ -2105,7 +2099,7 @@ impl Codegen {
|
|||
|
||||
pub fn start(
|
||||
&mut self,
|
||||
mut edit_range: Range<Anchor>,
|
||||
edit_range: Range<Anchor>,
|
||||
user_prompt: String,
|
||||
assistant_panel_context: Option<LanguageModelRequest>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
|
@ -2114,34 +2108,13 @@ impl Codegen {
|
|||
.active_model()
|
||||
.context("no active model")?;
|
||||
|
||||
self.undo(cx);
|
||||
|
||||
// Handle initial insertion
|
||||
self.transaction_id = if let Some(initial_insertion) = self.initial_insertion {
|
||||
if let Some(transformation_transaction_id) = self.transformation_transaction_id.take() {
|
||||
self.buffer.update(cx, |buffer, cx| {
|
||||
buffer.start_transaction(cx);
|
||||
let offset = edit_range.start.to_offset(&self.snapshot);
|
||||
let edit_position;
|
||||
match initial_insertion {
|
||||
InitialInsertion::NewlineBefore => {
|
||||
buffer.edit([(offset..offset, "\n\n")], None, cx);
|
||||
self.snapshot = buffer.snapshot(cx);
|
||||
edit_position = self.snapshot.anchor_after(offset + 1);
|
||||
}
|
||||
InitialInsertion::NewlineAfter => {
|
||||
buffer.edit([(offset..offset, "\n")], None, cx);
|
||||
self.snapshot = buffer.snapshot(cx);
|
||||
edit_position = self.snapshot.anchor_after(offset);
|
||||
}
|
||||
}
|
||||
self.edit_position = Some(edit_position);
|
||||
edit_range = edit_position.bias_left(&self.snapshot)..edit_position;
|
||||
buffer.end_transaction(cx)
|
||||
})
|
||||
} else {
|
||||
self.edit_position = Some(edit_range.start.bias_right(&self.snapshot));
|
||||
None
|
||||
};
|
||||
buffer.undo_transaction(transformation_transaction_id, cx)
|
||||
});
|
||||
}
|
||||
|
||||
self.edit_position = Some(edit_range.start.bias_right(&self.snapshot));
|
||||
|
||||
let telemetry_id = model.telemetry_id();
|
||||
let chunks: LocalBoxFuture<Result<BoxStream<Result<String>>>> = if user_prompt
|
||||
|
@ -2406,7 +2379,8 @@ impl Codegen {
|
|||
});
|
||||
|
||||
if let Some(transaction) = transaction {
|
||||
if let Some(first_transaction) = this.transaction_id {
|
||||
if let Some(first_transaction) = this.transformation_transaction_id
|
||||
{
|
||||
// Group all assistant edits into the first transaction.
|
||||
this.buffer.update(cx, |buffer, cx| {
|
||||
buffer.merge_transactions(
|
||||
|
@ -2416,7 +2390,7 @@ impl Codegen {
|
|||
)
|
||||
});
|
||||
} else {
|
||||
this.transaction_id = Some(transaction);
|
||||
this.transformation_transaction_id = Some(transaction);
|
||||
this.buffer.update(cx, |buffer, cx| {
|
||||
buffer.finalize_last_transaction(cx)
|
||||
});
|
||||
|
@ -2459,10 +2433,15 @@ impl Codegen {
|
|||
}
|
||||
|
||||
pub fn undo(&mut self, cx: &mut ModelContext<Self>) {
|
||||
if let Some(transaction_id) = self.transaction_id.take() {
|
||||
self.buffer
|
||||
.update(cx, |buffer, cx| buffer.undo_transaction(transaction_id, cx));
|
||||
}
|
||||
self.buffer.update(cx, |buffer, cx| {
|
||||
if let Some(transaction_id) = self.transformation_transaction_id.take() {
|
||||
buffer.undo_transaction(transaction_id, cx);
|
||||
}
|
||||
|
||||
if let Some(transaction_id) = self.initial_transaction_id.take() {
|
||||
buffer.undo_transaction(transaction_id, cx);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn update_diff(&mut self, edit_range: Range<Anchor>, cx: &mut ModelContext<Self>) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue