Cancel workflow step automatically when moving outside <step> tag (#15842)
Also, open edit suggestions automatically as soon as the edit step is resolved. Release Notes: - N/A
This commit is contained in:
parent
7b5fdcee7f
commit
44ae9efb27
3 changed files with 234 additions and 118 deletions
|
@ -8,12 +8,12 @@ use crate::{
|
|||
SlashCommandCompletionProvider, SlashCommandRegistry,
|
||||
},
|
||||
terminal_inline_assistant::TerminalInlineAssistant,
|
||||
Assist, ConfirmCommand, Context, ContextEvent, ContextId, ContextStore, CycleMessageRole,
|
||||
DebugEditSteps, DeployHistory, DeployPromptLibrary, EditSuggestionGroup, InlineAssist,
|
||||
InlineAssistId, InlineAssistant, InsertIntoEditor, MessageStatus, ModelSelector,
|
||||
Assist, CodegenStatus, ConfirmCommand, Context, ContextEvent, ContextId, ContextStore,
|
||||
CycleMessageRole, DebugEditSteps, DeployHistory, DeployPromptLibrary, EditSuggestionGroup,
|
||||
InlineAssist, InlineAssistId, InlineAssistant, InsertIntoEditor, MessageStatus, ModelSelector,
|
||||
PendingSlashCommand, PendingSlashCommandStatus, QuoteSelection, RemoteContextMetadata,
|
||||
SavedContextMetadata, Split, ToggleFocus, ToggleModelSelector, WorkflowStep,
|
||||
WorkflowStepEditSuggestions,
|
||||
ResolvedWorkflowStepEditSuggestions, SavedContextMetadata, Split, ToggleFocus,
|
||||
ToggleModelSelector, WorkflowStepEditSuggestions,
|
||||
};
|
||||
use crate::{ContextStoreEvent, ShowConfiguration};
|
||||
use anyhow::{anyhow, Result};
|
||||
|
@ -35,8 +35,8 @@ use gpui::{
|
|||
div, percentage, point, Action, Animation, AnimationExt, AnyElement, AnyView, AppContext,
|
||||
AsyncWindowContext, ClipboardItem, Context as _, DismissEvent, Empty, Entity, EventEmitter,
|
||||
FocusHandle, FocusableView, FontWeight, InteractiveElement, IntoElement, Model, ParentElement,
|
||||
Pixels, Render, SharedString, StatefulInteractiveElement, Styled, Subscription, Task,
|
||||
Transformation, UpdateGlobal, View, ViewContext, VisualContext, WeakView, WindowContext,
|
||||
Pixels, ReadGlobal, Render, SharedString, StatefulInteractiveElement, Styled, Subscription,
|
||||
Task, Transformation, UpdateGlobal, View, ViewContext, VisualContext, WeakView, WindowContext,
|
||||
};
|
||||
use indexed_docs::IndexedDocsStore;
|
||||
use language::{
|
||||
|
@ -1295,10 +1295,15 @@ struct ScrollPosition {
|
|||
cursor: Anchor,
|
||||
}
|
||||
|
||||
struct ActiveEditStep {
|
||||
start: language::Anchor,
|
||||
struct StepAssists {
|
||||
assist_ids: Vec<InlineAssistId>,
|
||||
editor: Option<WeakView<Editor>>,
|
||||
editor: WeakView<Editor>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
struct ActiveWorkflowStep {
|
||||
range: Range<language::Anchor>,
|
||||
suggestions: Option<ResolvedWorkflowStepEditSuggestions>,
|
||||
}
|
||||
|
||||
pub struct ContextEditor {
|
||||
|
@ -1314,7 +1319,8 @@ pub struct ContextEditor {
|
|||
pending_slash_command_creases: HashMap<Range<language::Anchor>, CreaseId>,
|
||||
pending_slash_command_blocks: HashMap<Range<language::Anchor>, CustomBlockId>,
|
||||
_subscriptions: Vec<Subscription>,
|
||||
active_edit_step: Option<ActiveEditStep>,
|
||||
assists_by_step: HashMap<Range<language::Anchor>, StepAssists>,
|
||||
active_workflow_step: Option<ActiveWorkflowStep>,
|
||||
assistant_panel: WeakView<AssistantPanel>,
|
||||
error_message: Option<SharedString>,
|
||||
}
|
||||
|
@ -1372,7 +1378,8 @@ impl ContextEditor {
|
|||
pending_slash_command_creases: HashMap::default(),
|
||||
pending_slash_command_blocks: HashMap::default(),
|
||||
_subscriptions,
|
||||
active_edit_step: None,
|
||||
assists_by_step: HashMap::default(),
|
||||
active_workflow_step: None,
|
||||
assistant_panel,
|
||||
error_message: None,
|
||||
};
|
||||
|
@ -1415,17 +1422,21 @@ impl ContextEditor {
|
|||
}
|
||||
|
||||
fn apply_edit_step(&mut self, cx: &mut ViewContext<Self>) -> bool {
|
||||
if let Some(step) = self.active_edit_step.as_ref() {
|
||||
let assist_ids = step.assist_ids.clone();
|
||||
cx.window_context().defer(|cx| {
|
||||
InlineAssistant::update_global(cx, |assistant, cx| {
|
||||
for assist_id in assist_ids {
|
||||
assistant.start_assist(assist_id, cx);
|
||||
}
|
||||
})
|
||||
});
|
||||
if let Some(step) = self.active_workflow_step.as_ref() {
|
||||
if let Some(assists) = self.assists_by_step.get(&step.range) {
|
||||
let assist_ids = assists.assist_ids.clone();
|
||||
cx.window_context().defer(|cx| {
|
||||
InlineAssistant::update_global(cx, |assistant, cx| {
|
||||
for assist_id in assist_ids {
|
||||
assistant.start_assist(assist_id, cx);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
!step.assist_ids.is_empty()
|
||||
!assists.assist_ids.is_empty()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
@ -1462,7 +1473,7 @@ impl ContextEditor {
|
|||
|
||||
fn debug_edit_steps(&mut self, _: &DebugEditSteps, cx: &mut ViewContext<Self>) {
|
||||
let mut output = String::new();
|
||||
for (i, step) in self.context.read(cx).edit_steps().iter().enumerate() {
|
||||
for (i, step) in self.context.read(cx).workflow_steps().iter().enumerate() {
|
||||
output.push_str(&format!("Step {}:\n", i + 1));
|
||||
output.push_str(&format!(
|
||||
"Content: {}\n",
|
||||
|
@ -1474,10 +1485,10 @@ impl ContextEditor {
|
|||
.collect::<String>()
|
||||
));
|
||||
match &step.edit_suggestions {
|
||||
WorkflowStepEditSuggestions::Resolved {
|
||||
WorkflowStepEditSuggestions::Resolved(ResolvedWorkflowStepEditSuggestions {
|
||||
title,
|
||||
edit_suggestions,
|
||||
} => {
|
||||
}) => {
|
||||
output.push_str("Resolution:\n");
|
||||
output.push_str(&format!(" {:?}\n", title));
|
||||
output.push_str(&format!(" {:?}\n", edit_suggestions));
|
||||
|
@ -1629,7 +1640,8 @@ impl ContextEditor {
|
|||
context.save(Some(Duration::from_millis(500)), self.fs.clone(), cx);
|
||||
});
|
||||
}
|
||||
ContextEvent::EditStepsChanged => {
|
||||
ContextEvent::WorkflowStepsChanged => {
|
||||
self.update_active_workflow_step(cx);
|
||||
cx.notify();
|
||||
}
|
||||
ContextEvent::SummaryChanged => {
|
||||
|
@ -1902,59 +1914,116 @@ impl ContextEditor {
|
|||
}
|
||||
|
||||
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 new_step = self
|
||||
.workflow_step_range_for_cursor(cx)
|
||||
.as_ref()
|
||||
.and_then(|step_range| {
|
||||
let workflow_step = self
|
||||
.context
|
||||
.read(cx)
|
||||
.workflow_step_for_range(step_range.clone())?;
|
||||
Some(ActiveWorkflowStep {
|
||||
range: workflow_step.tagged_range.clone(),
|
||||
suggestions: workflow_step.edit_suggestions.as_resolved().cloned(),
|
||||
})
|
||||
});
|
||||
if new_step.as_ref() != self.active_workflow_step.as_ref() {
|
||||
if let Some(old_step) = self.active_workflow_step.take() {
|
||||
self.cancel_workflow_step_if_idle(old_step.range, 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,
|
||||
});
|
||||
if let Some(new_step) = new_step {
|
||||
self.activate_workflow_step(new_step, cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cancel_workflow_step_if_idle(
|
||||
&mut self,
|
||||
step_range: Range<language::Anchor>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
let Some(step_assists) = self.assists_by_step.get_mut(&step_range) else {
|
||||
return;
|
||||
};
|
||||
let Some(editor) = step_assists.editor.upgrade() else {
|
||||
self.assists_by_step.remove(&step_range);
|
||||
return;
|
||||
};
|
||||
|
||||
InlineAssistant::update_global(cx, |assistant, cx| {
|
||||
step_assists.assist_ids.retain(|assist_id| {
|
||||
match assistant.status_for_assist(*assist_id, cx) {
|
||||
Some(CodegenStatus::Idle) | None => {
|
||||
assistant.finish_assist(*assist_id, true, cx);
|
||||
false
|
||||
}
|
||||
_ => true,
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if step_assists.assist_ids.is_empty() {
|
||||
self.assists_by_step.remove(&step_range);
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
fn activate_workflow_step(&mut self, step: ActiveWorkflowStep, cx: &mut ViewContext<Self>) {
|
||||
if let Some(step_assists) = self.assists_by_step.get(&step.range) {
|
||||
if let Some(editor) = step_assists.editor.upgrade() {
|
||||
for assist_id in &step_assists.assist_ids {
|
||||
match InlineAssistant::global(cx).status_for_assist(*assist_id, cx) {
|
||||
Some(CodegenStatus::Idle) | None => {}
|
||||
_ => {
|
||||
self.workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
workspace.activate_item(&editor, false, false, cx);
|
||||
})
|
||||
.ok();
|
||||
InlineAssistant::update_global(cx, |assistant, cx| {
|
||||
assistant.scroll_to_assist(*assist_id, cx)
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ResolvedWorkflowStepEditSuggestions {
|
||||
title,
|
||||
edit_suggestions,
|
||||
}) = step.suggestions.as_ref()
|
||||
{
|
||||
if let Some((editor, assist_ids)) =
|
||||
self.suggest_edits(title.clone(), edit_suggestions.clone(), cx)
|
||||
{
|
||||
self.assists_by_step.insert(
|
||||
step.range.clone(),
|
||||
StepAssists {
|
||||
assist_ids,
|
||||
editor: editor.downgrade(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
self.active_workflow_step = Some(step);
|
||||
}
|
||||
|
||||
fn suggest_edits(
|
||||
&mut self,
|
||||
title: String,
|
||||
|
@ -2436,11 +2505,14 @@ 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.workflow_step_for_cursor(cx) {
|
||||
Some(edit_step) => match &edit_step.edit_suggestions {
|
||||
WorkflowStepEditSuggestions::Pending(_) => "Computing Changes...",
|
||||
WorkflowStepEditSuggestions::Resolved { .. } => "Apply Changes",
|
||||
},
|
||||
let button_text = match self.active_workflow_step.as_ref() {
|
||||
Some(step) => {
|
||||
if step.suggestions.is_none() {
|
||||
"Computing Changes..."
|
||||
} else {
|
||||
"Apply Changes"
|
||||
}
|
||||
}
|
||||
None => "Send",
|
||||
};
|
||||
|
||||
|
@ -2482,7 +2554,7 @@ impl ContextEditor {
|
|||
})
|
||||
}
|
||||
|
||||
fn workflow_step_for_cursor<'a>(&'a self, cx: &'a AppContext) -> Option<&'a WorkflowStep> {
|
||||
fn workflow_step_range_for_cursor(&self, cx: &AppContext) -> Option<Range<language::Anchor>> {
|
||||
let newest_cursor = self
|
||||
.editor
|
||||
.read(cx)
|
||||
|
@ -2493,7 +2565,7 @@ impl ContextEditor {
|
|||
let context = self.context.read(cx);
|
||||
let buffer = context.buffer().read(cx);
|
||||
|
||||
let edit_steps = context.edit_steps();
|
||||
let edit_steps = context.workflow_steps();
|
||||
edit_steps
|
||||
.binary_search_by(|step| {
|
||||
let step_range = step.tagged_range.clone();
|
||||
|
@ -2506,7 +2578,7 @@ impl ContextEditor {
|
|||
}
|
||||
})
|
||||
.ok()
|
||||
.map(|index| &edit_steps[index])
|
||||
.map(|index| edit_steps[index].tagged_range.clone())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue