Remove workflow inspector, clean up workflow code (#16325)
Now that there's a dedicated, user-facing view for each workflow step, we don't need the inspector functionality. This PR also cleans up some naming around workflow steps and step resolutions. Release Notes: - N/A
This commit is contained in:
parent
da2bfbd29f
commit
c896ff292c
6 changed files with 208 additions and 612 deletions
|
@ -3,7 +3,6 @@
|
||||||
pub mod assistant_panel;
|
pub mod assistant_panel;
|
||||||
pub mod assistant_settings;
|
pub mod assistant_settings;
|
||||||
mod context;
|
mod context;
|
||||||
pub(crate) mod context_inspector;
|
|
||||||
pub mod context_store;
|
pub mod context_store;
|
||||||
mod inline_assistant;
|
mod inline_assistant;
|
||||||
mod model_selector;
|
mod model_selector;
|
||||||
|
@ -65,7 +64,6 @@ actions!(
|
||||||
DeployPromptLibrary,
|
DeployPromptLibrary,
|
||||||
ConfirmCommand,
|
ConfirmCommand,
|
||||||
ToggleModelSelector,
|
ToggleModelSelector,
|
||||||
DebugWorkflowSteps
|
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
assistant_settings::{AssistantDockPosition, AssistantSettings},
|
assistant_settings::{AssistantDockPosition, AssistantSettings},
|
||||||
context_inspector::ContextInspector,
|
|
||||||
humanize_token_count,
|
humanize_token_count,
|
||||||
prompt_library::open_prompt_library,
|
prompt_library::open_prompt_library,
|
||||||
prompts::PromptBuilder,
|
prompts::PromptBuilder,
|
||||||
|
@ -12,10 +11,10 @@ use crate::{
|
||||||
},
|
},
|
||||||
terminal_inline_assistant::TerminalInlineAssistant,
|
terminal_inline_assistant::TerminalInlineAssistant,
|
||||||
Assist, ConfirmCommand, Context, ContextEvent, ContextId, ContextStore, CycleMessageRole,
|
Assist, ConfirmCommand, Context, ContextEvent, ContextId, ContextStore, CycleMessageRole,
|
||||||
DebugWorkflowSteps, DeployHistory, DeployPromptLibrary, InlineAssist, InlineAssistId,
|
DeployHistory, DeployPromptLibrary, InlineAssist, InlineAssistId, InlineAssistant,
|
||||||
InlineAssistant, InsertIntoEditor, MessageStatus, ModelSelector, PendingSlashCommand,
|
InsertIntoEditor, MessageStatus, ModelSelector, PendingSlashCommand, PendingSlashCommandStatus,
|
||||||
PendingSlashCommandStatus, QuoteSelection, RemoteContextMetadata, ResolvedWorkflowStep,
|
QuoteSelection, RemoteContextMetadata, SavedContextMetadata, Split, ToggleFocus,
|
||||||
SavedContextMetadata, Split, ToggleFocus, ToggleModelSelector, WorkflowStepView,
|
ToggleModelSelector, WorkflowStepResolution, WorkflowStepView,
|
||||||
};
|
};
|
||||||
use crate::{ContextStoreEvent, ModelPickerDelegate, ShowConfiguration};
|
use crate::{ContextStoreEvent, ModelPickerDelegate, ShowConfiguration};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
|
@ -57,7 +56,7 @@ use settings::{update_settings_file, Settings};
|
||||||
use smol::stream::StreamExt;
|
use smol::stream::StreamExt;
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
cmp::{self, Ordering},
|
cmp,
|
||||||
fmt::Write,
|
fmt::Write,
|
||||||
ops::{DerefMut, Range},
|
ops::{DerefMut, Range},
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
|
@ -65,7 +64,6 @@ use std::{
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
use terminal_view::{terminal_panel::TerminalPanel, TerminalView};
|
use terminal_view::{terminal_panel::TerminalPanel, TerminalView};
|
||||||
use text::OffsetRangeExt;
|
|
||||||
use ui::TintColor;
|
use ui::TintColor;
|
||||||
use ui::{
|
use ui::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
|
@ -77,7 +75,6 @@ use util::ResultExt;
|
||||||
use workspace::{
|
use workspace::{
|
||||||
dock::{DockPosition, Panel, PanelEvent},
|
dock::{DockPosition, Panel, PanelEvent},
|
||||||
item::{self, FollowableItem, Item, ItemHandle},
|
item::{self, FollowableItem, Item, ItemHandle},
|
||||||
notifications::NotifyTaskExt,
|
|
||||||
pane::{self, SaveIntent},
|
pane::{self, SaveIntent},
|
||||||
searchable::{SearchEvent, SearchableItem},
|
searchable::{SearchEvent, SearchableItem},
|
||||||
Pane, Save, ToggleZoom, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace,
|
Pane, Save, ToggleZoom, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace,
|
||||||
|
@ -404,56 +401,13 @@ impl AssistantPanel {
|
||||||
} else {
|
} else {
|
||||||
"Zoom In"
|
"Zoom In"
|
||||||
};
|
};
|
||||||
let weak_pane = cx.view().downgrade();
|
|
||||||
let menu = ContextMenu::build(cx, |menu, cx| {
|
let menu = ContextMenu::build(cx, |menu, cx| {
|
||||||
let menu = menu
|
menu.context(pane.focus_handle(cx))
|
||||||
.context(pane.focus_handle(cx))
|
|
||||||
.action("New Context", Box::new(NewFile))
|
.action("New Context", Box::new(NewFile))
|
||||||
.action("History", Box::new(DeployHistory))
|
.action("History", Box::new(DeployHistory))
|
||||||
.action("Prompt Library", Box::new(DeployPromptLibrary))
|
.action("Prompt Library", Box::new(DeployPromptLibrary))
|
||||||
.action("Configure", Box::new(ShowConfiguration))
|
.action("Configure", Box::new(ShowConfiguration))
|
||||||
.action(zoom_label, Box::new(ToggleZoom));
|
.action(zoom_label, Box::new(ToggleZoom))
|
||||||
|
|
||||||
if let Some(editor) = pane
|
|
||||||
.active_item()
|
|
||||||
.and_then(|e| e.downcast::<ContextEditor>())
|
|
||||||
{
|
|
||||||
let is_enabled = editor.read(cx).debug_inspector.is_some();
|
|
||||||
menu.separator().toggleable_entry(
|
|
||||||
"Debug Workflows",
|
|
||||||
is_enabled,
|
|
||||||
IconPosition::End,
|
|
||||||
None,
|
|
||||||
move |cx| {
|
|
||||||
weak_pane
|
|
||||||
.update(cx, |this, cx| {
|
|
||||||
if let Some(context_editor) =
|
|
||||||
this.active_item().and_then(|item| {
|
|
||||||
item.downcast::<ContextEditor>()
|
|
||||||
})
|
|
||||||
{
|
|
||||||
context_editor.update(cx, |this, cx| {
|
|
||||||
if let Some(mut state) =
|
|
||||||
this.debug_inspector.take()
|
|
||||||
{
|
|
||||||
state.deactivate(cx);
|
|
||||||
} else {
|
|
||||||
this.debug_inspector = Some(
|
|
||||||
ContextInspector::new(
|
|
||||||
this.editor.clone(),
|
|
||||||
this.context.clone(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
},
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
menu
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
cx.subscribe(&menu, |pane, _, _: &DismissEvent, _| {
|
cx.subscribe(&menu, |pane, _, _: &DismissEvent, _| {
|
||||||
pane.new_item_menu = None;
|
pane.new_item_menu = None;
|
||||||
|
@ -1380,7 +1334,7 @@ struct WorkflowStep {
|
||||||
range: Range<language::Anchor>,
|
range: Range<language::Anchor>,
|
||||||
header_block_id: CustomBlockId,
|
header_block_id: CustomBlockId,
|
||||||
footer_block_id: CustomBlockId,
|
footer_block_id: CustomBlockId,
|
||||||
resolved_step: Option<Result<ResolvedWorkflowStep, Arc<anyhow::Error>>>,
|
resolved_step: Option<Result<WorkflowStepResolution, Arc<anyhow::Error>>>,
|
||||||
assist: Option<WorkflowAssist>,
|
assist: Option<WorkflowAssist>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1744,7 +1698,6 @@ pub struct ContextEditor {
|
||||||
active_workflow_step: Option<ActiveWorkflowStep>,
|
active_workflow_step: Option<ActiveWorkflowStep>,
|
||||||
assistant_panel: WeakView<AssistantPanel>,
|
assistant_panel: WeakView<AssistantPanel>,
|
||||||
error_message: Option<SharedString>,
|
error_message: Option<SharedString>,
|
||||||
debug_inspector: Option<ContextInspector>,
|
|
||||||
show_accept_terms: bool,
|
show_accept_terms: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1806,7 +1759,6 @@ impl ContextEditor {
|
||||||
active_workflow_step: None,
|
active_workflow_step: None,
|
||||||
assistant_panel,
|
assistant_panel,
|
||||||
error_message: None,
|
error_message: None,
|
||||||
debug_inspector: None,
|
|
||||||
show_accept_terms: false,
|
show_accept_terms: false,
|
||||||
};
|
};
|
||||||
this.update_message_headers(cx);
|
this.update_message_headers(cx);
|
||||||
|
@ -2016,51 +1968,6 @@ impl ContextEditor {
|
||||||
cx.propagate();
|
cx.propagate();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn debug_workflow_steps(&mut self, _: &DebugWorkflowSteps, cx: &mut ViewContext<Self>) {
|
|
||||||
let mut output = String::new();
|
|
||||||
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",
|
|
||||||
self.context
|
|
||||||
.read(cx)
|
|
||||||
.buffer()
|
|
||||||
.read(cx)
|
|
||||||
.text_for_range(step.tagged_range.clone())
|
|
||||||
.collect::<String>()
|
|
||||||
));
|
|
||||||
match &step.resolution.read(cx).result {
|
|
||||||
Some(Ok(ResolvedWorkflowStep {
|
|
||||||
title,
|
|
||||||
suggestion_groups: suggestions,
|
|
||||||
})) => {
|
|
||||||
output.push_str("Resolution:\n");
|
|
||||||
output.push_str(&format!(" {:?}\n", title));
|
|
||||||
output.push_str(&format!(" {:?}\n", suggestions));
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
output.push_str("Resolution: Pending\n");
|
|
||||||
}
|
|
||||||
Some(Err(error)) => {
|
|
||||||
writeln!(output, "Resolution: Error\n{:?}", error).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
output.push('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
let editor = self
|
|
||||||
.workspace
|
|
||||||
.update(cx, |workspace, cx| Editor::new_in_workspace(workspace, cx));
|
|
||||||
|
|
||||||
if let Ok(editor) = editor {
|
|
||||||
cx.spawn(|_, mut cx| async move {
|
|
||||||
let editor = editor.await?;
|
|
||||||
editor.update(&mut cx, |editor, cx| editor.set_text(output, cx))
|
|
||||||
})
|
|
||||||
.detach_and_notify_err(cx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cycle_message_role(&mut self, _: &CycleMessageRole, cx: &mut ViewContext<Self>) {
|
fn cycle_message_role(&mut self, _: &CycleMessageRole, cx: &mut ViewContext<Self>) {
|
||||||
let cursors = self.cursors(cx);
|
let cursors = self.cursors(cx);
|
||||||
self.context.update(cx, |context, cx| {
|
self.context.update(cx, |context, cx| {
|
||||||
|
@ -2482,9 +2389,6 @@ impl ContextEditor {
|
||||||
blocks_to_remove.insert(step.header_block_id);
|
blocks_to_remove.insert(step.header_block_id);
|
||||||
blocks_to_remove.insert(step.footer_block_id);
|
blocks_to_remove.insert(step.footer_block_id);
|
||||||
}
|
}
|
||||||
if let Some(debug) = self.debug_inspector.as_mut() {
|
|
||||||
debug.deactivate_for(step_range, cx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self.editor.update(cx, |editor, cx| {
|
self.editor.update(cx, |editor, cx| {
|
||||||
editor.remove_blocks(blocks_to_remove, None, cx)
|
editor.remove_blocks(blocks_to_remove, None, cx)
|
||||||
|
@ -2508,12 +2412,9 @@ impl ContextEditor {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let resolved_step = step.resolution.read(cx).result.clone();
|
let resolved_step = step.read(cx).resolution.clone();
|
||||||
if let Some(existing_step) = self.workflow_steps.get_mut(&step_range) {
|
if let Some(existing_step) = self.workflow_steps.get_mut(&step_range) {
|
||||||
existing_step.resolved_step = resolved_step;
|
existing_step.resolved_step = resolved_step;
|
||||||
if let Some(debug) = self.debug_inspector.as_mut() {
|
|
||||||
debug.refresh(&step_range, cx);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
let start = buffer_snapshot
|
let start = buffer_snapshot
|
||||||
.anchor_in_excerpt(excerpt_id, step_range.start)
|
.anchor_in_excerpt(excerpt_id, step_range.start)
|
||||||
|
@ -2553,51 +2454,80 @@ impl ContextEditor {
|
||||||
} else {
|
} else {
|
||||||
theme.info_border
|
theme.info_border
|
||||||
};
|
};
|
||||||
let step_index = weak_self.update(&mut **cx, |this, cx| {
|
let step_index = weak_self
|
||||||
let snapshot = this.editor.read(cx).buffer().read(cx).as_singleton()?.read(cx).text_snapshot();
|
.update(&mut **cx, |this, cx| {
|
||||||
let start_offset = step_range.start.to_offset(&snapshot);
|
let snapshot = this
|
||||||
let parent_message = this.context.read(cx).messages_for_offsets([start_offset], cx);
|
.editor
|
||||||
debug_assert_eq!(parent_message.len(), 1);
|
.read(cx)
|
||||||
let parent_message = parent_message.first()?;
|
.buffer()
|
||||||
|
.read(cx)
|
||||||
|
.as_singleton()?
|
||||||
|
.read(cx)
|
||||||
|
.text_snapshot();
|
||||||
|
let start_offset =
|
||||||
|
step_range.start.to_offset(&snapshot);
|
||||||
|
let parent_message = this
|
||||||
|
.context
|
||||||
|
.read(cx)
|
||||||
|
.messages_for_offsets([start_offset], cx);
|
||||||
|
debug_assert_eq!(parent_message.len(), 1);
|
||||||
|
let parent_message = parent_message.first()?;
|
||||||
|
|
||||||
let index_of_current_step = this.workflow_steps.keys().filter(|workflow_step_range| workflow_step_range.start.cmp(&parent_message.anchor, &snapshot).is_ge() && workflow_step_range.end.cmp(&step_range.end, &snapshot).is_le()).count();
|
let index_of_current_step = this
|
||||||
Some(index_of_current_step)
|
.workflow_steps
|
||||||
}).ok().flatten();
|
.keys()
|
||||||
|
.filter(|workflow_step_range| {
|
||||||
let debug_header = weak_self
|
workflow_step_range
|
||||||
.update(&mut **cx, |this, _| {
|
.start
|
||||||
if let Some(inspector) = this.debug_inspector.as_mut() {
|
.cmp(&parent_message.anchor, &snapshot)
|
||||||
Some(inspector.is_active(&step_range))
|
.is_ge()
|
||||||
} else {
|
&& workflow_step_range
|
||||||
None
|
.end
|
||||||
}
|
.cmp(&step_range.end, &snapshot)
|
||||||
|
.is_le()
|
||||||
|
})
|
||||||
|
.count();
|
||||||
|
Some(index_of_current_step)
|
||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.ok()
|
||||||
|
.flatten();
|
||||||
|
|
||||||
let step_label = if let Some(index) = step_index {
|
let step_label = if let Some(index) = step_index {
|
||||||
Label::new(format!("Step {index}")).size(LabelSize::Small)
|
Label::new(format!("Step {index}")).size(LabelSize::Small)
|
||||||
} else {
|
} else {
|
||||||
Label::new("Step").size(LabelSize::Small)
|
Label::new("Step").size(LabelSize::Small)
|
||||||
};
|
};
|
||||||
|
|
||||||
let step_label = if current_status.as_ref().is_some_and(|status| status.is_confirmed()) {
|
let step_label = if current_status
|
||||||
h_flex().items_center().gap_2().child(step_label.strikethrough(true).color(Color::Muted)).child(Icon::new(IconName::Check).size(IconSize::Small).color(Color::Created))
|
.as_ref()
|
||||||
|
.is_some_and(|status| status.is_confirmed())
|
||||||
|
{
|
||||||
|
h_flex()
|
||||||
|
.items_center()
|
||||||
|
.gap_2()
|
||||||
|
.child(
|
||||||
|
step_label.strikethrough(true).color(Color::Muted),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Icon::new(IconName::Check)
|
||||||
|
.size(IconSize::Small)
|
||||||
|
.color(Color::Created),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
div().child(step_label)
|
div().child(step_label)
|
||||||
};
|
};
|
||||||
|
|
||||||
let step_label = step_label.id("step")
|
let step_label = step_label
|
||||||
|
.id("step")
|
||||||
.cursor(CursorStyle::PointingHand)
|
.cursor(CursorStyle::PointingHand)
|
||||||
.on_click({
|
.on_click({
|
||||||
let this = weak_self.clone();
|
let this = weak_self.clone();
|
||||||
let step_range = step_range.clone();
|
let step_range = step_range.clone();
|
||||||
move |_, cx| {
|
move |_, cx| {
|
||||||
this
|
this.update(cx, |this, cx| {
|
||||||
.update(cx, |this, cx| {
|
this.open_workflow_step(step_range.clone(), cx);
|
||||||
this.open_workflow_step(
|
})
|
||||||
step_range.clone(), cx,
|
.ok();
|
||||||
);
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2614,42 +2544,12 @@ impl ContextEditor {
|
||||||
.items_center()
|
.items_center()
|
||||||
.justify_between()
|
.justify_between()
|
||||||
.gap_2()
|
.gap_2()
|
||||||
.child(h_flex().justify_start().gap_2().child(step_label).children(
|
.child(
|
||||||
debug_header.map(|is_active| {
|
h_flex()
|
||||||
|
.justify_start()
|
||||||
Button::new("debug-workflows-toggle", "Debug")
|
.gap_2()
|
||||||
.icon_color(Color::Hidden)
|
.child(step_label),
|
||||||
.color(Color::Hidden)
|
)
|
||||||
.selected_icon_color(Color::Default)
|
|
||||||
.selected_label_color(Color::Default)
|
|
||||||
.icon(IconName::Microscope)
|
|
||||||
.icon_position(IconPosition::Start)
|
|
||||||
.icon_size(IconSize::Small)
|
|
||||||
.label_size(LabelSize::Small)
|
|
||||||
.selected(is_active)
|
|
||||||
.on_click({
|
|
||||||
let weak_self = weak_self.clone();
|
|
||||||
let step_range = step_range.clone();
|
|
||||||
move |_, cx| {
|
|
||||||
weak_self
|
|
||||||
.update(cx, |this, cx| {
|
|
||||||
if let Some(inspector) =
|
|
||||||
this.debug_inspector
|
|
||||||
.as_mut()
|
|
||||||
{
|
|
||||||
if is_active {
|
|
||||||
|
|
||||||
inspector.deactivate_for(&step_range, cx);
|
|
||||||
} else {
|
|
||||||
inspector.activate_for_step(step_range.clone(), cx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
))
|
|
||||||
.children(current_status.as_ref().map(|status| {
|
.children(current_status.as_ref().map(|status| {
|
||||||
h_flex().w_full().justify_end().child(
|
h_flex().w_full().justify_end().child(
|
||||||
status.into_element(
|
status.into_element(
|
||||||
|
@ -2731,9 +2631,8 @@ impl ContextEditor {
|
||||||
let context = self.context.read(cx);
|
let context = self.context.read(cx);
|
||||||
let language_registry = context.language_registry();
|
let language_registry = context.language_registry();
|
||||||
let step = context.workflow_step_for_range(step_range)?;
|
let step = context.workflow_step_for_range(step_range)?;
|
||||||
let resolution = step.resolution.clone();
|
|
||||||
let view = cx.new_view(|cx| {
|
let view = cx.new_view(|cx| {
|
||||||
WorkflowStepView::new(self.context.clone(), resolution, language_registry, cx)
|
WorkflowStepView::new(self.context.clone(), step, language_registry, cx)
|
||||||
});
|
});
|
||||||
cx.deref_mut().defer(move |cx| {
|
cx.deref_mut().defer(move |cx| {
|
||||||
pane.update(cx, |pane, cx| {
|
pane.update(cx, |pane, cx| {
|
||||||
|
@ -2857,7 +2756,7 @@ impl ContextEditor {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_assists_for_step(
|
fn open_assists_for_step(
|
||||||
resolved_step: &ResolvedWorkflowStep,
|
resolved_step: &WorkflowStepResolution,
|
||||||
project: &Model<Project>,
|
project: &Model<Project>,
|
||||||
assistant_panel: &WeakView<AssistantPanel>,
|
assistant_panel: &WeakView<AssistantPanel>,
|
||||||
workspace: &WeakView<Workspace>,
|
workspace: &WeakView<Workspace>,
|
||||||
|
@ -2961,7 +2860,7 @@ impl ContextEditor {
|
||||||
let mut assist_ids = Vec::new();
|
let mut assist_ids = Vec::new();
|
||||||
for (excerpt_id, suggestion_group) in suggestion_groups {
|
for (excerpt_id, suggestion_group) in suggestion_groups {
|
||||||
for suggestion in &suggestion_group.suggestions {
|
for suggestion in &suggestion_group.suggestions {
|
||||||
assist_ids.extend(suggestion.kind.show(
|
assist_ids.extend(suggestion.show(
|
||||||
&editor,
|
&editor,
|
||||||
excerpt_id,
|
excerpt_id,
|
||||||
workspace,
|
workspace,
|
||||||
|
@ -3675,28 +3574,11 @@ impl ContextEditor {
|
||||||
fn active_workflow_step_for_cursor(&self, cx: &AppContext) -> Option<ActiveWorkflowStep> {
|
fn active_workflow_step_for_cursor(&self, cx: &AppContext) -> Option<ActiveWorkflowStep> {
|
||||||
let newest_cursor = self.editor.read(cx).selections.newest::<usize>(cx).head();
|
let newest_cursor = self.editor.read(cx).selections.newest::<usize>(cx).head();
|
||||||
let context = self.context.read(cx);
|
let context = self.context.read(cx);
|
||||||
let buffer = context.buffer().read(cx);
|
let (range, step) = context.workflow_step_containing(newest_cursor, cx)?;
|
||||||
|
Some(ActiveWorkflowStep {
|
||||||
let workflow_steps = context.workflow_steps();
|
resolved: step.read(cx).resolution.is_some(),
|
||||||
workflow_steps
|
range,
|
||||||
.binary_search_by(|step| {
|
})
|
||||||
let step_range = step.tagged_range.to_offset(&buffer);
|
|
||||||
if newest_cursor < step_range.start {
|
|
||||||
Ordering::Greater
|
|
||||||
} else if newest_cursor > step_range.end {
|
|
||||||
Ordering::Less
|
|
||||||
} else {
|
|
||||||
Ordering::Equal
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.ok()
|
|
||||||
.and_then(|index| {
|
|
||||||
let range = workflow_steps[index].tagged_range.clone();
|
|
||||||
Some(ActiveWorkflowStep {
|
|
||||||
resolved: self.workflow_steps.get(&range)?.resolved_step.is_some(),
|
|
||||||
range,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3724,7 +3606,6 @@ impl Render for ContextEditor {
|
||||||
.capture_action(cx.listener(ContextEditor::confirm_command))
|
.capture_action(cx.listener(ContextEditor::confirm_command))
|
||||||
.on_action(cx.listener(ContextEditor::assist))
|
.on_action(cx.listener(ContextEditor::assist))
|
||||||
.on_action(cx.listener(ContextEditor::split))
|
.on_action(cx.listener(ContextEditor::split))
|
||||||
.on_action(cx.listener(ContextEditor::debug_workflow_steps))
|
|
||||||
.size_full()
|
.size_full()
|
||||||
.children(self.render_notice(cx))
|
.children(self.render_notice(cx))
|
||||||
.child(
|
.child(
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
prompts::PromptBuilder, slash_command::SlashCommandLine, workflow::WorkflowStepResolution,
|
prompts::PromptBuilder, slash_command::SlashCommandLine, workflow::WorkflowStep, MessageId,
|
||||||
MessageId, MessageStatus,
|
MessageStatus,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Context as _, Result};
|
use anyhow::{anyhow, Context as _, Result};
|
||||||
use assistant_slash_command::{
|
use assistant_slash_command::{
|
||||||
|
@ -382,12 +382,6 @@ pub struct ImageAnchor {
|
||||||
pub image: Shared<Task<Option<LanguageModelImage>>>,
|
pub image: Shared<Task<Option<LanguageModelImage>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for ImageAnchor {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.image_id == other.image_id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct PendingCompletion {
|
struct PendingCompletion {
|
||||||
id: usize,
|
id: usize,
|
||||||
assistant_message_id: MessageId,
|
assistant_message_id: MessageId,
|
||||||
|
@ -397,18 +391,9 @@ struct PendingCompletion {
|
||||||
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
|
||||||
pub struct SlashCommandId(clock::Lamport);
|
pub struct SlashCommandId(clock::Lamport);
|
||||||
|
|
||||||
pub struct WorkflowStep {
|
struct WorkflowStepEntry {
|
||||||
pub tagged_range: Range<language::Anchor>,
|
range: Range<language::Anchor>,
|
||||||
pub resolution: Model<WorkflowStepResolution>,
|
step: Model<WorkflowStep>,
|
||||||
pub _task: Option<Task<()>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for WorkflowStep {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.debug_struct("WorkflowStep")
|
|
||||||
.field("tagged_range", &self.tagged_range)
|
|
||||||
.finish_non_exhaustive()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
|
@ -437,7 +422,7 @@ pub struct Context {
|
||||||
_subscriptions: Vec<Subscription>,
|
_subscriptions: Vec<Subscription>,
|
||||||
telemetry: Option<Arc<Telemetry>>,
|
telemetry: Option<Arc<Telemetry>>,
|
||||||
language_registry: Arc<LanguageRegistry>,
|
language_registry: Arc<LanguageRegistry>,
|
||||||
workflow_steps: Vec<WorkflowStep>,
|
workflow_steps: Vec<WorkflowStepEntry>,
|
||||||
edits_since_last_workflow_step_prune: language::Subscription,
|
edits_since_last_workflow_step_prune: language::Subscription,
|
||||||
project: Option<Model<Project>>,
|
project: Option<Model<Project>>,
|
||||||
prompt_builder: Arc<PromptBuilder>,
|
prompt_builder: Arc<PromptBuilder>,
|
||||||
|
@ -858,14 +843,40 @@ impl Context {
|
||||||
self.summary.as_ref()
|
self.summary.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn workflow_steps(&self) -> &[WorkflowStep] {
|
pub fn workflow_step_containing(
|
||||||
&self.workflow_steps
|
&self,
|
||||||
|
offset: usize,
|
||||||
|
cx: &AppContext,
|
||||||
|
) -> Option<(Range<language::Anchor>, Model<WorkflowStep>)> {
|
||||||
|
let buffer = self.buffer.read(cx);
|
||||||
|
let index = self
|
||||||
|
.workflow_steps
|
||||||
|
.binary_search_by(|step| {
|
||||||
|
let step_range = step.range.to_offset(&buffer);
|
||||||
|
if offset < step_range.start {
|
||||||
|
Ordering::Greater
|
||||||
|
} else if offset > step_range.end {
|
||||||
|
Ordering::Less
|
||||||
|
} else {
|
||||||
|
Ordering::Equal
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.ok()?;
|
||||||
|
let step = &self.workflow_steps[index];
|
||||||
|
Some((step.range.clone(), step.step.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn workflow_step_for_range(&self, range: Range<language::Anchor>) -> Option<&WorkflowStep> {
|
pub fn workflow_step_for_range(
|
||||||
self.workflow_steps
|
&self,
|
||||||
.iter()
|
range: Range<language::Anchor>,
|
||||||
.find(|step| step.tagged_range == range)
|
) -> Option<Model<WorkflowStep>> {
|
||||||
|
Some(
|
||||||
|
self.workflow_steps
|
||||||
|
.iter()
|
||||||
|
.find(|step| step.range == range)?
|
||||||
|
.step
|
||||||
|
.clone(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pending_slash_commands(&self) -> &[PendingSlashCommand] {
|
pub fn pending_slash_commands(&self) -> &[PendingSlashCommand] {
|
||||||
|
@ -1028,7 +1039,7 @@ impl Context {
|
||||||
removed.extend(
|
removed.extend(
|
||||||
self.workflow_steps
|
self.workflow_steps
|
||||||
.drain(intersecting_range)
|
.drain(intersecting_range)
|
||||||
.map(|step| step.tagged_range),
|
.map(|step| step.range),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1047,7 +1058,7 @@ impl Context {
|
||||||
let buffer = self.buffer.read(cx);
|
let buffer = self.buffer.read(cx);
|
||||||
let start_ix = match self.workflow_steps.binary_search_by(|probe| {
|
let start_ix = match self.workflow_steps.binary_search_by(|probe| {
|
||||||
probe
|
probe
|
||||||
.tagged_range
|
.range
|
||||||
.end
|
.end
|
||||||
.to_offset(buffer)
|
.to_offset(buffer)
|
||||||
.cmp(&range.start)
|
.cmp(&range.start)
|
||||||
|
@ -1061,7 +1072,7 @@ impl Context {
|
||||||
};
|
};
|
||||||
let end_ix = match self.workflow_steps.binary_search_by(|probe| {
|
let end_ix = match self.workflow_steps.binary_search_by(|probe| {
|
||||||
probe
|
probe
|
||||||
.tagged_range
|
.range
|
||||||
.start
|
.start
|
||||||
.to_offset(buffer)
|
.to_offset(buffer)
|
||||||
.cmp(&range.end)
|
.cmp(&range.end)
|
||||||
|
@ -1114,20 +1125,16 @@ impl Context {
|
||||||
// Check if a step with the same range already exists
|
// Check if a step with the same range already exists
|
||||||
let existing_step_index = self
|
let existing_step_index = self
|
||||||
.workflow_steps
|
.workflow_steps
|
||||||
.binary_search_by(|probe| probe.tagged_range.cmp(&tagged_range, &buffer));
|
.binary_search_by(|probe| probe.range.cmp(&tagged_range, &buffer));
|
||||||
|
|
||||||
if let Err(ix) = existing_step_index {
|
if let Err(ix) = existing_step_index {
|
||||||
new_edit_steps.push((
|
new_edit_steps.push((
|
||||||
ix,
|
ix,
|
||||||
WorkflowStep {
|
WorkflowStepEntry {
|
||||||
resolution: cx.new_model(|_| {
|
step: cx.new_model(|_| {
|
||||||
WorkflowStepResolution::new(
|
WorkflowStep::new(tagged_range.clone(), weak_self.clone())
|
||||||
tagged_range.clone(),
|
|
||||||
weak_self.clone(),
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
tagged_range,
|
range: tagged_range,
|
||||||
_task: None,
|
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -1141,7 +1148,7 @@ impl Context {
|
||||||
|
|
||||||
let mut updated = Vec::new();
|
let mut updated = Vec::new();
|
||||||
for (index, step) in new_edit_steps.into_iter().rev() {
|
for (index, step) in new_edit_steps.into_iter().rev() {
|
||||||
let step_range = step.tagged_range.clone();
|
let step_range = step.range.clone();
|
||||||
updated.push(step_range.clone());
|
updated.push(step_range.clone());
|
||||||
self.workflow_steps.insert(index, step);
|
self.workflow_steps.insert(index, step);
|
||||||
self.resolve_workflow_step(step_range, cx);
|
self.resolve_workflow_step(step_range, cx);
|
||||||
|
@ -1161,7 +1168,7 @@ impl Context {
|
||||||
) {
|
) {
|
||||||
let Ok(step_index) = self
|
let Ok(step_index) = self
|
||||||
.workflow_steps
|
.workflow_steps
|
||||||
.binary_search_by(|step| step.tagged_range.cmp(&tagged_range, self.buffer.read(cx)))
|
.binary_search_by(|step| step.range.cmp(&tagged_range, self.buffer.read(cx)))
|
||||||
else {
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
@ -1169,7 +1176,7 @@ impl Context {
|
||||||
cx.emit(ContextEvent::WorkflowStepUpdated(tagged_range.clone()));
|
cx.emit(ContextEvent::WorkflowStepUpdated(tagged_range.clone()));
|
||||||
cx.notify();
|
cx.notify();
|
||||||
|
|
||||||
let resolution = self.workflow_steps[step_index].resolution.clone();
|
let resolution = self.workflow_steps[step_index].step.clone();
|
||||||
cx.defer(move |cx| {
|
cx.defer(move |cx| {
|
||||||
resolution.update(cx, |resolution, cx| resolution.resolve(cx));
|
resolution.update(cx, |resolution, cx| resolution.resolve(cx));
|
||||||
});
|
});
|
||||||
|
@ -3032,12 +3039,12 @@ mod tests {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|step| {
|
.map(|step| {
|
||||||
let buffer = context.buffer.read(cx);
|
let buffer = context.buffer.read(cx);
|
||||||
let status = match &step.resolution.read(cx).result {
|
let status = match &step.step.read(cx).resolution {
|
||||||
None => WorkflowStepTestStatus::Pending,
|
None => WorkflowStepTestStatus::Pending,
|
||||||
Some(Ok(_)) => WorkflowStepTestStatus::Resolved,
|
Some(Ok(_)) => WorkflowStepTestStatus::Resolved,
|
||||||
Some(Err(_)) => WorkflowStepTestStatus::Error,
|
Some(Err(_)) => WorkflowStepTestStatus::Error,
|
||||||
};
|
};
|
||||||
(step.tagged_range.to_point(buffer), status)
|
(step.range.to_point(buffer), status)
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,241 +0,0 @@
|
||||||
use std::{ops::Range, sync::Arc};
|
|
||||||
|
|
||||||
use collections::{HashMap, HashSet};
|
|
||||||
use editor::{
|
|
||||||
display_map::{BlockDisposition, BlockProperties, BlockStyle, CustomBlockId},
|
|
||||||
Editor,
|
|
||||||
};
|
|
||||||
use gpui::{AppContext, Model, View};
|
|
||||||
use text::{Bias, ToOffset, ToPoint};
|
|
||||||
use ui::{
|
|
||||||
div, h_flex, px, Color, Element as _, ParentElement as _, Styled, ViewContext, WindowContext,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{Context, ResolvedWorkflowStep, WorkflowSuggestion, WorkflowSuggestionKind};
|
|
||||||
|
|
||||||
type StepRange = Range<language::Anchor>;
|
|
||||||
|
|
||||||
struct DebugInfo {
|
|
||||||
range: Range<editor::Anchor>,
|
|
||||||
block_id: CustomBlockId,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct ContextInspector {
|
|
||||||
active_debug_views: HashMap<Range<language::Anchor>, DebugInfo>,
|
|
||||||
context: Model<Context>,
|
|
||||||
editor: View<Editor>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ContextInspector {
|
|
||||||
pub(crate) fn new(editor: View<Editor>, context: Model<Context>) -> Self {
|
|
||||||
Self {
|
|
||||||
editor,
|
|
||||||
context,
|
|
||||||
active_debug_views: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn is_active(&self, range: &StepRange) -> bool {
|
|
||||||
self.active_debug_views.contains_key(range)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn refresh(&mut self, range: &StepRange, cx: &mut WindowContext<'_>) {
|
|
||||||
if self.deactivate_for(range, cx) {
|
|
||||||
self.activate_for_step(range.clone(), cx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn crease_content(
|
|
||||||
context: &Model<Context>,
|
|
||||||
range: StepRange,
|
|
||||||
cx: &mut AppContext,
|
|
||||||
) -> Option<Arc<str>> {
|
|
||||||
use std::fmt::Write;
|
|
||||||
let step = context.read(cx).workflow_step_for_range(range)?;
|
|
||||||
let mut output = String::from("\n\n");
|
|
||||||
match &step.resolution.read(cx).result {
|
|
||||||
Some(Ok(ResolvedWorkflowStep {
|
|
||||||
title,
|
|
||||||
suggestion_groups: suggestions,
|
|
||||||
})) => {
|
|
||||||
writeln!(output, "Resolution:").ok()?;
|
|
||||||
writeln!(output, " {title:?}").ok()?;
|
|
||||||
if suggestions.is_empty() {
|
|
||||||
writeln!(output, " No suggestions").ok()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (buffer, suggestion_groups) in suggestions {
|
|
||||||
let buffer = buffer.read(cx);
|
|
||||||
let buffer_path = buffer
|
|
||||||
.file()
|
|
||||||
.and_then(|file| file.path().to_str())
|
|
||||||
.unwrap_or("untitled");
|
|
||||||
let snapshot = buffer.text_snapshot();
|
|
||||||
writeln!(output, "Path: {buffer_path}:").ok()?;
|
|
||||||
for group in suggestion_groups {
|
|
||||||
for suggestion in &group.suggestions {
|
|
||||||
pretty_print_workflow_suggestion(&mut output, suggestion, &snapshot);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(Err(error)) => {
|
|
||||||
writeln!(output, "Resolution: Error").ok()?;
|
|
||||||
writeln!(output, "{error:?}").ok()?;
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
writeln!(output, "Resolution: Pending").ok()?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(output.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn activate_for_step(&mut self, range: StepRange, cx: &mut WindowContext<'_>) {
|
|
||||||
let text = Self::crease_content(&self.context, range.clone(), cx)
|
|
||||||
.unwrap_or_else(|| Arc::from("Error fetching debug info"));
|
|
||||||
self.editor.update(cx, |editor, cx| {
|
|
||||||
let buffer = editor.buffer().read(cx).as_singleton()?;
|
|
||||||
let snapshot = buffer.read(cx).text_snapshot();
|
|
||||||
let start_offset = range.end.to_offset(&snapshot) + 1;
|
|
||||||
let start_offset = snapshot.clip_offset(start_offset, Bias::Right);
|
|
||||||
let text_len = text.len();
|
|
||||||
buffer.update(cx, |this, cx| {
|
|
||||||
this.edit([(start_offset..start_offset, text)], None, cx);
|
|
||||||
});
|
|
||||||
|
|
||||||
let end_offset = start_offset + text_len;
|
|
||||||
let multibuffer_snapshot = editor.buffer().read(cx).snapshot(cx);
|
|
||||||
let anchor_before = multibuffer_snapshot.anchor_after(start_offset);
|
|
||||||
let anchor_after = multibuffer_snapshot.anchor_before(end_offset);
|
|
||||||
|
|
||||||
let block_id = editor
|
|
||||||
.insert_blocks(
|
|
||||||
[BlockProperties {
|
|
||||||
position: anchor_after,
|
|
||||||
height: 0,
|
|
||||||
style: BlockStyle::Sticky,
|
|
||||||
render: Box::new(move |cx| {
|
|
||||||
div()
|
|
||||||
.w_full()
|
|
||||||
.px(cx.gutter_dimensions.full_width())
|
|
||||||
.child(h_flex().h(px(1.)).bg(Color::Warning.color(cx)))
|
|
||||||
.into_any()
|
|
||||||
}),
|
|
||||||
disposition: BlockDisposition::Below,
|
|
||||||
priority: 0,
|
|
||||||
}],
|
|
||||||
None,
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
.into_iter()
|
|
||||||
.next()?;
|
|
||||||
let info = DebugInfo {
|
|
||||||
range: anchor_before..anchor_after,
|
|
||||||
block_id,
|
|
||||||
};
|
|
||||||
self.active_debug_views.insert(range, info);
|
|
||||||
Some(())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deactivate_impl(editor: &mut Editor, debug_data: DebugInfo, cx: &mut ViewContext<Editor>) {
|
|
||||||
editor.remove_blocks(HashSet::from_iter([debug_data.block_id]), None, cx);
|
|
||||||
editor.edit([(debug_data.range, Arc::<str>::default())], cx)
|
|
||||||
}
|
|
||||||
pub(crate) fn deactivate_for(&mut self, range: &StepRange, cx: &mut WindowContext<'_>) -> bool {
|
|
||||||
if let Some(debug_data) = self.active_debug_views.remove(range) {
|
|
||||||
self.editor.update(cx, |this, cx| {
|
|
||||||
Self::deactivate_impl(this, debug_data, cx);
|
|
||||||
});
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn deactivate(&mut self, cx: &mut WindowContext<'_>) {
|
|
||||||
let steps_to_disable = std::mem::take(&mut self.active_debug_views);
|
|
||||||
|
|
||||||
self.editor.update(cx, move |editor, cx| {
|
|
||||||
for (_, debug_data) in steps_to_disable {
|
|
||||||
Self::deactivate_impl(editor, debug_data, cx);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn pretty_print_anchor(
|
|
||||||
out: &mut String,
|
|
||||||
anchor: language::Anchor,
|
|
||||||
snapshot: &text::BufferSnapshot,
|
|
||||||
) {
|
|
||||||
use std::fmt::Write;
|
|
||||||
let point = anchor.to_point(snapshot);
|
|
||||||
write!(out, "{}:{}", point.row, point.column).ok();
|
|
||||||
}
|
|
||||||
fn pretty_print_range(
|
|
||||||
out: &mut String,
|
|
||||||
range: &Range<language::Anchor>,
|
|
||||||
snapshot: &text::BufferSnapshot,
|
|
||||||
) {
|
|
||||||
use std::fmt::Write;
|
|
||||||
write!(out, " Range: ").ok();
|
|
||||||
pretty_print_anchor(out, range.start, snapshot);
|
|
||||||
write!(out, "..").ok();
|
|
||||||
pretty_print_anchor(out, range.end, snapshot);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pretty_print_workflow_suggestion(
|
|
||||||
out: &mut String,
|
|
||||||
suggestion: &WorkflowSuggestion,
|
|
||||||
snapshot: &text::BufferSnapshot,
|
|
||||||
) {
|
|
||||||
use std::fmt::Write;
|
|
||||||
let (position, description, range) = match &suggestion.kind {
|
|
||||||
WorkflowSuggestionKind::Update {
|
|
||||||
range, description, ..
|
|
||||||
} => (None, Some(description), Some(range)),
|
|
||||||
WorkflowSuggestionKind::CreateFile { description } => (None, Some(description), None),
|
|
||||||
WorkflowSuggestionKind::AppendChild {
|
|
||||||
position,
|
|
||||||
description,
|
|
||||||
..
|
|
||||||
} => (Some(position), Some(description), None),
|
|
||||||
WorkflowSuggestionKind::InsertSiblingBefore {
|
|
||||||
position,
|
|
||||||
description,
|
|
||||||
..
|
|
||||||
} => (Some(position), Some(description), None),
|
|
||||||
WorkflowSuggestionKind::InsertSiblingAfter {
|
|
||||||
position,
|
|
||||||
description,
|
|
||||||
..
|
|
||||||
} => (Some(position), Some(description), None),
|
|
||||||
WorkflowSuggestionKind::PrependChild {
|
|
||||||
position,
|
|
||||||
description,
|
|
||||||
..
|
|
||||||
} => (Some(position), Some(description), None),
|
|
||||||
WorkflowSuggestionKind::Delete { range, .. } => (None, None, Some(range)),
|
|
||||||
};
|
|
||||||
writeln!(out, " Tool input: {}", suggestion.tool_input).ok();
|
|
||||||
writeln!(
|
|
||||||
out,
|
|
||||||
" Tool output: {}",
|
|
||||||
serde_json::to_string_pretty(&suggestion.tool_output)
|
|
||||||
.expect("Should not fail on valid struct serialization")
|
|
||||||
)
|
|
||||||
.ok();
|
|
||||||
if let Some(description) = description {
|
|
||||||
writeln!(out, " Description: {description}").ok();
|
|
||||||
}
|
|
||||||
if let Some(range) = range {
|
|
||||||
pretty_print_range(out, &range, snapshot);
|
|
||||||
}
|
|
||||||
if let Some(position) = position {
|
|
||||||
write!(out, " Position: ").ok();
|
|
||||||
pretty_print_anchor(out, *position, snapshot);
|
|
||||||
write!(out, "\n").ok();
|
|
||||||
}
|
|
||||||
write!(out, "\n").ok();
|
|
||||||
}
|
|
|
@ -8,8 +8,7 @@ use collections::HashMap;
|
||||||
use editor::Editor;
|
use editor::Editor;
|
||||||
use futures::future;
|
use futures::future;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
AppContext, Model, ModelContext, Task, UpdateGlobal as _, View, WeakModel, WeakView,
|
Model, ModelContext, Task, UpdateGlobal as _, View, WeakModel, WeakView, WindowContext,
|
||||||
WindowContext,
|
|
||||||
};
|
};
|
||||||
use language::{Anchor, Buffer, BufferSnapshot, SymbolPath};
|
use language::{Anchor, Buffer, BufferSnapshot, SymbolPath};
|
||||||
use language_model::{LanguageModelRegistry, LanguageModelRequestMessage, Role};
|
use language_model::{LanguageModelRegistry, LanguageModelRequestMessage, Role};
|
||||||
|
@ -24,16 +23,16 @@ use workspace::Workspace;
|
||||||
|
|
||||||
pub use step_view::WorkflowStepView;
|
pub use step_view::WorkflowStepView;
|
||||||
|
|
||||||
pub struct WorkflowStepResolution {
|
pub struct WorkflowStep {
|
||||||
tagged_range: Range<Anchor>,
|
|
||||||
output: String,
|
|
||||||
context: WeakModel<Context>,
|
context: WeakModel<Context>,
|
||||||
|
context_buffer_range: Range<Anchor>,
|
||||||
|
tool_output: String,
|
||||||
resolve_task: Option<Task<()>>,
|
resolve_task: Option<Task<()>>,
|
||||||
pub result: Option<Result<ResolvedWorkflowStep, Arc<Error>>>,
|
pub resolution: Option<Result<WorkflowStepResolution, Arc<Error>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct ResolvedWorkflowStep {
|
pub struct WorkflowStepResolution {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub suggestion_groups: HashMap<Model<Buffer>, Vec<WorkflowSuggestionGroup>>,
|
pub suggestion_groups: HashMap<Model<Buffer>, Vec<WorkflowSuggestionGroup>>,
|
||||||
}
|
}
|
||||||
|
@ -45,36 +44,7 @@ pub struct WorkflowSuggestionGroup {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct WorkflowSuggestion {
|
pub enum WorkflowSuggestion {
|
||||||
pub kind: WorkflowSuggestionKind,
|
|
||||||
pub tool_input: String,
|
|
||||||
pub tool_output: tool::WorkflowSuggestionTool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WorkflowSuggestion {
|
|
||||||
pub fn range(&self) -> Range<Anchor> {
|
|
||||||
self.kind.range()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn show(
|
|
||||||
&self,
|
|
||||||
editor: &View<Editor>,
|
|
||||||
excerpt_id: editor::ExcerptId,
|
|
||||||
workspace: &WeakView<Workspace>,
|
|
||||||
assistant_panel: &View<AssistantPanel>,
|
|
||||||
cx: &mut ui::ViewContext<crate::assistant_panel::ContextEditor>,
|
|
||||||
) -> Option<InlineAssistId> {
|
|
||||||
self.kind
|
|
||||||
.show(editor, excerpt_id, workspace, assistant_panel, cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_merge(&mut self, other: &mut WorkflowSuggestion, snapshot: &BufferSnapshot) -> bool {
|
|
||||||
self.kind.try_merge(&other.kind, snapshot)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
||||||
pub enum WorkflowSuggestionKind {
|
|
||||||
Update {
|
Update {
|
||||||
symbol_path: SymbolPath,
|
symbol_path: SymbolPath,
|
||||||
range: Range<language::Anchor>,
|
range: Range<language::Anchor>,
|
||||||
|
@ -109,28 +79,19 @@ pub enum WorkflowSuggestionKind {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WorkflowStepResolution {
|
impl WorkflowStep {
|
||||||
pub fn new(range: Range<Anchor>, context: WeakModel<Context>) -> Self {
|
pub fn new(range: Range<Anchor>, context: WeakModel<Context>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
tagged_range: range,
|
context_buffer_range: range,
|
||||||
output: String::new(),
|
tool_output: String::new(),
|
||||||
context,
|
context,
|
||||||
result: None,
|
resolution: None,
|
||||||
resolve_task: None,
|
resolve_task: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn step_text(&self, context: &Context, cx: &AppContext) -> String {
|
pub fn resolve(&mut self, cx: &mut ModelContext<WorkflowStep>) -> Option<()> {
|
||||||
context
|
let range = self.context_buffer_range.clone();
|
||||||
.buffer()
|
|
||||||
.clone()
|
|
||||||
.read(cx)
|
|
||||||
.text_for_range(self.tagged_range.clone())
|
|
||||||
.collect::<String>()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resolve(&mut self, cx: &mut ModelContext<WorkflowStepResolution>) -> Option<()> {
|
|
||||||
let range = self.tagged_range.clone();
|
|
||||||
let context = self.context.upgrade()?;
|
let context = self.context.upgrade()?;
|
||||||
let context = context.read(cx);
|
let context = context.read(cx);
|
||||||
let project = context.project()?;
|
let project = context.project()?;
|
||||||
|
@ -159,8 +120,8 @@ impl WorkflowStepResolution {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
this.output.clear();
|
this.tool_output.clear();
|
||||||
this.result = None;
|
this.resolution = None;
|
||||||
this.result_updated(cx);
|
this.result_updated(cx);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})?;
|
})?;
|
||||||
|
@ -184,17 +145,17 @@ impl WorkflowStepResolution {
|
||||||
while let Some(chunk) = stream.next().await {
|
while let Some(chunk) = stream.next().await {
|
||||||
let chunk = chunk?;
|
let chunk = chunk?;
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
this.output.push_str(&chunk);
|
this.tool_output.push_str(&chunk);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let resolution = this.update(&mut cx, |this, _| {
|
let resolution = this.update(&mut cx, |this, _| {
|
||||||
serde_json::from_str::<tool::WorkflowStepResolutionTool>(&this.output)
|
serde_json::from_str::<tool::WorkflowStepResolutionTool>(&this.tool_output)
|
||||||
})??;
|
})??;
|
||||||
|
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
this.output = serde_json::to_string_pretty(&resolution).unwrap();
|
this.tool_output = serde_json::to_string_pretty(&resolution).unwrap();
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
@ -202,9 +163,7 @@ impl WorkflowStepResolution {
|
||||||
let suggestion_tasks: Vec<_> = resolution
|
let suggestion_tasks: Vec<_> = resolution
|
||||||
.suggestions
|
.suggestions
|
||||||
.iter()
|
.iter()
|
||||||
.map(|suggestion| {
|
.map(|suggestion| suggestion.resolve(project.clone(), cx.clone()))
|
||||||
suggestion.resolve(step_text.clone(), project.clone(), cx.clone())
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// Expand the context ranges of each suggestion and group suggestions with overlapping context ranges.
|
// Expand the context ranges of each suggestion and group suggestions with overlapping context ranges.
|
||||||
|
@ -281,8 +240,8 @@ impl WorkflowStepResolution {
|
||||||
|
|
||||||
let result = result.await;
|
let result = result.await;
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
this.result = Some(match result {
|
this.resolution = Some(match result {
|
||||||
Ok((title, suggestion_groups)) => Ok(ResolvedWorkflowStep {
|
Ok((title, suggestion_groups)) => Ok(WorkflowStepResolution {
|
||||||
title,
|
title,
|
||||||
suggestion_groups,
|
suggestion_groups,
|
||||||
}),
|
}),
|
||||||
|
@ -301,13 +260,13 @@ impl WorkflowStepResolution {
|
||||||
fn result_updated(&mut self, cx: &mut ModelContext<Self>) {
|
fn result_updated(&mut self, cx: &mut ModelContext<Self>) {
|
||||||
self.context
|
self.context
|
||||||
.update(cx, |context, cx| {
|
.update(cx, |context, cx| {
|
||||||
context.workflow_step_updated(self.tagged_range.clone(), cx)
|
context.workflow_step_updated(self.context_buffer_range.clone(), cx)
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WorkflowSuggestionKind {
|
impl WorkflowSuggestion {
|
||||||
pub fn range(&self) -> Range<language::Anchor> {
|
pub fn range(&self) -> Range<language::Anchor> {
|
||||||
match self {
|
match self {
|
||||||
Self::Update { range, .. } => range.clone(),
|
Self::Update { range, .. } => range.clone(),
|
||||||
|
@ -504,8 +463,6 @@ impl WorkflowSuggestionKind {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod tool {
|
pub mod tool {
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use anyhow::Context as _;
|
use anyhow::Context as _;
|
||||||
use gpui::AsyncAppContext;
|
use gpui::AsyncAppContext;
|
||||||
|
@ -513,6 +470,7 @@ pub mod tool {
|
||||||
use language_model::LanguageModelTool;
|
use language_model::LanguageModelTool;
|
||||||
use project::ProjectPath;
|
use project::ProjectPath;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
pub struct WorkflowStepResolutionTool {
|
pub struct WorkflowStepResolutionTool {
|
||||||
|
@ -562,7 +520,6 @@ pub mod tool {
|
||||||
impl WorkflowSuggestionTool {
|
impl WorkflowSuggestionTool {
|
||||||
pub(super) async fn resolve(
|
pub(super) async fn resolve(
|
||||||
&self,
|
&self,
|
||||||
tool_input: String,
|
|
||||||
project: Model<Project>,
|
project: Model<Project>,
|
||||||
mut cx: AsyncAppContext,
|
mut cx: AsyncAppContext,
|
||||||
) -> Result<(Model<Buffer>, super::WorkflowSuggestion)> {
|
) -> Result<(Model<Buffer>, super::WorkflowSuggestion)> {
|
||||||
|
@ -599,7 +556,7 @@ pub mod tool {
|
||||||
let snapshot = buffer.update(&mut cx, |buffer, _| buffer.snapshot())?;
|
let snapshot = buffer.update(&mut cx, |buffer, _| buffer.snapshot())?;
|
||||||
let outline = snapshot.outline(None).context("no outline for buffer")?;
|
let outline = snapshot.outline(None).context("no outline for buffer")?;
|
||||||
|
|
||||||
let kind = match kind {
|
let suggestion = match kind {
|
||||||
WorkflowSuggestionToolKind::Update {
|
WorkflowSuggestionToolKind::Update {
|
||||||
symbol,
|
symbol,
|
||||||
description,
|
description,
|
||||||
|
@ -617,14 +574,14 @@ pub mod tool {
|
||||||
snapshot.line_len(symbol.range.end.row),
|
snapshot.line_len(symbol.range.end.row),
|
||||||
);
|
);
|
||||||
let range = snapshot.anchor_before(start)..snapshot.anchor_after(end);
|
let range = snapshot.anchor_before(start)..snapshot.anchor_after(end);
|
||||||
WorkflowSuggestionKind::Update {
|
WorkflowSuggestion::Update {
|
||||||
range,
|
range,
|
||||||
description,
|
description,
|
||||||
symbol_path,
|
symbol_path,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WorkflowSuggestionToolKind::Create { description } => {
|
WorkflowSuggestionToolKind::Create { description } => {
|
||||||
WorkflowSuggestionKind::CreateFile { description }
|
WorkflowSuggestion::CreateFile { description }
|
||||||
}
|
}
|
||||||
WorkflowSuggestionToolKind::InsertSiblingBefore {
|
WorkflowSuggestionToolKind::InsertSiblingBefore {
|
||||||
symbol,
|
symbol,
|
||||||
|
@ -641,7 +598,7 @@ pub mod tool {
|
||||||
annotation_range.start
|
annotation_range.start
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
WorkflowSuggestionKind::InsertSiblingBefore {
|
WorkflowSuggestion::InsertSiblingBefore {
|
||||||
position,
|
position,
|
||||||
description,
|
description,
|
||||||
symbol_path,
|
symbol_path,
|
||||||
|
@ -656,7 +613,7 @@ pub mod tool {
|
||||||
.with_context(|| format!("symbol not found: {:?}", symbol))?;
|
.with_context(|| format!("symbol not found: {:?}", symbol))?;
|
||||||
let symbol = symbol.to_point(&snapshot);
|
let symbol = symbol.to_point(&snapshot);
|
||||||
let position = snapshot.anchor_after(symbol.range.end);
|
let position = snapshot.anchor_after(symbol.range.end);
|
||||||
WorkflowSuggestionKind::InsertSiblingAfter {
|
WorkflowSuggestion::InsertSiblingAfter {
|
||||||
position,
|
position,
|
||||||
description,
|
description,
|
||||||
symbol_path,
|
symbol_path,
|
||||||
|
@ -677,13 +634,13 @@ pub mod tool {
|
||||||
.body_range
|
.body_range
|
||||||
.map_or(symbol.range.start, |body_range| body_range.start),
|
.map_or(symbol.range.start, |body_range| body_range.start),
|
||||||
);
|
);
|
||||||
WorkflowSuggestionKind::PrependChild {
|
WorkflowSuggestion::PrependChild {
|
||||||
position,
|
position,
|
||||||
description,
|
description,
|
||||||
symbol_path: Some(symbol_path),
|
symbol_path: Some(symbol_path),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
WorkflowSuggestionKind::PrependChild {
|
WorkflowSuggestion::PrependChild {
|
||||||
position: language::Anchor::MIN,
|
position: language::Anchor::MIN,
|
||||||
description,
|
description,
|
||||||
symbol_path: None,
|
symbol_path: None,
|
||||||
|
@ -705,13 +662,13 @@ pub mod tool {
|
||||||
.body_range
|
.body_range
|
||||||
.map_or(symbol.range.end, |body_range| body_range.end),
|
.map_or(symbol.range.end, |body_range| body_range.end),
|
||||||
);
|
);
|
||||||
WorkflowSuggestionKind::AppendChild {
|
WorkflowSuggestion::AppendChild {
|
||||||
position,
|
position,
|
||||||
description,
|
description,
|
||||||
symbol_path: Some(symbol_path),
|
symbol_path: Some(symbol_path),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
WorkflowSuggestionKind::PrependChild {
|
WorkflowSuggestion::PrependChild {
|
||||||
position: language::Anchor::MAX,
|
position: language::Anchor::MAX,
|
||||||
description,
|
description,
|
||||||
symbol_path: None,
|
symbol_path: None,
|
||||||
|
@ -732,16 +689,10 @@ pub mod tool {
|
||||||
snapshot.line_len(symbol.range.end.row),
|
snapshot.line_len(symbol.range.end.row),
|
||||||
);
|
);
|
||||||
let range = snapshot.anchor_before(start)..snapshot.anchor_after(end);
|
let range = snapshot.anchor_before(start)..snapshot.anchor_after(end);
|
||||||
WorkflowSuggestionKind::Delete { range, symbol_path }
|
WorkflowSuggestion::Delete { range, symbol_path }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let suggestion = WorkflowSuggestion {
|
|
||||||
kind,
|
|
||||||
tool_output: self.clone(),
|
|
||||||
tool_input,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok((buffer, suggestion))
|
Ok((buffer, suggestion))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use super::WorkflowStepResolution;
|
use super::WorkflowStep;
|
||||||
use crate::{Assist, Context};
|
use crate::{Assist, Context};
|
||||||
use editor::{
|
use editor::{
|
||||||
display_map::{BlockDisposition, BlockProperties, BlockStyle},
|
display_map::{BlockDisposition, BlockProperties, BlockStyle},
|
||||||
|
@ -23,7 +23,7 @@ use workspace::{
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct WorkflowStepView {
|
pub struct WorkflowStepView {
|
||||||
step: WeakModel<WorkflowStepResolution>,
|
step: WeakModel<WorkflowStep>,
|
||||||
tool_output_buffer: Model<Buffer>,
|
tool_output_buffer: Model<Buffer>,
|
||||||
editor: View<Editor>,
|
editor: View<Editor>,
|
||||||
}
|
}
|
||||||
|
@ -31,17 +31,18 @@ pub struct WorkflowStepView {
|
||||||
impl WorkflowStepView {
|
impl WorkflowStepView {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
context: Model<Context>,
|
context: Model<Context>,
|
||||||
step: Model<WorkflowStepResolution>,
|
step: Model<WorkflowStep>,
|
||||||
language_registry: Arc<LanguageRegistry>,
|
language_registry: Arc<LanguageRegistry>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let tool_output_buffer = cx.new_model(|cx| Buffer::local(step.read(cx).output.clone(), cx));
|
let tool_output_buffer =
|
||||||
|
cx.new_model(|cx| Buffer::local(step.read(cx).tool_output.clone(), cx));
|
||||||
let buffer = cx.new_model(|cx| {
|
let buffer = cx.new_model(|cx| {
|
||||||
let mut buffer = MultiBuffer::without_headers(0, language::Capability::ReadWrite);
|
let mut buffer = MultiBuffer::without_headers(0, language::Capability::ReadWrite);
|
||||||
buffer.push_excerpts(
|
buffer.push_excerpts(
|
||||||
context.read(cx).buffer().clone(),
|
context.read(cx).buffer().clone(),
|
||||||
[ExcerptRange {
|
[ExcerptRange {
|
||||||
context: step.read(cx).tagged_range.clone(),
|
context: step.read(cx).context_buffer_range.clone(),
|
||||||
primary: None,
|
primary: None,
|
||||||
}],
|
}],
|
||||||
cx,
|
cx,
|
||||||
|
@ -146,55 +147,54 @@ impl WorkflowStepView {
|
||||||
|
|
||||||
fn render_result(&mut self, cx: &mut ViewContext<Self>) -> Option<AnyElement> {
|
fn render_result(&mut self, cx: &mut ViewContext<Self>) -> Option<AnyElement> {
|
||||||
let step = self.step.upgrade()?;
|
let step = self.step.upgrade()?;
|
||||||
let result = step.read(cx).result.as_ref()?;
|
let result = step.read(cx).resolution.as_ref()?;
|
||||||
match result {
|
match result {
|
||||||
Ok(result) => Some(
|
Ok(result) => {
|
||||||
v_flex()
|
Some(
|
||||||
.child(result.title.clone())
|
v_flex()
|
||||||
.children(result.suggestion_groups.iter().filter_map(
|
.child(result.title.clone())
|
||||||
|(buffer, suggestion_groups)| {
|
.children(result.suggestion_groups.iter().filter_map(
|
||||||
let path = buffer.read(cx).file().map(|f| f.path());
|
|(buffer, suggestion_groups)| {
|
||||||
v_flex()
|
let path = buffer.read(cx).file().map(|f| f.path());
|
||||||
.mb_2()
|
v_flex()
|
||||||
.border_b_1()
|
.mb_2()
|
||||||
.children(path.map(|path| format!("path: {}", path.display())))
|
.border_b_1()
|
||||||
.children(suggestion_groups.iter().map(|group| {
|
.children(path.map(|path| format!("path: {}", path.display())))
|
||||||
v_flex().pl_2().children(group.suggestions.iter().map(
|
.children(suggestion_groups.iter().map(|group| {
|
||||||
|suggestion| {
|
v_flex().pl_2().children(group.suggestions.iter().map(
|
||||||
v_flex()
|
|suggestion| {
|
||||||
.children(
|
v_flex()
|
||||||
suggestion
|
.children(
|
||||||
.kind
|
suggestion.description().map(|desc| {
|
||||||
.description()
|
format!("description: {desc}")
|
||||||
.map(|desc| format!("description: {desc}")),
|
}),
|
||||||
)
|
)
|
||||||
.child(format!("kind: {}", suggestion.kind.kind()))
|
.child(format!("kind: {}", suggestion.kind()))
|
||||||
.children(
|
.children(suggestion.symbol_path().map(
|
||||||
suggestion.kind.symbol_path().map(|path| {
|
|path| format!("symbol path: {}", path.0),
|
||||||
format!("symbol path: {}", path.0)
|
))
|
||||||
}),
|
},
|
||||||
)
|
))
|
||||||
},
|
}))
|
||||||
))
|
.into()
|
||||||
}))
|
},
|
||||||
.into()
|
))
|
||||||
},
|
.into_any_element(),
|
||||||
))
|
)
|
||||||
.into_any_element(),
|
}
|
||||||
),
|
|
||||||
Err(error) => Some(format!("{:?}", error).into_any_element()),
|
Err(error) => Some(format!("{:?}", error).into_any_element()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn step_updated(&mut self, step: Model<WorkflowStepResolution>, cx: &mut ViewContext<Self>) {
|
fn step_updated(&mut self, step: Model<WorkflowStep>, cx: &mut ViewContext<Self>) {
|
||||||
self.tool_output_buffer.update(cx, |buffer, cx| {
|
self.tool_output_buffer.update(cx, |buffer, cx| {
|
||||||
let text = step.read(cx).output.clone();
|
let text = step.read(cx).tool_output.clone();
|
||||||
buffer.set_text(text, cx);
|
buffer.set_text(text, cx);
|
||||||
});
|
});
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn step_released(&mut self, _: &mut WorkflowStepResolution, cx: &mut ViewContext<Self>) {
|
fn step_released(&mut self, _: &mut WorkflowStep, cx: &mut ViewContext<Self>) {
|
||||||
cx.emit(EditorEvent::Closed);
|
cx.emit(EditorEvent::Closed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue