Compare commits
6 Commits
quick-comm
...
lipstick
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aaba50b984 | ||
|
|
eac3b68007 | ||
|
|
a241c06672 | ||
|
|
dd816ea36f | ||
|
|
7723de1eb8 | ||
|
|
e359f275dd |
@@ -1,4 +1,4 @@
|
|||||||
Your task is to map a step from the conversation above to operations on symbols inside the provided source files.
|
Your task is to map a step from the conversation above to suggestions on symbols inside the provided source files.
|
||||||
|
|
||||||
Guidelines:
|
Guidelines:
|
||||||
- There's no need to describe *what* to do, just *where* to do it.
|
- There's no need to describe *what* to do, just *where* to do it.
|
||||||
@@ -6,13 +6,13 @@ Guidelines:
|
|||||||
- Don't create and then update a file.
|
- Don't create and then update a file.
|
||||||
- We'll create it in one shot.
|
- We'll create it in one shot.
|
||||||
- Prefer updating symbols lower in the syntax tree if possible.
|
- Prefer updating symbols lower in the syntax tree if possible.
|
||||||
- Never include operations on a parent symbol and one of its children in the same operations block.
|
- Never include suggestions on a parent symbol and one of its children in the same suggestions block.
|
||||||
- Never nest an operation with another operation or include CDATA or other content. All operations are leaf nodes.
|
- Never nest an operation with another operation or include CDATA or other content. All suggestions are leaf nodes.
|
||||||
- Include a description attribute for each operation with a brief, one-line description of the change to perform.
|
- Include a description attribute for each operation with a brief, one-line description of the change to perform.
|
||||||
- Descriptions are required for all operations except delete.
|
- Descriptions are required for all suggestions except delete.
|
||||||
- When generating multiple operations, ensure the descriptions are specific to each individual operation.
|
- When generating multiple suggestions, ensure the descriptions are specific to each individual operation.
|
||||||
- Avoid referring to the location in the description. Focus on the change to be made, not the location where it's made. That's implicit with the symbol you provide.
|
- Avoid referring to the location in the description. Focus on the change to be made, not the location where it's made. That's implicit with the symbol you provide.
|
||||||
- Don't generate multiple operations at the same location. Instead, combine them together in a single operation with a succinct combined description.
|
- Don't generate multiple suggestions at the same location. Instead, combine them together in a single operation with a succinct combined description.
|
||||||
|
|
||||||
Example 1:
|
Example 1:
|
||||||
|
|
||||||
@@ -33,12 +33,12 @@ impl Rectangle {
|
|||||||
<step>Add new methods 'calculate_area' and 'calculate_perimeter' to the Rectangle struct</step>
|
<step>Add new methods 'calculate_area' and 'calculate_perimeter' to the Rectangle struct</step>
|
||||||
<step>Implement the 'Display' trait for the Rectangle struct</step>
|
<step>Implement the 'Display' trait for the Rectangle struct</step>
|
||||||
|
|
||||||
What are the operations for the step: <step>Add a new method 'calculate_area' to the Rectangle struct</step>
|
What are the suggestions for the step: <step>Add a new method 'calculate_area' to the Rectangle struct</step>
|
||||||
|
|
||||||
A (wrong):
|
A (wrong):
|
||||||
{
|
{
|
||||||
"title": "Add Rectangle methods",
|
"title": "Add Rectangle methods",
|
||||||
"operations": [
|
"suggestions": [
|
||||||
{
|
{
|
||||||
"kind": "AppendChild",
|
"kind": "AppendChild",
|
||||||
"path": "src/shapes.rs",
|
"path": "src/shapes.rs",
|
||||||
@@ -59,7 +59,7 @@ This demonstrates what NOT to do. NEVER append multiple children at the same loc
|
|||||||
A (corrected):
|
A (corrected):
|
||||||
{
|
{
|
||||||
"title": "Add Rectangle methods",
|
"title": "Add Rectangle methods",
|
||||||
"operations": [
|
"suggestions": [
|
||||||
{
|
{
|
||||||
"kind": "AppendChild",
|
"kind": "AppendChild",
|
||||||
"path": "src/shapes.rs",
|
"path": "src/shapes.rs",
|
||||||
@@ -70,12 +70,12 @@ A (corrected):
|
|||||||
}
|
}
|
||||||
|
|
||||||
User:
|
User:
|
||||||
What are the operations for the step: <step>Implement the 'Display' trait for the Rectangle struct</step>
|
What are the suggestions for the step: <step>Implement the 'Display' trait for the Rectangle struct</step>
|
||||||
|
|
||||||
A:
|
A:
|
||||||
{
|
{
|
||||||
"title": "Implement Display for Rectangle",
|
"title": "Implement Display for Rectangle",
|
||||||
"operations": [
|
"suggestions": [
|
||||||
{
|
{
|
||||||
"kind": "InsertSiblingAfter",
|
"kind": "InsertSiblingAfter",
|
||||||
"path": "src/shapes.rs",
|
"path": "src/shapes.rs",
|
||||||
@@ -109,12 +109,12 @@ impl User {
|
|||||||
<step>Update the 'print_info' method to use formatted output</step>
|
<step>Update the 'print_info' method to use formatted output</step>
|
||||||
<step>Remove the 'email' field from the User struct</step>
|
<step>Remove the 'email' field from the User struct</step>
|
||||||
|
|
||||||
What are the operations for the step: <step>Update the 'print_info' method to use formatted output</step>
|
What are the suggestions for the step: <step>Update the 'print_info' method to use formatted output</step>
|
||||||
|
|
||||||
A:
|
A:
|
||||||
{
|
{
|
||||||
"title": "Use formatted output",
|
"title": "Use formatted output",
|
||||||
"operations": [
|
"suggestions": [
|
||||||
{
|
{
|
||||||
"kind": "Update",
|
"kind": "Update",
|
||||||
"path": "src/user.rs",
|
"path": "src/user.rs",
|
||||||
@@ -125,12 +125,12 @@ A:
|
|||||||
}
|
}
|
||||||
|
|
||||||
User:
|
User:
|
||||||
What are the operations for the step: <step>Remove the 'email' field from the User struct</step>
|
What are the suggestions for the step: <step>Remove the 'email' field from the User struct</step>
|
||||||
|
|
||||||
A:
|
A:
|
||||||
{
|
{
|
||||||
"title": "Remove email field",
|
"title": "Remove email field",
|
||||||
"operations": [
|
"suggestions": [
|
||||||
{
|
{
|
||||||
"kind": "Delete",
|
"kind": "Delete",
|
||||||
"path": "src/user.rs",
|
"path": "src/user.rs",
|
||||||
@@ -163,12 +163,12 @@ impl Vehicle {
|
|||||||
<step>Add a 'use std::fmt;' statement at the beginning of the file</step>
|
<step>Add a 'use std::fmt;' statement at the beginning of the file</step>
|
||||||
<step>Add a new method 'start_engine' in the Vehicle impl block</step>
|
<step>Add a new method 'start_engine' in the Vehicle impl block</step>
|
||||||
|
|
||||||
What are the operations for the step: <step>Add a 'use std::fmt;' statement at the beginning of the file</step>
|
What are the suggestions for the step: <step>Add a 'use std::fmt;' statement at the beginning of the file</step>
|
||||||
|
|
||||||
A:
|
A:
|
||||||
{
|
{
|
||||||
"title": "Add use std::fmt statement",
|
"title": "Add use std::fmt statement",
|
||||||
"operations": [
|
"suggestions": [
|
||||||
{
|
{
|
||||||
"kind": "PrependChild",
|
"kind": "PrependChild",
|
||||||
"path": "src/vehicle.rs",
|
"path": "src/vehicle.rs",
|
||||||
@@ -178,12 +178,12 @@ A:
|
|||||||
}
|
}
|
||||||
|
|
||||||
User:
|
User:
|
||||||
What are the operations for the step: <step>Add a new method 'start_engine' in the Vehicle impl block</step>
|
What are the suggestions for the step: <step>Add a new method 'start_engine' in the Vehicle impl block</step>
|
||||||
|
|
||||||
A:
|
A:
|
||||||
{
|
{
|
||||||
"title": "Add start_engine method",
|
"title": "Add start_engine method",
|
||||||
"operations": [
|
"suggestions": [
|
||||||
{
|
{
|
||||||
"kind": "InsertSiblingAfter",
|
"kind": "InsertSiblingAfter",
|
||||||
"path": "src/vehicle.rs",
|
"path": "src/vehicle.rs",
|
||||||
@@ -222,12 +222,12 @@ impl Employee {
|
|||||||
|
|
||||||
<step>Make salary an f32</step>
|
<step>Make salary an f32</step>
|
||||||
|
|
||||||
What are the operations for the step: <step>Make salary an f32</step>
|
What are the suggestions for the step: <step>Make salary an f32</step>
|
||||||
|
|
||||||
A (wrong):
|
A (wrong):
|
||||||
{
|
{
|
||||||
"title": "Change salary to f32",
|
"title": "Change salary to f32",
|
||||||
"operations": [
|
"suggestions": [
|
||||||
{
|
{
|
||||||
"kind": "Update",
|
"kind": "Update",
|
||||||
"path": "src/employee.rs",
|
"path": "src/employee.rs",
|
||||||
@@ -248,7 +248,7 @@ This example demonstrates what not to do. `struct Employee salary` is a child of
|
|||||||
A (corrected):
|
A (corrected):
|
||||||
{
|
{
|
||||||
"title": "Change salary to f32",
|
"title": "Change salary to f32",
|
||||||
"operations": [
|
"suggestions": [
|
||||||
{
|
{
|
||||||
"kind": "Update",
|
"kind": "Update",
|
||||||
"path": "src/employee.rs",
|
"path": "src/employee.rs",
|
||||||
@@ -259,12 +259,12 @@ A (corrected):
|
|||||||
}
|
}
|
||||||
|
|
||||||
User:
|
User:
|
||||||
What are the correct operations for the step: <step>Remove the 'department' field and update the 'print_details' method</step>
|
What are the correct suggestions for the step: <step>Remove the 'department' field and update the 'print_details' method</step>
|
||||||
|
|
||||||
A:
|
A:
|
||||||
{
|
{
|
||||||
"title": "Remove department",
|
"title": "Remove department",
|
||||||
"operations": [
|
"suggestions": [
|
||||||
{
|
{
|
||||||
"kind": "Delete",
|
"kind": "Delete",
|
||||||
"path": "src/employee.rs",
|
"path": "src/employee.rs",
|
||||||
@@ -311,7 +311,7 @@ impl Game {
|
|||||||
A:
|
A:
|
||||||
{
|
{
|
||||||
"title": "Add level field to Player",
|
"title": "Add level field to Player",
|
||||||
"operations": [
|
"suggestions": [
|
||||||
{
|
{
|
||||||
"kind": "InsertSiblingAfter",
|
"kind": "InsertSiblingAfter",
|
||||||
"path": "src/game.rs",
|
"path": "src/game.rs",
|
||||||
@@ -349,7 +349,7 @@ impl Config {
|
|||||||
A:
|
A:
|
||||||
{
|
{
|
||||||
"title": "Add load_from_file method",
|
"title": "Add load_from_file method",
|
||||||
"operations": [
|
"suggestions": [
|
||||||
{
|
{
|
||||||
"kind": "PrependChild",
|
"kind": "PrependChild",
|
||||||
"path": "src/config.rs",
|
"path": "src/config.rs",
|
||||||
@@ -389,7 +389,7 @@ impl Database {
|
|||||||
A:
|
A:
|
||||||
{
|
{
|
||||||
"title": "Add error handling to query",
|
"title": "Add error handling to query",
|
||||||
"operations": [
|
"suggestions": [
|
||||||
{
|
{
|
||||||
"kind": "PrependChild",
|
"kind": "PrependChild",
|
||||||
"path": "src/database.rs",
|
"path": "src/database.rs",
|
||||||
@@ -410,4 +410,4 @@ A:
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
Now generate the operations for the following step:
|
Now generate the suggestions for the following step:
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ use crate::{
|
|||||||
},
|
},
|
||||||
terminal_inline_assistant::TerminalInlineAssistant,
|
terminal_inline_assistant::TerminalInlineAssistant,
|
||||||
Assist, CodegenStatus, ConfirmCommand, Context, ContextEvent, ContextId, ContextStore,
|
Assist, CodegenStatus, ConfirmCommand, Context, ContextEvent, ContextId, ContextStore,
|
||||||
CycleMessageRole, DebugEditSteps, DeployHistory, DeployPromptLibrary, EditSuggestionGroup,
|
CycleMessageRole, DebugEditSteps, DeployHistory, DeployPromptLibrary, InlineAssist,
|
||||||
InlineAssist, InlineAssistId, InlineAssistant, InsertIntoEditor, MessageStatus, ModelSelector,
|
InlineAssistId, InlineAssistant, InsertIntoEditor, MessageStatus, ModelSelector,
|
||||||
PendingSlashCommand, PendingSlashCommandStatus, QuoteSelection, RemoteContextMetadata,
|
PendingSlashCommand, PendingSlashCommandStatus, QuoteSelection, RemoteContextMetadata,
|
||||||
ResolvedWorkflowStepEditSuggestions, SavedContextMetadata, Split, ToggleFocus,
|
ResolvedWorkflowStep, SavedContextMetadata, Split, ToggleFocus, ToggleModelSelector,
|
||||||
ToggleModelSelector, WorkflowStepEditSuggestions,
|
WorkflowStepStatus,
|
||||||
};
|
};
|
||||||
use crate::{ContextStoreEvent, ShowConfiguration};
|
use crate::{ContextStoreEvent, ShowConfiguration};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Context as _, Result};
|
||||||
use assistant_slash_command::{SlashCommand, SlashCommandOutputSection};
|
use assistant_slash_command::{SlashCommand, SlashCommandOutputSection};
|
||||||
use client::{proto, Client, Status};
|
use client::{proto, Client, Status};
|
||||||
use collections::{BTreeSet, HashMap, HashSet};
|
use collections::{BTreeSet, HashMap, HashSet};
|
||||||
@@ -41,8 +41,7 @@ use gpui::{
|
|||||||
};
|
};
|
||||||
use indexed_docs::IndexedDocsStore;
|
use indexed_docs::IndexedDocsStore;
|
||||||
use language::{
|
use language::{
|
||||||
language_settings::SoftWrap, Buffer, Capability, LanguageRegistry, LspAdapterDelegate, Point,
|
language_settings::SoftWrap, Capability, LanguageRegistry, LspAdapterDelegate, Point, ToOffset,
|
||||||
ToOffset,
|
|
||||||
};
|
};
|
||||||
use language_model::{
|
use language_model::{
|
||||||
provider::cloud::PROVIDER_ID, LanguageModelProvider, LanguageModelProviderId,
|
provider::cloud::PROVIDER_ID, LanguageModelProvider, LanguageModelProviderId,
|
||||||
@@ -1330,15 +1329,10 @@ struct ScrollPosition {
|
|||||||
cursor: Anchor,
|
cursor: Anchor,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct StepAssists {
|
struct WorkflowAssist {
|
||||||
assist_ids: Vec<InlineAssistId>,
|
|
||||||
editor: WeakView<Editor>,
|
editor: WeakView<Editor>,
|
||||||
}
|
editor_was_open: bool,
|
||||||
|
assist_ids: Vec<InlineAssistId>,
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
|
||||||
struct ActiveWorkflowStep {
|
|
||||||
range: Range<language::Anchor>,
|
|
||||||
suggestions: Option<ResolvedWorkflowStepEditSuggestions>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ContextEditor {
|
pub struct ContextEditor {
|
||||||
@@ -1353,9 +1347,9 @@ pub struct ContextEditor {
|
|||||||
remote_id: Option<workspace::ViewId>,
|
remote_id: Option<workspace::ViewId>,
|
||||||
pending_slash_command_creases: HashMap<Range<language::Anchor>, CreaseId>,
|
pending_slash_command_creases: HashMap<Range<language::Anchor>, CreaseId>,
|
||||||
pending_slash_command_blocks: HashMap<Range<language::Anchor>, CustomBlockId>,
|
pending_slash_command_blocks: HashMap<Range<language::Anchor>, CustomBlockId>,
|
||||||
|
workflow_assists: HashMap<Range<language::Anchor>, WorkflowAssist>,
|
||||||
|
active_workflow_step_range: Option<Range<language::Anchor>>,
|
||||||
_subscriptions: Vec<Subscription>,
|
_subscriptions: Vec<Subscription>,
|
||||||
assists_by_step: HashMap<Range<language::Anchor>, StepAssists>,
|
|
||||||
active_workflow_step: Option<ActiveWorkflowStep>,
|
|
||||||
assistant_panel: WeakView<AssistantPanel>,
|
assistant_panel: WeakView<AssistantPanel>,
|
||||||
error_message: Option<SharedString>,
|
error_message: Option<SharedString>,
|
||||||
}
|
}
|
||||||
@@ -1413,8 +1407,8 @@ impl ContextEditor {
|
|||||||
pending_slash_command_creases: HashMap::default(),
|
pending_slash_command_creases: HashMap::default(),
|
||||||
pending_slash_command_blocks: HashMap::default(),
|
pending_slash_command_blocks: HashMap::default(),
|
||||||
_subscriptions,
|
_subscriptions,
|
||||||
assists_by_step: HashMap::default(),
|
workflow_assists: HashMap::default(),
|
||||||
active_workflow_step: None,
|
active_workflow_step_range: None,
|
||||||
assistant_panel,
|
assistant_panel,
|
||||||
error_message: None,
|
error_message: None,
|
||||||
};
|
};
|
||||||
@@ -1449,16 +1443,16 @@ impl ContextEditor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn assist(&mut self, _: &Assist, cx: &mut ViewContext<Self>) {
|
fn assist(&mut self, _: &Assist, cx: &mut ViewContext<Self>) {
|
||||||
if !self.apply_edit_step(cx) {
|
if !self.apply_workflow_step(cx) {
|
||||||
self.error_message = None;
|
self.error_message = None;
|
||||||
self.send_to_model(cx);
|
self.send_to_model(cx);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_edit_step(&mut self, cx: &mut ViewContext<Self>) -> bool {
|
fn apply_workflow_step(&mut self, cx: &mut ViewContext<Self>) -> bool {
|
||||||
if let Some(step) = self.active_workflow_step.as_ref() {
|
if let Some(step_range) = self.active_workflow_step_range.as_ref() {
|
||||||
if let Some(assists) = self.assists_by_step.get(&step.range) {
|
if let Some(assists) = self.workflow_assists.get(&step_range) {
|
||||||
let assist_ids = assists.assist_ids.clone();
|
let assist_ids = assists.assist_ids.clone();
|
||||||
cx.window_context().defer(|cx| {
|
cx.window_context().defer(|cx| {
|
||||||
InlineAssistant::update_global(cx, |assistant, cx| {
|
InlineAssistant::update_global(cx, |assistant, cx| {
|
||||||
@@ -1519,16 +1513,13 @@ impl ContextEditor {
|
|||||||
.text_for_range(step.tagged_range.clone())
|
.text_for_range(step.tagged_range.clone())
|
||||||
.collect::<String>()
|
.collect::<String>()
|
||||||
));
|
));
|
||||||
match &step.edit_suggestions {
|
match &step.status {
|
||||||
WorkflowStepEditSuggestions::Resolved(ResolvedWorkflowStepEditSuggestions {
|
WorkflowStepStatus::Resolved(ResolvedWorkflowStep { title, suggestions }) => {
|
||||||
title,
|
|
||||||
edit_suggestions,
|
|
||||||
}) => {
|
|
||||||
output.push_str("Resolution:\n");
|
output.push_str("Resolution:\n");
|
||||||
output.push_str(&format!(" {:?}\n", title));
|
output.push_str(&format!(" {:?}\n", title));
|
||||||
output.push_str(&format!(" {:?}\n", edit_suggestions));
|
output.push_str(&format!(" {:?}\n", suggestions));
|
||||||
}
|
}
|
||||||
WorkflowStepEditSuggestions::Pending(_) => {
|
WorkflowStepStatus::Pending(_) => {
|
||||||
output.push_str("Resolution: Pending\n");
|
output.push_str("Resolution: Pending\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1676,7 +1667,7 @@ impl ContextEditor {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
ContextEvent::WorkflowStepsChanged => {
|
ContextEvent::WorkflowStepsChanged => {
|
||||||
self.update_active_workflow_step(cx);
|
self.update_active_workflow_step_from_cursor(cx);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
ContextEvent::SummaryChanged => {
|
ContextEvent::SummaryChanged => {
|
||||||
@@ -1941,14 +1932,14 @@ impl ContextEditor {
|
|||||||
}
|
}
|
||||||
EditorEvent::SelectionsChanged { .. } => {
|
EditorEvent::SelectionsChanged { .. } => {
|
||||||
self.scroll_position = self.cursor_scroll_position(cx);
|
self.scroll_position = self.cursor_scroll_position(cx);
|
||||||
self.update_active_workflow_step(cx);
|
self.update_active_workflow_step_from_cursor(cx);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
cx.emit(event.clone());
|
cx.emit(event.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_active_workflow_step(&mut self, cx: &mut ViewContext<Self>) {
|
fn update_active_workflow_step_from_cursor(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
let new_step = self
|
let new_step = self
|
||||||
.workflow_step_range_for_cursor(cx)
|
.workflow_step_range_for_cursor(cx)
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@@ -1957,14 +1948,11 @@ impl ContextEditor {
|
|||||||
.context
|
.context
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.workflow_step_for_range(step_range.clone())?;
|
.workflow_step_for_range(step_range.clone())?;
|
||||||
Some(ActiveWorkflowStep {
|
Some(workflow_step.tagged_range.clone())
|
||||||
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 new_step.as_ref() != self.active_workflow_step_range.as_ref() {
|
||||||
if let Some(old_step) = self.active_workflow_step.take() {
|
if let Some(old_step_range) = self.active_workflow_step_range.take() {
|
||||||
self.cancel_workflow_step_if_idle(old_step.range, cx);
|
self.hide_workflow_step(old_step_range, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(new_step) = new_step {
|
if let Some(new_step) = new_step {
|
||||||
@@ -1973,21 +1961,21 @@ impl ContextEditor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cancel_workflow_step_if_idle(
|
fn hide_workflow_step(
|
||||||
&mut self,
|
&mut self,
|
||||||
step_range: Range<language::Anchor>,
|
step_range: Range<language::Anchor>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) {
|
) {
|
||||||
let Some(step_assists) = self.assists_by_step.get_mut(&step_range) else {
|
let Some(step_assist) = self.workflow_assists.get_mut(&step_range) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let Some(editor) = step_assists.editor.upgrade() else {
|
let Some(editor) = step_assist.editor.upgrade() else {
|
||||||
self.assists_by_step.remove(&step_range);
|
self.workflow_assists.remove(&step_range);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
InlineAssistant::update_global(cx, |assistant, cx| {
|
InlineAssistant::update_global(cx, |assistant, cx| {
|
||||||
step_assists.assist_ids.retain(|assist_id| {
|
step_assist.assist_ids.retain(|assist_id| {
|
||||||
match assistant.status_for_assist(*assist_id, cx) {
|
match assistant.status_for_assist(*assist_id, cx) {
|
||||||
Some(CodegenStatus::Idle) | None => {
|
Some(CodegenStatus::Idle) | None => {
|
||||||
assistant.finish_assist(*assist_id, true, cx);
|
assistant.finish_assist(*assist_id, true, cx);
|
||||||
@@ -1998,14 +1986,15 @@ impl ContextEditor {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if step_assists.assist_ids.is_empty() {
|
if step_assist.assist_ids.is_empty() {
|
||||||
self.assists_by_step.remove(&step_range);
|
let editor_was_open = step_assist.editor_was_open;
|
||||||
|
self.workflow_assists.remove(&step_range);
|
||||||
self.workspace
|
self.workspace
|
||||||
.update(cx, |workspace, cx| {
|
.update(cx, |workspace, cx| {
|
||||||
if let Some(pane) = workspace.pane_for(&editor) {
|
if let Some(pane) = workspace.pane_for(&editor) {
|
||||||
pane.update(cx, |pane, cx| {
|
pane.update(cx, |pane, cx| {
|
||||||
let item_id = editor.entity_id();
|
let item_id = editor.entity_id();
|
||||||
if pane.is_active_preview_item(item_id) {
|
if !editor_was_open && pane.is_active_preview_item(item_id) {
|
||||||
pane.close_item_by_id(item_id, SaveIntent::Skip, cx)
|
pane.close_item_by_id(item_id, SaveIntent::Skip, cx)
|
||||||
.detach_and_log_err(cx);
|
.detach_and_log_err(cx);
|
||||||
}
|
}
|
||||||
@@ -2016,147 +2005,200 @@ impl ContextEditor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn activate_workflow_step(&mut self, step: ActiveWorkflowStep, cx: &mut ViewContext<Self>) {
|
fn activate_workflow_step(
|
||||||
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,
|
&mut self,
|
||||||
title: String,
|
step_range: Range<language::Anchor>,
|
||||||
edit_suggestions: HashMap<Model<Buffer>, Vec<EditSuggestionGroup>>,
|
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Option<(View<Editor>, Vec<InlineAssistId>)> {
|
) -> Option<()> {
|
||||||
let assistant_panel = self.assistant_panel.upgrade()?;
|
if self.scroll_to_existing_workflow_assist(&step_range, cx) {
|
||||||
if edit_suggestions.is_empty() {
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let editor;
|
let step = self
|
||||||
let mut suggestion_groups = Vec::new();
|
.workflow_step(&step_range, cx)
|
||||||
if edit_suggestions.len() == 1 && edit_suggestions.values().next().unwrap().len() == 1 {
|
.with_context(|| format!("could not find workflow step for range {:?}", step_range))
|
||||||
// If there's only one buffer and one suggestion group, open it directly
|
.log_err()?;
|
||||||
let (buffer, groups) = edit_suggestions.into_iter().next().unwrap();
|
let Some(resolved) = step.status.as_resolved() else {
|
||||||
let group = groups.into_iter().next().unwrap();
|
return None;
|
||||||
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()?;
|
|
||||||
|
|
||||||
let (&excerpt_id, _, _) = editor
|
let title = resolved.title.clone();
|
||||||
.read(cx)
|
let suggestions = resolved.suggestions.clone();
|
||||||
.buffer()
|
|
||||||
.read(cx)
|
|
||||||
.read(cx)
|
|
||||||
.as_singleton()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Scroll the editor to the suggested assist
|
if let Some((editor, assist_ids, editor_was_open)) = {
|
||||||
editor.update(cx, |editor, cx| {
|
let assistant_panel = self.assistant_panel.upgrade()?;
|
||||||
let multibuffer = editor.buffer().read(cx).snapshot(cx);
|
if suggestions.is_empty() {
|
||||||
let (&excerpt_id, _, buffer) = multibuffer.as_singleton().unwrap();
|
return None;
|
||||||
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()
|
|
||||||
};
|
|
||||||
|
|
||||||
editor.set_scroll_anchor(
|
let editor;
|
||||||
ScrollAnchor {
|
let mut editor_was_open = false;
|
||||||
offset: gpui::Point::default(),
|
let mut suggestion_groups = Vec::new();
|
||||||
anchor,
|
if suggestions.len() == 1 && suggestions.values().next().unwrap().len() == 1 {
|
||||||
},
|
// If there's only one buffer and one suggestion group, open it directly
|
||||||
cx,
|
let (buffer, groups) = 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();
|
||||||
|
editor_was_open =
|
||||||
|
workspace.is_project_item_open::<Editor>(&active_pane, &buffer, cx);
|
||||||
|
workspace.open_project_item::<Editor>(active_pane, buffer, false, false, cx)
|
||||||
|
})
|
||||||
|
.log_err()?;
|
||||||
|
|
||||||
suggestion_groups.push((excerpt_id, group));
|
let (&excerpt_id, _, _) = editor
|
||||||
} else {
|
.read(cx)
|
||||||
// If there are multiple buffers or suggestion groups, create a multibuffer
|
.buffer()
|
||||||
let multibuffer = cx.new_model(|cx| {
|
.read(cx)
|
||||||
let replica_id = self.project.read(cx).replica_id();
|
.read(cx)
|
||||||
let mut multibuffer =
|
.as_singleton()
|
||||||
MultiBuffer::new(replica_id, Capability::ReadWrite).with_title(title);
|
.unwrap();
|
||||||
for (buffer, groups) in edit_suggestions {
|
|
||||||
let excerpt_ids = multibuffer.push_excerpts(
|
// Scroll the editor to the suggested assist
|
||||||
buffer,
|
editor.update(cx, |editor, cx| {
|
||||||
groups.iter().map(|suggestion_group| ExcerptRange {
|
let multibuffer = editor.buffer().read(cx).snapshot(cx);
|
||||||
context: suggestion_group.context_range.clone(),
|
let (&excerpt_id, _, buffer) = multibuffer.as_singleton().unwrap();
|
||||||
primary: None,
|
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()
|
||||||
|
};
|
||||||
|
|
||||||
|
editor.set_scroll_anchor(
|
||||||
|
ScrollAnchor {
|
||||||
|
offset: gpui::Point::default(),
|
||||||
|
anchor,
|
||||||
|
},
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
suggestion_groups.extend(excerpt_ids.into_iter().zip(groups));
|
});
|
||||||
}
|
|
||||||
multibuffer
|
|
||||||
});
|
|
||||||
|
|
||||||
editor = cx.new_view(|cx| {
|
suggestion_groups.push((excerpt_id, group));
|
||||||
Editor::for_multibuffer(multibuffer, Some(self.project.clone()), true, cx)
|
} else {
|
||||||
});
|
// If there are multiple buffers or suggestion groups, create a multibuffer
|
||||||
self.workspace
|
let multibuffer = cx.new_model(|cx| {
|
||||||
.update(cx, |workspace, cx| {
|
let replica_id = self.project.read(cx).replica_id();
|
||||||
workspace.add_item_to_active_pane(Box::new(editor.clone()), None, false, cx)
|
let mut multibuffer =
|
||||||
})
|
MultiBuffer::new(replica_id, Capability::ReadWrite).with_title(title);
|
||||||
.log_err()?;
|
for (buffer, groups) in 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,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(range) = self.active_workflow_step_range.clone() {
|
||||||
|
self.workflow_assists.insert(
|
||||||
|
range,
|
||||||
|
WorkflowAssist {
|
||||||
|
assist_ids: assist_ids.clone(),
|
||||||
|
editor: editor.downgrade(),
|
||||||
|
editor_was_open,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some((editor, assist_ids, editor_was_open))
|
||||||
|
} {
|
||||||
|
self.workflow_assists.insert(
|
||||||
|
step_range.clone(),
|
||||||
|
WorkflowAssist {
|
||||||
|
assist_ids,
|
||||||
|
editor_was_open,
|
||||||
|
editor: editor.downgrade(),
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut assist_ids = Vec::new();
|
self.active_workflow_step_range = Some(step_range);
|
||||||
for (excerpt_id, suggestion_group) in suggestion_groups {
|
|
||||||
for suggestion in suggestion_group.suggestions {
|
Some(())
|
||||||
assist_ids.extend(suggestion.show(
|
}
|
||||||
&editor,
|
|
||||||
excerpt_id,
|
fn active_workflow_step<'a>(&'a self, cx: &'a AppContext) -> Option<&'a crate::WorkflowStep> {
|
||||||
&self.workspace,
|
self.active_workflow_step_range
|
||||||
&assistant_panel,
|
.as_ref()
|
||||||
cx,
|
.and_then(|step_range| {
|
||||||
));
|
self.context
|
||||||
|
.read(cx)
|
||||||
|
.workflow_step_for_range(step_range.clone())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn workflow_step<'a>(
|
||||||
|
&'a mut self,
|
||||||
|
step_range: &Range<text::Anchor>,
|
||||||
|
cx: &'a mut ViewContext<ContextEditor>,
|
||||||
|
) -> Option<&'a crate::WorkflowStep> {
|
||||||
|
self.context
|
||||||
|
.read(cx)
|
||||||
|
.workflow_step_for_range(step_range.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scroll_to_existing_workflow_assist(
|
||||||
|
&self,
|
||||||
|
step_range: &Range<language::Anchor>,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) -> bool {
|
||||||
|
let step_assists = match self.workflow_assists.get(step_range) {
|
||||||
|
Some(assists) => assists,
|
||||||
|
None => return false,
|
||||||
|
};
|
||||||
|
let editor = match step_assists.editor.upgrade() {
|
||||||
|
Some(editor) => editor,
|
||||||
|
None => return false,
|
||||||
|
};
|
||||||
|
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 true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some((editor, assist_ids))
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_editor_search_event(
|
fn handle_editor_search_event(
|
||||||
@@ -2540,12 +2582,12 @@ impl ContextEditor {
|
|||||||
|
|
||||||
fn render_send_button(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
fn render_send_button(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
let focus_handle = self.focus_handle(cx).clone();
|
let focus_handle = self.focus_handle(cx).clone();
|
||||||
let button_text = match self.active_workflow_step.as_ref() {
|
let button_text = match self.active_workflow_step(cx) {
|
||||||
Some(step) => {
|
Some(step) => {
|
||||||
if step.suggestions.is_none() {
|
if step.status.is_resolved() {
|
||||||
"Computing Changes..."
|
|
||||||
} else {
|
|
||||||
"Apply Changes"
|
"Apply Changes"
|
||||||
|
} else {
|
||||||
|
"Computing Changes..."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => "Send",
|
None => "Send",
|
||||||
|
|||||||
@@ -348,37 +348,44 @@ pub struct SlashCommandId(clock::Lamport);
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct WorkflowStep {
|
pub struct WorkflowStep {
|
||||||
pub tagged_range: Range<language::Anchor>,
|
pub tagged_range: Range<language::Anchor>,
|
||||||
pub edit_suggestions: WorkflowStepEditSuggestions,
|
pub status: WorkflowStepStatus,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct ResolvedWorkflowStepEditSuggestions {
|
pub struct ResolvedWorkflowStep {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub edit_suggestions: HashMap<Model<Buffer>, Vec<EditSuggestionGroup>>,
|
pub suggestions: HashMap<Model<Buffer>, Vec<WorkflowSuggestionGroup>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum WorkflowStepEditSuggestions {
|
pub enum WorkflowStepStatus {
|
||||||
Pending(Task<Option<()>>),
|
Pending(Task<Option<()>>),
|
||||||
Resolved(ResolvedWorkflowStepEditSuggestions),
|
Resolved(ResolvedWorkflowStep),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WorkflowStepEditSuggestions {
|
impl WorkflowStepStatus {
|
||||||
pub fn as_resolved(&self) -> Option<&ResolvedWorkflowStepEditSuggestions> {
|
pub fn as_resolved(&self) -> Option<&ResolvedWorkflowStep> {
|
||||||
match self {
|
match self {
|
||||||
WorkflowStepEditSuggestions::Resolved(suggestions) => Some(suggestions),
|
WorkflowStepStatus::Resolved(suggestions) => Some(suggestions),
|
||||||
WorkflowStepEditSuggestions::Pending(_) => None,
|
WorkflowStepStatus::Pending(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_resolved(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
WorkflowStepStatus::Resolved(_) => true,
|
||||||
|
WorkflowStepStatus::Pending(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct EditSuggestionGroup {
|
pub struct WorkflowSuggestionGroup {
|
||||||
pub context_range: Range<language::Anchor>,
|
pub context_range: Range<language::Anchor>,
|
||||||
pub suggestions: Vec<EditSuggestion>,
|
pub suggestions: Vec<WorkflowSuggestion>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum EditSuggestion {
|
pub enum WorkflowSuggestion {
|
||||||
Update {
|
Update {
|
||||||
range: Range<language::Anchor>,
|
range: Range<language::Anchor>,
|
||||||
description: String,
|
description: String,
|
||||||
@@ -407,40 +414,40 @@ pub enum EditSuggestion {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EditSuggestion {
|
impl WorkflowSuggestion {
|
||||||
pub fn range(&self) -> Range<language::Anchor> {
|
pub fn range(&self) -> Range<language::Anchor> {
|
||||||
match self {
|
match self {
|
||||||
EditSuggestion::Update { range, .. } => range.clone(),
|
WorkflowSuggestion::Update { range, .. } => range.clone(),
|
||||||
EditSuggestion::CreateFile { .. } => language::Anchor::MIN..language::Anchor::MAX,
|
WorkflowSuggestion::CreateFile { .. } => language::Anchor::MIN..language::Anchor::MAX,
|
||||||
EditSuggestion::InsertSiblingBefore { position, .. }
|
WorkflowSuggestion::InsertSiblingBefore { position, .. }
|
||||||
| EditSuggestion::InsertSiblingAfter { position, .. }
|
| WorkflowSuggestion::InsertSiblingAfter { position, .. }
|
||||||
| EditSuggestion::PrependChild { position, .. }
|
| WorkflowSuggestion::PrependChild { position, .. }
|
||||||
| EditSuggestion::AppendChild { position, .. } => *position..*position,
|
| WorkflowSuggestion::AppendChild { position, .. } => *position..*position,
|
||||||
EditSuggestion::Delete { range } => range.clone(),
|
WorkflowSuggestion::Delete { range } => range.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn description(&self) -> Option<&str> {
|
pub fn description(&self) -> Option<&str> {
|
||||||
match self {
|
match self {
|
||||||
EditSuggestion::Update { description, .. }
|
WorkflowSuggestion::Update { description, .. }
|
||||||
| EditSuggestion::CreateFile { description }
|
| WorkflowSuggestion::CreateFile { description }
|
||||||
| EditSuggestion::InsertSiblingBefore { description, .. }
|
| WorkflowSuggestion::InsertSiblingBefore { description, .. }
|
||||||
| EditSuggestion::InsertSiblingAfter { description, .. }
|
| WorkflowSuggestion::InsertSiblingAfter { description, .. }
|
||||||
| EditSuggestion::PrependChild { description, .. }
|
| WorkflowSuggestion::PrependChild { description, .. }
|
||||||
| EditSuggestion::AppendChild { description, .. } => Some(description),
|
| WorkflowSuggestion::AppendChild { description, .. } => Some(description),
|
||||||
EditSuggestion::Delete { .. } => None,
|
WorkflowSuggestion::Delete { .. } => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn description_mut(&mut self) -> Option<&mut String> {
|
fn description_mut(&mut self) -> Option<&mut String> {
|
||||||
match self {
|
match self {
|
||||||
EditSuggestion::Update { description, .. }
|
WorkflowSuggestion::Update { description, .. }
|
||||||
| EditSuggestion::CreateFile { description }
|
| WorkflowSuggestion::CreateFile { description }
|
||||||
| EditSuggestion::InsertSiblingBefore { description, .. }
|
| WorkflowSuggestion::InsertSiblingBefore { description, .. }
|
||||||
| EditSuggestion::InsertSiblingAfter { description, .. }
|
| WorkflowSuggestion::InsertSiblingAfter { description, .. }
|
||||||
| EditSuggestion::PrependChild { description, .. }
|
| WorkflowSuggestion::PrependChild { description, .. }
|
||||||
| EditSuggestion::AppendChild { description, .. } => Some(description),
|
| WorkflowSuggestion::AppendChild { description, .. } => Some(description),
|
||||||
EditSuggestion::Delete { .. } => None,
|
WorkflowSuggestion::Delete { .. } => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -479,16 +486,16 @@ impl EditSuggestion {
|
|||||||
let snapshot = buffer.read(cx).snapshot(cx);
|
let snapshot = buffer.read(cx).snapshot(cx);
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
EditSuggestion::Update { range, description } => {
|
WorkflowSuggestion::Update { range, description } => {
|
||||||
initial_prompt = description.clone();
|
initial_prompt = description.clone();
|
||||||
suggestion_range = snapshot.anchor_in_excerpt(excerpt_id, range.start)?
|
suggestion_range = snapshot.anchor_in_excerpt(excerpt_id, range.start)?
|
||||||
..snapshot.anchor_in_excerpt(excerpt_id, range.end)?;
|
..snapshot.anchor_in_excerpt(excerpt_id, range.end)?;
|
||||||
}
|
}
|
||||||
EditSuggestion::CreateFile { description } => {
|
WorkflowSuggestion::CreateFile { description } => {
|
||||||
initial_prompt = description.clone();
|
initial_prompt = description.clone();
|
||||||
suggestion_range = editor::Anchor::min()..editor::Anchor::min();
|
suggestion_range = editor::Anchor::min()..editor::Anchor::min();
|
||||||
}
|
}
|
||||||
EditSuggestion::InsertSiblingBefore {
|
WorkflowSuggestion::InsertSiblingBefore {
|
||||||
position,
|
position,
|
||||||
description,
|
description,
|
||||||
} => {
|
} => {
|
||||||
@@ -503,7 +510,7 @@ impl EditSuggestion {
|
|||||||
line_start..line_start
|
line_start..line_start
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
EditSuggestion::InsertSiblingAfter {
|
WorkflowSuggestion::InsertSiblingAfter {
|
||||||
position,
|
position,
|
||||||
description,
|
description,
|
||||||
} => {
|
} => {
|
||||||
@@ -518,7 +525,7 @@ impl EditSuggestion {
|
|||||||
line_start..line_start
|
line_start..line_start
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
EditSuggestion::PrependChild {
|
WorkflowSuggestion::PrependChild {
|
||||||
position,
|
position,
|
||||||
description,
|
description,
|
||||||
} => {
|
} => {
|
||||||
@@ -533,7 +540,7 @@ impl EditSuggestion {
|
|||||||
line_start..line_start
|
line_start..line_start
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
EditSuggestion::AppendChild {
|
WorkflowSuggestion::AppendChild {
|
||||||
position,
|
position,
|
||||||
description,
|
description,
|
||||||
} => {
|
} => {
|
||||||
@@ -548,7 +555,7 @@ impl EditSuggestion {
|
|||||||
line_start..line_start
|
line_start..line_start
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
EditSuggestion::Delete { range } => {
|
WorkflowSuggestion::Delete { range } => {
|
||||||
initial_prompt = "Delete".to_string();
|
initial_prompt = "Delete".to_string();
|
||||||
suggestion_range = snapshot.anchor_in_excerpt(excerpt_id, range.start)?
|
suggestion_range = snapshot.anchor_in_excerpt(excerpt_id, range.start)?
|
||||||
..snapshot.anchor_in_excerpt(excerpt_id, range.end)?;
|
..snapshot.anchor_in_excerpt(excerpt_id, range.end)?;
|
||||||
@@ -569,17 +576,14 @@ impl EditSuggestion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for WorkflowStepEditSuggestions {
|
impl Debug for WorkflowStepStatus {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
WorkflowStepEditSuggestions::Pending(_) => write!(f, "EditStepOperations::Pending"),
|
WorkflowStepStatus::Pending(_) => write!(f, "EditStepOperations::Pending"),
|
||||||
WorkflowStepEditSuggestions::Resolved(ResolvedWorkflowStepEditSuggestions {
|
WorkflowStepStatus::Resolved(ResolvedWorkflowStep { title, suggestions }) => f
|
||||||
title,
|
|
||||||
edit_suggestions,
|
|
||||||
}) => f
|
|
||||||
.debug_struct("EditStepOperations::Parsed")
|
.debug_struct("EditStepOperations::Parsed")
|
||||||
.field("title", title)
|
.field("title", title)
|
||||||
.field("edit_suggestions", edit_suggestions)
|
.field("suggestions", suggestions)
|
||||||
.finish(),
|
.finish(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1205,16 +1209,13 @@ impl Context {
|
|||||||
|
|
||||||
if let Err(ix) = existing_step_index {
|
if let Err(ix) = existing_step_index {
|
||||||
// Step doesn't exist, so add it
|
// Step doesn't exist, so add it
|
||||||
let task = self.compute_workflow_step_edit_suggestions(
|
let task =
|
||||||
tagged_range.clone(),
|
self.resolve_workflow_step(tagged_range.clone(), project.clone(), cx);
|
||||||
project.clone(),
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
new_edit_steps.push((
|
new_edit_steps.push((
|
||||||
ix,
|
ix,
|
||||||
WorkflowStep {
|
WorkflowStep {
|
||||||
tagged_range,
|
tagged_range,
|
||||||
edit_suggestions: WorkflowStepEditSuggestions::Pending(task),
|
status: WorkflowStepStatus::Pending(task),
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -1235,7 +1236,7 @@ impl Context {
|
|||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_workflow_step_edit_suggestions(
|
fn resolve_workflow_step(
|
||||||
&self,
|
&self,
|
||||||
tagged_range: Range<language::Anchor>,
|
tagged_range: Range<language::Anchor>,
|
||||||
project: Model<Project>,
|
project: Model<Project>,
|
||||||
@@ -1265,13 +1266,13 @@ impl Context {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Invoke the model to get its edit suggestions for this workflow step.
|
// Invoke the model to get its edit suggestions for this workflow step.
|
||||||
let step_suggestions = model
|
let resolution = model
|
||||||
.use_tool::<tool::WorkflowStepEditSuggestions>(request, &cx)
|
.use_tool::<tool::WorkflowStepResolution>(request, &cx)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Translate the parsed suggestions to our internal types, which anchor the suggestions to locations in the code.
|
// Translate the parsed suggestions to our internal types, which anchor the suggestions to locations in the code.
|
||||||
let suggestion_tasks: Vec<_> = step_suggestions
|
let suggestion_tasks: Vec<_> = resolution
|
||||||
.edit_suggestions
|
.suggestions
|
||||||
.iter()
|
.iter()
|
||||||
.map(|suggestion| suggestion.resolve(project.clone(), cx.clone()))
|
.map(|suggestion| suggestion.resolve(project.clone(), cx.clone()))
|
||||||
.collect();
|
.collect();
|
||||||
@@ -1293,7 +1294,7 @@ impl Context {
|
|||||||
|
|
||||||
let mut suggestion_groups_by_buffer = HashMap::default();
|
let mut suggestion_groups_by_buffer = HashMap::default();
|
||||||
for (buffer, mut suggestions) in suggestions_by_buffer {
|
for (buffer, mut suggestions) in suggestions_by_buffer {
|
||||||
let mut suggestion_groups = Vec::<EditSuggestionGroup>::new();
|
let mut suggestion_groups = Vec::<WorkflowSuggestionGroup>::new();
|
||||||
let snapshot = buffer.update(&mut cx, |buffer, _| buffer.snapshot())?;
|
let snapshot = buffer.update(&mut cx, |buffer, _| buffer.snapshot())?;
|
||||||
// Sort suggestions by their range so that earlier, larger ranges come first
|
// Sort suggestions by their range so that earlier, larger ranges come first
|
||||||
suggestions.sort_by(|a, b| a.range().cmp(&b.range(), &snapshot));
|
suggestions.sort_by(|a, b| a.range().cmp(&b.range(), &snapshot));
|
||||||
@@ -1328,14 +1329,14 @@ impl Context {
|
|||||||
last_group.suggestions.push(suggestion);
|
last_group.suggestions.push(suggestion);
|
||||||
} else {
|
} else {
|
||||||
// Create a new group
|
// Create a new group
|
||||||
suggestion_groups.push(EditSuggestionGroup {
|
suggestion_groups.push(WorkflowSuggestionGroup {
|
||||||
context_range,
|
context_range,
|
||||||
suggestions: vec![suggestion],
|
suggestions: vec![suggestion],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Create the first group
|
// Create the first group
|
||||||
suggestion_groups.push(EditSuggestionGroup {
|
suggestion_groups.push(WorkflowSuggestionGroup {
|
||||||
context_range,
|
context_range,
|
||||||
suggestions: vec![suggestion],
|
suggestions: vec![suggestion],
|
||||||
});
|
});
|
||||||
@@ -1353,12 +1354,10 @@ impl Context {
|
|||||||
})
|
})
|
||||||
.map_err(|_| anyhow!("edit step not found"))?;
|
.map_err(|_| anyhow!("edit step not found"))?;
|
||||||
if let Some(edit_step) = this.workflow_steps.get_mut(step_index) {
|
if let Some(edit_step) = this.workflow_steps.get_mut(step_index) {
|
||||||
edit_step.edit_suggestions = WorkflowStepEditSuggestions::Resolved(
|
edit_step.status = WorkflowStepStatus::Resolved(ResolvedWorkflowStep {
|
||||||
ResolvedWorkflowStepEditSuggestions {
|
title: resolution.step_title,
|
||||||
title: step_suggestions.step_title,
|
suggestions: suggestion_groups_by_buffer,
|
||||||
edit_suggestions: suggestion_groups_by_buffer,
|
});
|
||||||
},
|
|
||||||
);
|
|
||||||
cx.emit(ContextEvent::WorkflowStepsChanged);
|
cx.emit(ContextEvent::WorkflowStepsChanged);
|
||||||
}
|
}
|
||||||
anyhow::Ok(())
|
anyhow::Ok(())
|
||||||
@@ -3022,19 +3021,17 @@ mod tests {
|
|||||||
|
|
||||||
model
|
model
|
||||||
.as_fake()
|
.as_fake()
|
||||||
.respond_to_last_tool_use(Ok(serde_json::to_value(
|
.respond_to_last_tool_use(Ok(serde_json::to_value(tool::WorkflowStepResolution {
|
||||||
tool::WorkflowStepEditSuggestions {
|
step_title: "Title".into(),
|
||||||
step_title: "Title".into(),
|
suggestions: vec![tool::WorkflowSuggestion {
|
||||||
edit_suggestions: vec![tool::EditSuggestion {
|
path: "/root/hello.rs".into(),
|
||||||
path: "/root/hello.rs".into(),
|
// Simulate a symbol name that's slightly different than our outline query
|
||||||
// Simulate a symbol name that's slightly different than our outline query
|
kind: tool::WorkflowSuggestionKind::Update {
|
||||||
kind: tool::EditSuggestionKind::Update {
|
symbol: "fn main()".into(),
|
||||||
symbol: "fn main()".into(),
|
description: "Extract a greeting function".into(),
|
||||||
description: "Extract a greeting function".into(),
|
},
|
||||||
},
|
}],
|
||||||
}],
|
})
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap()));
|
.unwrap()));
|
||||||
|
|
||||||
// Wait for tool use to be processed.
|
// Wait for tool use to be processed.
|
||||||
@@ -3074,11 +3071,9 @@ mod tests {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|step| {
|
.map(|step| {
|
||||||
let buffer = context.buffer.read(cx);
|
let buffer = context.buffer.read(cx);
|
||||||
let status = match &step.edit_suggestions {
|
let status = match &step.status {
|
||||||
WorkflowStepEditSuggestions::Pending(_) => {
|
WorkflowStepStatus::Pending(_) => WorkflowStepEditSuggestionStatus::Pending,
|
||||||
WorkflowStepEditSuggestionStatus::Pending
|
WorkflowStepStatus::Resolved { .. } => {
|
||||||
}
|
|
||||||
WorkflowStepEditSuggestions::Resolved { .. } => {
|
|
||||||
WorkflowStepEditSuggestionStatus::Resolved
|
WorkflowStepEditSuggestionStatus::Resolved
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -3490,15 +3485,15 @@ mod tool {
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
pub struct WorkflowStepEditSuggestions {
|
pub struct WorkflowStepResolution {
|
||||||
/// An extremely short title for the edit step represented by these operations.
|
/// An extremely short title for the edit step represented by these operations.
|
||||||
pub step_title: String,
|
pub step_title: String,
|
||||||
/// A sequence of operations to apply to the codebase.
|
/// A sequence of operations to apply to the codebase.
|
||||||
/// When multiple operations are required for a step, be sure to include multiple operations in this list.
|
/// When multiple operations are required for a step, be sure to include multiple operations in this list.
|
||||||
pub edit_suggestions: Vec<EditSuggestion>,
|
pub suggestions: Vec<WorkflowSuggestion>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LanguageModelTool for WorkflowStepEditSuggestions {
|
impl LanguageModelTool for WorkflowStepResolution {
|
||||||
fn name() -> String {
|
fn name() -> String {
|
||||||
"edit".into()
|
"edit".into()
|
||||||
}
|
}
|
||||||
@@ -3527,19 +3522,19 @@ mod tool {
|
|||||||
/// programmatic changes to source code. It provides a structured way to describe
|
/// programmatic changes to source code. It provides a structured way to describe
|
||||||
/// edits for features like refactoring tools or AI-assisted coding suggestions.
|
/// edits for features like refactoring tools or AI-assisted coding suggestions.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||||
pub struct EditSuggestion {
|
pub struct WorkflowSuggestion {
|
||||||
/// The path to the file containing the relevant operation
|
/// The path to the file containing the relevant operation
|
||||||
pub path: String,
|
pub path: String,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub kind: EditSuggestionKind,
|
pub kind: WorkflowSuggestionKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EditSuggestion {
|
impl WorkflowSuggestion {
|
||||||
pub(super) async fn resolve(
|
pub(super) async fn resolve(
|
||||||
&self,
|
&self,
|
||||||
project: Model<Project>,
|
project: Model<Project>,
|
||||||
mut cx: AsyncAppContext,
|
mut cx: AsyncAppContext,
|
||||||
) -> Result<(Model<Buffer>, super::EditSuggestion)> {
|
) -> Result<(Model<Buffer>, super::WorkflowSuggestion)> {
|
||||||
let path = self.path.clone();
|
let path = self.path.clone();
|
||||||
let kind = self.kind.clone();
|
let kind = self.kind.clone();
|
||||||
let buffer = project
|
let buffer = project
|
||||||
@@ -3561,7 +3556,7 @@ mod tool {
|
|||||||
|
|
||||||
let suggestion;
|
let suggestion;
|
||||||
match kind {
|
match kind {
|
||||||
EditSuggestionKind::Update {
|
WorkflowSuggestionKind::Update {
|
||||||
symbol,
|
symbol,
|
||||||
description,
|
description,
|
||||||
} => {
|
} => {
|
||||||
@@ -3578,12 +3573,12 @@ 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);
|
||||||
suggestion = super::EditSuggestion::Update { range, description };
|
suggestion = super::WorkflowSuggestion::Update { range, description };
|
||||||
}
|
}
|
||||||
EditSuggestionKind::Create { description } => {
|
WorkflowSuggestionKind::Create { description } => {
|
||||||
suggestion = super::EditSuggestion::CreateFile { description };
|
suggestion = super::WorkflowSuggestion::CreateFile { description };
|
||||||
}
|
}
|
||||||
EditSuggestionKind::InsertSiblingBefore {
|
WorkflowSuggestionKind::InsertSiblingBefore {
|
||||||
symbol,
|
symbol,
|
||||||
description,
|
description,
|
||||||
} => {
|
} => {
|
||||||
@@ -3598,12 +3593,12 @@ mod tool {
|
|||||||
annotation_range.start
|
annotation_range.start
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
suggestion = super::EditSuggestion::InsertSiblingBefore {
|
suggestion = super::WorkflowSuggestion::InsertSiblingBefore {
|
||||||
position,
|
position,
|
||||||
description,
|
description,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
EditSuggestionKind::InsertSiblingAfter {
|
WorkflowSuggestionKind::InsertSiblingAfter {
|
||||||
symbol,
|
symbol,
|
||||||
description,
|
description,
|
||||||
} => {
|
} => {
|
||||||
@@ -3612,12 +3607,12 @@ mod tool {
|
|||||||
.with_context(|| format!("symbol not found: {:?}", symbol))?
|
.with_context(|| format!("symbol not found: {:?}", symbol))?
|
||||||
.to_point(&snapshot);
|
.to_point(&snapshot);
|
||||||
let position = snapshot.anchor_after(symbol.range.end);
|
let position = snapshot.anchor_after(symbol.range.end);
|
||||||
suggestion = super::EditSuggestion::InsertSiblingAfter {
|
suggestion = super::WorkflowSuggestion::InsertSiblingAfter {
|
||||||
position,
|
position,
|
||||||
description,
|
description,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
EditSuggestionKind::PrependChild {
|
WorkflowSuggestionKind::PrependChild {
|
||||||
symbol,
|
symbol,
|
||||||
description,
|
description,
|
||||||
} => {
|
} => {
|
||||||
@@ -3632,18 +3627,18 @@ 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),
|
||||||
);
|
);
|
||||||
suggestion = super::EditSuggestion::PrependChild {
|
suggestion = super::WorkflowSuggestion::PrependChild {
|
||||||
position,
|
position,
|
||||||
description,
|
description,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
suggestion = super::EditSuggestion::PrependChild {
|
suggestion = super::WorkflowSuggestion::PrependChild {
|
||||||
position: language::Anchor::MIN,
|
position: language::Anchor::MIN,
|
||||||
description,
|
description,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EditSuggestionKind::AppendChild {
|
WorkflowSuggestionKind::AppendChild {
|
||||||
symbol,
|
symbol,
|
||||||
description,
|
description,
|
||||||
} => {
|
} => {
|
||||||
@@ -3658,18 +3653,18 @@ 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),
|
||||||
);
|
);
|
||||||
suggestion = super::EditSuggestion::AppendChild {
|
suggestion = super::WorkflowSuggestion::AppendChild {
|
||||||
position,
|
position,
|
||||||
description,
|
description,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
suggestion = super::EditSuggestion::PrependChild {
|
suggestion = super::WorkflowSuggestion::PrependChild {
|
||||||
position: language::Anchor::MAX,
|
position: language::Anchor::MAX,
|
||||||
description,
|
description,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EditSuggestionKind::Delete { symbol } => {
|
WorkflowSuggestionKind::Delete { symbol } => {
|
||||||
let symbol = outline
|
let symbol = outline
|
||||||
.find_most_similar(&symbol)
|
.find_most_similar(&symbol)
|
||||||
.with_context(|| format!("symbol not found: {:?}", symbol))?
|
.with_context(|| format!("symbol not found: {:?}", symbol))?
|
||||||
@@ -3683,7 +3678,7 @@ 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);
|
||||||
suggestion = super::EditSuggestion::Delete { range };
|
suggestion = super::WorkflowSuggestion::Delete { range };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3693,7 +3688,7 @@ mod tool {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||||
#[serde(tag = "kind")]
|
#[serde(tag = "kind")]
|
||||||
pub enum EditSuggestionKind {
|
pub enum WorkflowSuggestionKind {
|
||||||
/// Rewrites the specified symbol entirely based on the given description.
|
/// Rewrites the specified symbol entirely based on the given description.
|
||||||
/// This operation completely replaces the existing symbol with new content.
|
/// This operation completely replaces the existing symbol with new content.
|
||||||
Update {
|
Update {
|
||||||
@@ -3754,7 +3749,7 @@ mod tool {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EditSuggestionKind {
|
impl WorkflowSuggestionKind {
|
||||||
pub fn symbol(&self) -> Option<&str> {
|
pub fn symbol(&self) -> Option<&str> {
|
||||||
match self {
|
match self {
|
||||||
Self::Update { symbol, .. } => Some(symbol),
|
Self::Update { symbol, .. } => Some(symbol),
|
||||||
@@ -3781,14 +3776,14 @@ mod tool {
|
|||||||
|
|
||||||
pub fn initial_insertion(&self) -> Option<InitialInsertion> {
|
pub fn initial_insertion(&self) -> Option<InitialInsertion> {
|
||||||
match self {
|
match self {
|
||||||
EditSuggestionKind::InsertSiblingBefore { .. } => {
|
WorkflowSuggestionKind::InsertSiblingBefore { .. } => {
|
||||||
Some(InitialInsertion::NewlineAfter)
|
Some(InitialInsertion::NewlineAfter)
|
||||||
}
|
}
|
||||||
EditSuggestionKind::InsertSiblingAfter { .. } => {
|
WorkflowSuggestionKind::InsertSiblingAfter { .. } => {
|
||||||
Some(InitialInsertion::NewlineBefore)
|
Some(InitialInsertion::NewlineBefore)
|
||||||
}
|
}
|
||||||
EditSuggestionKind::PrependChild { .. } => Some(InitialInsertion::NewlineAfter),
|
WorkflowSuggestionKind::PrependChild { .. } => Some(InitialInsertion::NewlineAfter),
|
||||||
EditSuggestionKind::AppendChild { .. } => Some(InitialInsertion::NewlineBefore),
|
WorkflowSuggestionKind::AppendChild { .. } => Some(InitialInsertion::NewlineBefore),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -894,6 +894,11 @@ impl Item for Editor {
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn edited_since_preview(&self, _cx: &AppContext) -> bool {
|
||||||
|
// todo! actually implement this
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SerializableItem for Editor {
|
impl SerializableItem for Editor {
|
||||||
|
|||||||
@@ -287,6 +287,10 @@ pub trait Item: FocusableView + EventEmitter<Self::Event> {
|
|||||||
fn pixel_position_of_cursor(&self, _: &AppContext) -> Option<Point<Pixels>> {
|
fn pixel_position_of_cursor(&self, _: &AppContext) -> Option<Point<Pixels>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn edited_since_preview(&self, _cx: &AppContext) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait SerializableItem: Item {
|
pub trait SerializableItem: Item {
|
||||||
@@ -427,6 +431,7 @@ pub trait ItemHandle: 'static + Send {
|
|||||||
fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option<Point<Pixels>>;
|
fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option<Point<Pixels>>;
|
||||||
fn downgrade_item(&self) -> Box<dyn WeakItemHandle>;
|
fn downgrade_item(&self) -> Box<dyn WeakItemHandle>;
|
||||||
fn workspace_settings<'a>(&self, cx: &'a AppContext) -> &'a WorkspaceSettings;
|
fn workspace_settings<'a>(&self, cx: &'a AppContext) -> &'a WorkspaceSettings;
|
||||||
|
fn edited_since_preview(&self, cx: &AppContext) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait WeakItemHandle: Send + Sync {
|
pub trait WeakItemHandle: Send + Sync {
|
||||||
@@ -818,6 +823,10 @@ impl<T: Item> ItemHandle for View<T> {
|
|||||||
) -> Option<Box<dyn SerializableItemHandle>> {
|
) -> Option<Box<dyn SerializableItemHandle>> {
|
||||||
SerializableItemRegistry::view_to_serializable_item_handle(self.to_any(), cx)
|
SerializableItemRegistry::view_to_serializable_item_handle(self.to_any(), cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn edited_since_preview(&self, cx: &AppContext) -> bool {
|
||||||
|
self.read(cx).edited_since_preview(cx)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Box<dyn ItemHandle>> for AnyView {
|
impl From<Box<dyn ItemHandle>> for AnyView {
|
||||||
|
|||||||
@@ -665,6 +665,12 @@ impl Pane {
|
|||||||
self.preview_item_id
|
self.preview_item_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn preview_item(&self) -> Option<Box<dyn ItemHandle>> {
|
||||||
|
self.preview_item_id
|
||||||
|
.and_then(|id| self.items.iter().find(|item| item.item_id() == id))
|
||||||
|
.cloned()
|
||||||
|
}
|
||||||
|
|
||||||
fn preview_item_idx(&self) -> Option<usize> {
|
fn preview_item_idx(&self) -> Option<usize> {
|
||||||
if let Some(preview_item_id) = self.preview_item_id {
|
if let Some(preview_item_id) = self.preview_item_id {
|
||||||
self.items
|
self.items
|
||||||
@@ -688,9 +694,9 @@ impl Pane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_item_edit(&mut self, item_id: EntityId, cx: &AppContext) {
|
pub fn handle_item_edit(&mut self, item_id: EntityId, cx: &AppContext) {
|
||||||
if let Some(preview_item_id) = self.preview_item_id {
|
if let Some(preview_item) = self.preview_item() {
|
||||||
if preview_item_id == item_id {
|
if preview_item.item_id() == item_id && preview_item.edited_since_preview(cx) {
|
||||||
self.set_preview_item_id(None, cx)
|
self.set_preview_item_id(None, cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2611,6 +2611,25 @@ impl Workspace {
|
|||||||
open_project_item
|
open_project_item
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_project_item_open<T>(
|
||||||
|
&self,
|
||||||
|
pane: &View<Pane>,
|
||||||
|
project_item: &Model<T::Item>,
|
||||||
|
cx: &AppContext,
|
||||||
|
) -> bool
|
||||||
|
where
|
||||||
|
T: ProjectItem,
|
||||||
|
{
|
||||||
|
use project::Item as _;
|
||||||
|
|
||||||
|
project_item
|
||||||
|
.read(cx)
|
||||||
|
.entry_id(cx)
|
||||||
|
.and_then(|entry_id| pane.read(cx).item_for_entry(entry_id, cx))
|
||||||
|
.and_then(|item| item.downcast::<T>())
|
||||||
|
.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn open_project_item<T>(
|
pub fn open_project_item<T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
pane: View<Pane>,
|
pane: View<Pane>,
|
||||||
|
|||||||
Reference in New Issue
Block a user