Improve workflow suggestion steps and debug info (#16309)
Release Notes: - N/A --------- Co-authored-by: Nathan Sobo <nathan@zed.dev> Co-authored-by: Bennet Bo Fenner <bennet@zed.dev>
This commit is contained in:
parent
6b7664ef4a
commit
ff83e5b55a
6 changed files with 311 additions and 157 deletions
|
@ -1,22 +1,27 @@
|
||||||
Your task is to map a step from the conversation above to suggestions on symbols inside the provided source files.
|
<overview>
|
||||||
|
Your task is to map a step from a workflow to locations in source code where code needs to be changed to fulfill that step.
|
||||||
|
Given a workflow containing background context plus a series of <step> tags, you will resolve *one* of these step tags to resolve to one or more locations in the code.
|
||||||
|
With each location, you will produce a brief, one-line description of the changes to be made.
|
||||||
|
|
||||||
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.
|
||||||
|
- Only reference locations that actually exist (unless you're creating a file).
|
||||||
- If creating a file, assume any subsequent updates are included at the time of creation.
|
- If creating a file, assume any subsequent updates are included at the time of creation.
|
||||||
- Don't create and then update a file.
|
- Don't create and then update a file. Always create new files in 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 suggestions on a parent symbol and one of its children in the same suggestions 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 suggestions 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.
|
|
||||||
- Descriptions are required for all suggestions except delete.
|
- Descriptions are required for all suggestions except delete.
|
||||||
- When generating multiple suggestions, 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 suggestions 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.
|
||||||
|
</guidelines>
|
||||||
|
</overview>
|
||||||
|
|
||||||
Example 1:
|
<examples>
|
||||||
|
<example>
|
||||||
User:
|
<workflow_context>
|
||||||
|
<message role="user">
|
||||||
```rs src/rectangle.rs
|
```rs src/rectangle.rs
|
||||||
struct Rectangle {
|
struct Rectangle {
|
||||||
width: f64,
|
width: f64,
|
||||||
|
@ -30,12 +35,21 @@ impl Rectangle {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
We need to add methods to calculate the area and perimeter of the rectangle. Can you help with that?
|
||||||
|
</message>
|
||||||
|
<message role="assistant">
|
||||||
|
Sure, I can help with that!
|
||||||
|
|
||||||
<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>
|
||||||
|
</message>
|
||||||
|
</workflow_context>
|
||||||
|
|
||||||
What are the suggestions for the step: <step>Add a new method 'calculate_area' to the Rectangle struct</step>
|
<step_to_resolve>
|
||||||
|
Add new methods 'calculate_area' and 'calculate_perimeter' to the Rectangle struct
|
||||||
|
</step_to_resolve>
|
||||||
|
|
||||||
A (wrong):
|
<incorrect_output reason="NEVER append multiple children at the same location.">
|
||||||
{
|
{
|
||||||
"title": "Add Rectangle methods",
|
"title": "Add Rectangle methods",
|
||||||
"suggestions": [
|
"suggestions": [
|
||||||
|
@ -53,10 +67,9 @@ A (wrong):
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
</incorrect_output>
|
||||||
|
|
||||||
This demonstrates what NOT to do. NEVER append multiple children at the same location.
|
<correct_output>
|
||||||
|
|
||||||
A (corrected):
|
|
||||||
{
|
{
|
||||||
"title": "Add Rectangle methods",
|
"title": "Add Rectangle methods",
|
||||||
"suggestions": [
|
"suggestions": [
|
||||||
|
@ -68,11 +81,13 @@ A (corrected):
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
</correct_output>
|
||||||
|
|
||||||
User:
|
<step_to_resolve>
|
||||||
What are the suggestions for the step: <step>Implement the 'Display' trait for the Rectangle struct</step>
|
Implement the 'Display' trait for the Rectangle struct
|
||||||
|
</step_to_resolve>
|
||||||
|
|
||||||
A:
|
<output>
|
||||||
{
|
{
|
||||||
"title": "Implement Display for Rectangle",
|
"title": "Implement Display for Rectangle",
|
||||||
"suggestions": [
|
"suggestions": [
|
||||||
|
@ -84,10 +99,11 @@ A:
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
</output>
|
||||||
|
|
||||||
Example 2:
|
<example>
|
||||||
|
<workflow_context>
|
||||||
User:
|
<message role="user">
|
||||||
```rs src/user.rs
|
```rs src/user.rs
|
||||||
struct User {
|
struct User {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
@ -105,13 +121,19 @@ impl User {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
</message>
|
||||||
|
<message role="assistant">
|
||||||
|
Certainly!
|
||||||
<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>
|
||||||
|
</message>
|
||||||
|
</workflow_context>
|
||||||
|
|
||||||
What are the suggestions for the step: <step>Update the 'print_info' method to use formatted output</step>
|
<step_to_resolve>
|
||||||
|
Update the 'print_info' method to use formatted output
|
||||||
|
</step_to_resolve>
|
||||||
|
|
||||||
A:
|
<output>
|
||||||
{
|
{
|
||||||
"title": "Use formatted output",
|
"title": "Use formatted output",
|
||||||
"suggestions": [
|
"suggestions": [
|
||||||
|
@ -123,11 +145,13 @@ A:
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
</output>
|
||||||
|
|
||||||
User:
|
<step_to_resolve>
|
||||||
What are the suggestions for the step: <step>Remove the 'email' field from the User struct</step>
|
Remove the 'email' field from the User struct
|
||||||
|
</step_to_resolve>
|
||||||
|
|
||||||
A:
|
<output>
|
||||||
{
|
{
|
||||||
"title": "Remove email field",
|
"title": "Remove email field",
|
||||||
"suggestions": [
|
"suggestions": [
|
||||||
|
@ -138,10 +162,12 @@ A:
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
</output>
|
||||||
|
</example>
|
||||||
|
|
||||||
Example 3:
|
<example>
|
||||||
|
<workflow_context>
|
||||||
User:
|
<message role="user">
|
||||||
```rs src/vehicle.rs
|
```rs src/vehicle.rs
|
||||||
struct Vehicle {
|
struct Vehicle {
|
||||||
make: String,
|
make: String,
|
||||||
|
@ -159,13 +185,18 @@ impl Vehicle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
</message>
|
||||||
|
<message role="assistant">
|
||||||
<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>
|
||||||
|
</message>
|
||||||
|
</workflow_context>
|
||||||
|
|
||||||
What are the suggestions for the step: <step>Add a 'use std::fmt;' statement at the beginning of the file</step>
|
<step_to_resolve>
|
||||||
|
Add a 'use std::fmt;' statement at the beginning of the file
|
||||||
|
</step_to_resolve>
|
||||||
|
|
||||||
A:
|
<output>
|
||||||
{
|
{
|
||||||
"title": "Add use std::fmt statement",
|
"title": "Add use std::fmt statement",
|
||||||
"suggestions": [
|
"suggestions": [
|
||||||
|
@ -176,11 +207,13 @@ A:
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
</output>
|
||||||
|
|
||||||
User:
|
<step_to_resolve>
|
||||||
What are the suggestions for the step: <step>Add a new method 'start_engine' in the Vehicle impl block</step>
|
Add a new method 'start_engine' in the Vehicle impl block
|
||||||
|
</step_to_resolve>
|
||||||
|
|
||||||
A:
|
<output>
|
||||||
{
|
{
|
||||||
"title": "Add start_engine method",
|
"title": "Add start_engine method",
|
||||||
"suggestions": [
|
"suggestions": [
|
||||||
|
@ -192,10 +225,12 @@ A:
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
</output>
|
||||||
|
</example>
|
||||||
|
|
||||||
Example 4:
|
<example>
|
||||||
|
<workflow_context>
|
||||||
User:
|
<message role="user">
|
||||||
```rs src/employee.rs
|
```rs src/employee.rs
|
||||||
struct Employee {
|
struct Employee {
|
||||||
name: String,
|
name: String,
|
||||||
|
@ -219,12 +254,18 @@ impl Employee {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
</message>
|
||||||
|
<message role="assistant">
|
||||||
<step>Make salary an f32</step>
|
<step>Make salary an f32</step>
|
||||||
|
<step>Remove the 'department' field and update the 'print_details' method</step>
|
||||||
|
</message>
|
||||||
|
</workflow_context>
|
||||||
|
|
||||||
What are the suggestions for the step: <step>Make salary an f32</step>
|
<step_to_resolve>
|
||||||
|
Make salary an f32
|
||||||
|
</step_to_resolve>
|
||||||
|
|
||||||
A (wrong):
|
<incorrect_output reason="NEVER include suggestions on a parent symbol and one of its children in the same suggestions block.">
|
||||||
{
|
{
|
||||||
"title": "Change salary to f32",
|
"title": "Change salary to f32",
|
||||||
"suggestions": [
|
"suggestions": [
|
||||||
|
@ -242,10 +283,9 @@ A (wrong):
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
</incorrect_output>
|
||||||
|
|
||||||
This example demonstrates what not to do. `struct Employee salary` is a child of `struct Employee`.
|
<correct_output>
|
||||||
|
|
||||||
A (corrected):
|
|
||||||
{
|
{
|
||||||
"title": "Change salary to f32",
|
"title": "Change salary to f32",
|
||||||
"suggestions": [
|
"suggestions": [
|
||||||
|
@ -257,11 +297,13 @@ A (corrected):
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
</correct_output>
|
||||||
|
|
||||||
User:
|
<step_to_resolve>
|
||||||
What are the correct suggestions for the step: <step>Remove the 'department' field and update the 'print_details' method</step>
|
Remove the 'department' field and update the 'print_details' method
|
||||||
|
</step_to_resolve>
|
||||||
|
|
||||||
A:
|
<output>
|
||||||
{
|
{
|
||||||
"title": "Remove department",
|
"title": "Remove department",
|
||||||
"suggestions": [
|
"suggestions": [
|
||||||
|
@ -278,10 +320,12 @@ A:
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
</output>
|
||||||
|
</example>
|
||||||
|
|
||||||
Example 5:
|
<example>
|
||||||
|
<workflow_context>
|
||||||
User:
|
<message role="user">
|
||||||
```rs src/game.rs
|
```rs src/game.rs
|
||||||
struct Player {
|
struct Player {
|
||||||
name: String,
|
name: String,
|
||||||
|
@ -305,10 +349,17 @@ impl Game {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
</message>
|
||||||
|
<message role="assistant">
|
||||||
<step>Add a 'level' field to Player and update the 'new' method</step>
|
<step>Add a 'level' field to Player and update the 'new' method</step>
|
||||||
|
</message>
|
||||||
|
</workflow_context>
|
||||||
|
|
||||||
A:
|
<step_to_resolve>
|
||||||
|
Add a 'level' field to Player and update the 'new' method
|
||||||
|
</step_to_resolve>
|
||||||
|
|
||||||
|
<output>
|
||||||
{
|
{
|
||||||
"title": "Add level field to Player",
|
"title": "Add level field to Player",
|
||||||
"suggestions": [
|
"suggestions": [
|
||||||
|
@ -326,10 +377,12 @@ A:
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
</output>
|
||||||
|
</example>
|
||||||
|
|
||||||
Example 6:
|
<example>
|
||||||
|
<workflow_context>
|
||||||
User:
|
<message role="user">
|
||||||
```rs src/config.rs
|
```rs src/config.rs
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
@ -343,10 +396,17 @@ impl Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
</message>
|
||||||
|
<message role="assistant">
|
||||||
<step>Add a 'load_from_file' method to Config and import necessary modules</step>
|
<step>Add a 'load_from_file' method to Config and import necessary modules</step>
|
||||||
|
</message>
|
||||||
|
</workflow_context>
|
||||||
|
|
||||||
A:
|
<step_to_resolve>
|
||||||
|
Add a 'load_from_file' method to Config and import necessary modules
|
||||||
|
</step_to_resolve>
|
||||||
|
|
||||||
|
<output>
|
||||||
{
|
{
|
||||||
"title": "Add load_from_file method",
|
"title": "Add load_from_file method",
|
||||||
"suggestions": [
|
"suggestions": [
|
||||||
|
@ -363,10 +423,12 @@ A:
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
</output>
|
||||||
|
</example>
|
||||||
|
|
||||||
Example 7:
|
<example>
|
||||||
|
<workflow_context>
|
||||||
User:
|
<message role="user">
|
||||||
```rs src/database.rs
|
```rs src/database.rs
|
||||||
pub(crate) struct Database {
|
pub(crate) struct Database {
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
|
@ -383,10 +445,17 @@ impl Database {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
</message>
|
||||||
|
<message role="assistant">
|
||||||
<step>Add error handling to the 'query' method and create a custom error type</step>
|
<step>Add error handling to the 'query' method and create a custom error type</step>
|
||||||
|
</message>
|
||||||
|
</workflow_context>
|
||||||
|
|
||||||
A:
|
<step_to_resolve>
|
||||||
|
Add error handling to the 'query' method and create a custom error type
|
||||||
|
</step_to_resolve>
|
||||||
|
|
||||||
|
<output>
|
||||||
{
|
{
|
||||||
"title": "Add error handling to query",
|
"title": "Add error handling to query",
|
||||||
"suggestions": [
|
"suggestions": [
|
||||||
|
@ -409,5 +478,16 @@ A:
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
</output>
|
||||||
|
</example>
|
||||||
|
</examples>
|
||||||
|
|
||||||
Now generate the suggestions for the following step:
|
Now generate the suggestions for the following step:
|
||||||
|
|
||||||
|
<workflow_context>
|
||||||
|
{{{workflow_context}}}
|
||||||
|
</workflow_context>
|
||||||
|
|
||||||
|
<step_to_resolve>
|
||||||
|
{{{step_to_resolve}}}
|
||||||
|
</step_to_resolve>
|
||||||
|
|
|
@ -2911,7 +2911,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.show(
|
assist_ids.extend(suggestion.kind.show(
|
||||||
&editor,
|
&editor,
|
||||||
excerpt_id,
|
excerpt_id,
|
||||||
workspace,
|
workspace,
|
||||||
|
|
|
@ -2974,12 +2974,12 @@ mod tests {
|
||||||
|
|
||||||
model
|
model
|
||||||
.as_fake()
|
.as_fake()
|
||||||
.respond_to_last_tool_use(tool::WorkflowStepResolution {
|
.respond_to_last_tool_use(tool::WorkflowStepResolutionTool {
|
||||||
step_title: "Title".into(),
|
step_title: "Title".into(),
|
||||||
suggestions: vec![tool::WorkflowSuggestion {
|
suggestions: vec![tool::WorkflowSuggestionTool {
|
||||||
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::WorkflowSuggestionToolKind::Update {
|
||||||
symbol: "fn main()".into(),
|
symbol: "fn main()".into(),
|
||||||
description: "Extract a greeting function".into(),
|
description: "Extract a greeting function".into(),
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,7 +11,7 @@ use ui::{
|
||||||
div, h_flex, px, Color, Element as _, ParentElement as _, Styled, ViewContext, WindowContext,
|
div, h_flex, px, Color, Element as _, ParentElement as _, Styled, ViewContext, WindowContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Context, ResolvedWorkflowStep, WorkflowSuggestion};
|
use crate::{Context, ResolvedWorkflowStep, WorkflowSuggestion, WorkflowSuggestionKind};
|
||||||
|
|
||||||
type StepRange = Range<language::Anchor>;
|
type StepRange = Range<language::Anchor>;
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ impl ContextInspector {
|
||||||
.and_then(|file| file.path().to_str())
|
.and_then(|file| file.path().to_str())
|
||||||
.unwrap_or("untitled");
|
.unwrap_or("untitled");
|
||||||
let snapshot = buffer.text_snapshot();
|
let snapshot = buffer.text_snapshot();
|
||||||
writeln!(output, " {buffer_path}:").ok()?;
|
writeln!(output, "Path: {buffer_path}:").ok()?;
|
||||||
for group in suggestion_groups {
|
for group in suggestion_groups {
|
||||||
for suggestion in &group.suggestions {
|
for suggestion in &group.suggestions {
|
||||||
pretty_print_workflow_suggestion(&mut output, suggestion, &snapshot);
|
pretty_print_workflow_suggestion(&mut output, suggestion, &snapshot);
|
||||||
|
@ -163,7 +163,7 @@ impl ContextInspector {
|
||||||
}
|
}
|
||||||
fn pretty_print_anchor(
|
fn pretty_print_anchor(
|
||||||
out: &mut String,
|
out: &mut String,
|
||||||
anchor: &language::Anchor,
|
anchor: language::Anchor,
|
||||||
snapshot: &text::BufferSnapshot,
|
snapshot: &text::BufferSnapshot,
|
||||||
) {
|
) {
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
@ -177,9 +177,9 @@ fn pretty_print_range(
|
||||||
) {
|
) {
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
write!(out, " Range: ").ok();
|
write!(out, " Range: ").ok();
|
||||||
pretty_print_anchor(out, &range.start, snapshot);
|
pretty_print_anchor(out, range.start, snapshot);
|
||||||
write!(out, "..").ok();
|
write!(out, "..").ok();
|
||||||
pretty_print_anchor(out, &range.end, snapshot);
|
pretty_print_anchor(out, range.end, snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pretty_print_workflow_suggestion(
|
fn pretty_print_workflow_suggestion(
|
||||||
|
@ -188,37 +188,46 @@ fn pretty_print_workflow_suggestion(
|
||||||
snapshot: &text::BufferSnapshot,
|
snapshot: &text::BufferSnapshot,
|
||||||
) {
|
) {
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
let (range, description, position) = match suggestion {
|
let (position, description, range) = match &suggestion.kind {
|
||||||
WorkflowSuggestion::Update { range, description } => (Some(range), Some(description), None),
|
WorkflowSuggestionKind::Update { range, description } => {
|
||||||
WorkflowSuggestion::CreateFile { description } => (None, Some(description), None),
|
(None, Some(description), Some(range))
|
||||||
WorkflowSuggestion::AppendChild {
|
|
||||||
position,
|
|
||||||
description,
|
|
||||||
}
|
}
|
||||||
| WorkflowSuggestion::InsertSiblingBefore {
|
WorkflowSuggestionKind::CreateFile { description } => (None, Some(description), None),
|
||||||
|
WorkflowSuggestionKind::AppendChild {
|
||||||
position,
|
position,
|
||||||
description,
|
description,
|
||||||
}
|
} => (Some(position), Some(description), None),
|
||||||
| WorkflowSuggestion::InsertSiblingAfter {
|
WorkflowSuggestionKind::InsertSiblingBefore {
|
||||||
position,
|
position,
|
||||||
description,
|
description,
|
||||||
}
|
} => (Some(position), Some(description), None),
|
||||||
| WorkflowSuggestion::PrependChild {
|
WorkflowSuggestionKind::InsertSiblingAfter {
|
||||||
position,
|
position,
|
||||||
description,
|
description,
|
||||||
} => (None, Some(description), Some(position)),
|
} => (Some(position), Some(description), None),
|
||||||
|
WorkflowSuggestionKind::PrependChild {
|
||||||
WorkflowSuggestion::Delete { range } => (Some(range), None, None),
|
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 {
|
if let Some(description) = description {
|
||||||
writeln!(out, " Description: {description}").ok();
|
writeln!(out, " Description: {description}").ok();
|
||||||
}
|
}
|
||||||
if let Some(range) = range {
|
if let Some(range) = range {
|
||||||
pretty_print_range(out, range, snapshot);
|
pretty_print_range(out, &range, snapshot);
|
||||||
}
|
}
|
||||||
if let Some(position) = position {
|
if let Some(position) = position {
|
||||||
write!(out, " Position: ").ok();
|
write!(out, " Position: ").ok();
|
||||||
pretty_print_anchor(out, position, snapshot);
|
pretty_print_anchor(out, *position, snapshot);
|
||||||
write!(out, "\n").ok();
|
write!(out, "\n").ok();
|
||||||
}
|
}
|
||||||
write!(out, "\n").ok();
|
write!(out, "\n").ok();
|
||||||
|
|
|
@ -31,6 +31,15 @@ pub struct TerminalAssistantPromptContext {
|
||||||
pub user_prompt: String,
|
pub user_prompt: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Context required to generate a workflow step resolution prompt.
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct StepResolutionContext {
|
||||||
|
/// The full context, including <step>...</step> tags
|
||||||
|
pub workflow_context: String,
|
||||||
|
/// The text of the specific step from the context to resolve
|
||||||
|
pub step_to_resolve: String,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct PromptBuilder {
|
pub struct PromptBuilder {
|
||||||
handlebars: Arc<Mutex<Handlebars<'static>>>,
|
handlebars: Arc<Mutex<Handlebars<'static>>>,
|
||||||
}
|
}
|
||||||
|
@ -278,7 +287,10 @@ impl PromptBuilder {
|
||||||
self.handlebars.lock().render("edit_workflow", &())
|
self.handlebars.lock().render("edit_workflow", &())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_step_resolution_prompt(&self) -> Result<String, RenderError> {
|
pub fn generate_step_resolution_prompt(
|
||||||
self.handlebars.lock().render("step_resolution", &())
|
&self,
|
||||||
|
context: &StepResolutionContext,
|
||||||
|
) -> Result<String, RenderError> {
|
||||||
|
self.handlebars.lock().render("step_resolution", context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
use crate::{AssistantPanel, Context, InlineAssistId, InlineAssistant};
|
use crate::{
|
||||||
|
prompts::StepResolutionContext, AssistantPanel, Context, InlineAssistId, InlineAssistant,
|
||||||
|
};
|
||||||
use anyhow::{anyhow, Error, Result};
|
use anyhow::{anyhow, Error, Result};
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use editor::Editor;
|
use editor::Editor;
|
||||||
|
@ -10,7 +12,7 @@ use project::Project;
|
||||||
use rope::Point;
|
use rope::Point;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use smol::stream::StreamExt;
|
use smol::stream::StreamExt;
|
||||||
use std::{cmp, ops::Range, sync::Arc};
|
use std::{cmp, fmt::Write, ops::Range, sync::Arc};
|
||||||
use text::{AnchorRangeExt as _, OffsetRangeExt as _};
|
use text::{AnchorRangeExt as _, OffsetRangeExt as _};
|
||||||
use util::ResultExt as _;
|
use util::ResultExt as _;
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
@ -34,7 +36,36 @@ pub struct WorkflowSuggestionGroup {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum WorkflowSuggestion {
|
pub struct 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 {
|
||||||
range: Range<language::Anchor>,
|
range: Range<language::Anchor>,
|
||||||
description: String,
|
description: String,
|
||||||
|
@ -87,6 +118,15 @@ impl WorkflowStepResolution {
|
||||||
.text_for_range(self.tagged_range.clone())
|
.text_for_range(self.tagged_range.clone())
|
||||||
.collect::<String>();
|
.collect::<String>();
|
||||||
|
|
||||||
|
let mut workflow_context = String::new();
|
||||||
|
for message in context.messages(cx) {
|
||||||
|
write!(&mut workflow_context, "<message role={}>", message.role).unwrap();
|
||||||
|
for chunk in context_buffer.read(cx).text_for_range(message.offset_range) {
|
||||||
|
write!(&mut workflow_context, "{chunk}").unwrap();
|
||||||
|
}
|
||||||
|
write!(&mut workflow_context, "</message>").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
Some(cx.spawn(|this, mut cx| async move {
|
Some(cx.spawn(|this, mut cx| async move {
|
||||||
let result = async {
|
let result = async {
|
||||||
let Some(model) = model else {
|
let Some(model) = model else {
|
||||||
|
@ -99,7 +139,12 @@ impl WorkflowStepResolution {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let mut prompt = prompt_builder.generate_step_resolution_prompt()?;
|
let resolution_context = StepResolutionContext {
|
||||||
|
workflow_context,
|
||||||
|
step_to_resolve: step_text.clone(),
|
||||||
|
};
|
||||||
|
let mut prompt =
|
||||||
|
prompt_builder.generate_step_resolution_prompt(&resolution_context)?;
|
||||||
prompt.push_str(&step_text);
|
prompt.push_str(&step_text);
|
||||||
request.messages.push(LanguageModelRequestMessage {
|
request.messages.push(LanguageModelRequestMessage {
|
||||||
role: Role::User,
|
role: Role::User,
|
||||||
|
@ -108,7 +153,7 @@ impl WorkflowStepResolution {
|
||||||
|
|
||||||
// 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 mut stream = model
|
let mut stream = model
|
||||||
.use_tool_stream::<tool::WorkflowStepResolution>(request, &cx)
|
.use_tool_stream::<tool::WorkflowStepResolutionTool>(request, &cx)
|
||||||
.await?;
|
.await?;
|
||||||
while let Some(chunk) = stream.next().await {
|
while let Some(chunk) = stream.next().await {
|
||||||
let chunk = chunk?;
|
let chunk = chunk?;
|
||||||
|
@ -119,14 +164,16 @@ impl WorkflowStepResolution {
|
||||||
}
|
}
|
||||||
|
|
||||||
let resolution = this.update(&mut cx, |this, _| {
|
let resolution = this.update(&mut cx, |this, _| {
|
||||||
serde_json::from_str::<tool::WorkflowStepResolution>(&this.output)
|
serde_json::from_str::<tool::WorkflowStepResolutionTool>(&this.output)
|
||||||
})??;
|
})??;
|
||||||
|
|
||||||
// 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<_> = resolution
|
let suggestion_tasks: Vec<_> = resolution
|
||||||
.suggestions
|
.suggestions
|
||||||
.iter()
|
.iter()
|
||||||
.map(|suggestion| suggestion.resolve(project.clone(), cx.clone()))
|
.map(|suggestion| {
|
||||||
|
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.
|
||||||
|
@ -152,7 +199,7 @@ impl WorkflowStepResolution {
|
||||||
suggestions.sort_by(|a, b| a.range().cmp(&b.range(), &snapshot));
|
suggestions.sort_by(|a, b| a.range().cmp(&b.range(), &snapshot));
|
||||||
|
|
||||||
// Merge overlapping suggestions
|
// Merge overlapping suggestions
|
||||||
suggestions.dedup_by(|a, b| b.try_merge(&a, &snapshot));
|
suggestions.dedup_by(|a, b| b.try_merge(a, &snapshot));
|
||||||
|
|
||||||
// Create context ranges for each suggestion
|
// Create context ranges for each suggestion
|
||||||
for suggestion in suggestions {
|
for suggestion in suggestions {
|
||||||
|
@ -214,40 +261,40 @@ impl WorkflowStepResolution {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WorkflowSuggestion {
|
impl WorkflowSuggestionKind {
|
||||||
pub fn range(&self) -> Range<language::Anchor> {
|
pub fn range(&self) -> Range<language::Anchor> {
|
||||||
match self {
|
match self {
|
||||||
WorkflowSuggestion::Update { range, .. } => range.clone(),
|
Self::Update { range, .. } => range.clone(),
|
||||||
WorkflowSuggestion::CreateFile { .. } => language::Anchor::MIN..language::Anchor::MAX,
|
Self::CreateFile { .. } => language::Anchor::MIN..language::Anchor::MAX,
|
||||||
WorkflowSuggestion::InsertSiblingBefore { position, .. }
|
Self::InsertSiblingBefore { position, .. }
|
||||||
| WorkflowSuggestion::InsertSiblingAfter { position, .. }
|
| Self::InsertSiblingAfter { position, .. }
|
||||||
| WorkflowSuggestion::PrependChild { position, .. }
|
| Self::PrependChild { position, .. }
|
||||||
| WorkflowSuggestion::AppendChild { position, .. } => *position..*position,
|
| Self::AppendChild { position, .. } => *position..*position,
|
||||||
WorkflowSuggestion::Delete { range } => range.clone(),
|
Self::Delete { range } => range.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn description(&self) -> Option<&str> {
|
pub fn description(&self) -> Option<&str> {
|
||||||
match self {
|
match self {
|
||||||
WorkflowSuggestion::Update { description, .. }
|
Self::Update { description, .. }
|
||||||
| WorkflowSuggestion::CreateFile { description }
|
| Self::CreateFile { description }
|
||||||
| WorkflowSuggestion::InsertSiblingBefore { description, .. }
|
| Self::InsertSiblingBefore { description, .. }
|
||||||
| WorkflowSuggestion::InsertSiblingAfter { description, .. }
|
| Self::InsertSiblingAfter { description, .. }
|
||||||
| WorkflowSuggestion::PrependChild { description, .. }
|
| Self::PrependChild { description, .. }
|
||||||
| WorkflowSuggestion::AppendChild { description, .. } => Some(description),
|
| Self::AppendChild { description, .. } => Some(description),
|
||||||
WorkflowSuggestion::Delete { .. } => None,
|
Self::Delete { .. } => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn description_mut(&mut self) -> Option<&mut String> {
|
fn description_mut(&mut self) -> Option<&mut String> {
|
||||||
match self {
|
match self {
|
||||||
WorkflowSuggestion::Update { description, .. }
|
Self::Update { description, .. }
|
||||||
| WorkflowSuggestion::CreateFile { description }
|
| Self::CreateFile { description }
|
||||||
| WorkflowSuggestion::InsertSiblingBefore { description, .. }
|
| Self::InsertSiblingBefore { description, .. }
|
||||||
| WorkflowSuggestion::InsertSiblingAfter { description, .. }
|
| Self::InsertSiblingAfter { description, .. }
|
||||||
| WorkflowSuggestion::PrependChild { description, .. }
|
| Self::PrependChild { description, .. }
|
||||||
| WorkflowSuggestion::AppendChild { description, .. } => Some(description),
|
| Self::AppendChild { description, .. } => Some(description),
|
||||||
WorkflowSuggestion::Delete { .. } => None,
|
Self::Delete { .. } => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,16 +333,16 @@ impl WorkflowSuggestion {
|
||||||
let snapshot = buffer.read(cx).snapshot(cx);
|
let snapshot = buffer.read(cx).snapshot(cx);
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
WorkflowSuggestion::Update { range, description } => {
|
Self::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)?;
|
||||||
}
|
}
|
||||||
WorkflowSuggestion::CreateFile { description } => {
|
Self::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();
|
||||||
}
|
}
|
||||||
WorkflowSuggestion::InsertSiblingBefore {
|
Self::InsertSiblingBefore {
|
||||||
position,
|
position,
|
||||||
description,
|
description,
|
||||||
} => {
|
} => {
|
||||||
|
@ -311,7 +358,7 @@ impl WorkflowSuggestion {
|
||||||
line_start..line_start
|
line_start..line_start
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
WorkflowSuggestion::InsertSiblingAfter {
|
Self::InsertSiblingAfter {
|
||||||
position,
|
position,
|
||||||
description,
|
description,
|
||||||
} => {
|
} => {
|
||||||
|
@ -327,7 +374,7 @@ impl WorkflowSuggestion {
|
||||||
line_start..line_start
|
line_start..line_start
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
WorkflowSuggestion::PrependChild {
|
Self::PrependChild {
|
||||||
position,
|
position,
|
||||||
description,
|
description,
|
||||||
} => {
|
} => {
|
||||||
|
@ -343,7 +390,7 @@ impl WorkflowSuggestion {
|
||||||
line_start..line_start
|
line_start..line_start
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
WorkflowSuggestion::AppendChild {
|
Self::AppendChild {
|
||||||
position,
|
position,
|
||||||
description,
|
description,
|
||||||
} => {
|
} => {
|
||||||
|
@ -359,7 +406,7 @@ impl WorkflowSuggestion {
|
||||||
line_start..line_start
|
line_start..line_start
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
WorkflowSuggestion::Delete { range } => {
|
Self::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)?;
|
||||||
|
@ -392,15 +439,15 @@ pub mod tool {
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
pub struct WorkflowStepResolution {
|
pub struct WorkflowStepResolutionTool {
|
||||||
/// 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 suggestions: Vec<WorkflowSuggestion>,
|
pub suggestions: Vec<WorkflowSuggestionTool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LanguageModelTool for WorkflowStepResolution {
|
impl LanguageModelTool for WorkflowStepResolutionTool {
|
||||||
fn name() -> String {
|
fn name() -> String {
|
||||||
"edit".into()
|
"edit".into()
|
||||||
}
|
}
|
||||||
|
@ -429,16 +476,17 @@ pub 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 WorkflowSuggestion {
|
pub struct WorkflowSuggestionTool {
|
||||||
/// 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: WorkflowSuggestionKind,
|
pub kind: WorkflowSuggestionToolKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WorkflowSuggestion {
|
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)> {
|
||||||
|
@ -475,9 +523,8 @@ 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 suggestion;
|
let kind = match kind {
|
||||||
match kind {
|
WorkflowSuggestionToolKind::Update {
|
||||||
WorkflowSuggestionKind::Update {
|
|
||||||
symbol,
|
symbol,
|
||||||
description,
|
description,
|
||||||
} => {
|
} => {
|
||||||
|
@ -494,12 +541,12 @@ 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);
|
||||||
suggestion = super::WorkflowSuggestion::Update { range, description };
|
WorkflowSuggestionKind::Update { range, description }
|
||||||
}
|
}
|
||||||
WorkflowSuggestionKind::Create { description } => {
|
WorkflowSuggestionToolKind::Create { description } => {
|
||||||
suggestion = super::WorkflowSuggestion::CreateFile { description };
|
WorkflowSuggestionKind::CreateFile { description }
|
||||||
}
|
}
|
||||||
WorkflowSuggestionKind::InsertSiblingBefore {
|
WorkflowSuggestionToolKind::InsertSiblingBefore {
|
||||||
symbol,
|
symbol,
|
||||||
description,
|
description,
|
||||||
} => {
|
} => {
|
||||||
|
@ -514,12 +561,12 @@ pub mod tool {
|
||||||
annotation_range.start
|
annotation_range.start
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
suggestion = super::WorkflowSuggestion::InsertSiblingBefore {
|
WorkflowSuggestionKind::InsertSiblingBefore {
|
||||||
position,
|
position,
|
||||||
description,
|
description,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
WorkflowSuggestionKind::InsertSiblingAfter {
|
WorkflowSuggestionToolKind::InsertSiblingAfter {
|
||||||
symbol,
|
symbol,
|
||||||
description,
|
description,
|
||||||
} => {
|
} => {
|
||||||
|
@ -528,12 +575,12 @@ pub 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::WorkflowSuggestion::InsertSiblingAfter {
|
WorkflowSuggestionKind::InsertSiblingAfter {
|
||||||
position,
|
position,
|
||||||
description,
|
description,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
WorkflowSuggestionKind::PrependChild {
|
WorkflowSuggestionToolKind::PrependChild {
|
||||||
symbol,
|
symbol,
|
||||||
description,
|
description,
|
||||||
} => {
|
} => {
|
||||||
|
@ -548,18 +595,18 @@ 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),
|
||||||
);
|
);
|
||||||
suggestion = super::WorkflowSuggestion::PrependChild {
|
WorkflowSuggestionKind::PrependChild {
|
||||||
position,
|
position,
|
||||||
description,
|
description,
|
||||||
};
|
}
|
||||||
} else {
|
} else {
|
||||||
suggestion = super::WorkflowSuggestion::PrependChild {
|
WorkflowSuggestionKind::PrependChild {
|
||||||
position: language::Anchor::MIN,
|
position: language::Anchor::MIN,
|
||||||
description,
|
description,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WorkflowSuggestionKind::AppendChild {
|
WorkflowSuggestionToolKind::AppendChild {
|
||||||
symbol,
|
symbol,
|
||||||
description,
|
description,
|
||||||
} => {
|
} => {
|
||||||
|
@ -574,18 +621,18 @@ 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),
|
||||||
);
|
);
|
||||||
suggestion = super::WorkflowSuggestion::AppendChild {
|
WorkflowSuggestionKind::AppendChild {
|
||||||
position,
|
position,
|
||||||
description,
|
description,
|
||||||
};
|
}
|
||||||
} else {
|
} else {
|
||||||
suggestion = super::WorkflowSuggestion::PrependChild {
|
WorkflowSuggestionKind::PrependChild {
|
||||||
position: language::Anchor::MAX,
|
position: language::Anchor::MAX,
|
||||||
description,
|
description,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WorkflowSuggestionKind::Delete { symbol } => {
|
WorkflowSuggestionToolKind::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))?
|
||||||
|
@ -599,9 +646,15 @@ 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);
|
||||||
suggestion = super::WorkflowSuggestion::Delete { range };
|
WorkflowSuggestionKind::Delete { range }
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
let suggestion = WorkflowSuggestion {
|
||||||
|
kind,
|
||||||
|
tool_output: self.clone(),
|
||||||
|
tool_input,
|
||||||
|
};
|
||||||
|
|
||||||
Ok((buffer, suggestion))
|
Ok((buffer, suggestion))
|
||||||
}
|
}
|
||||||
|
@ -609,7 +662,7 @@ pub 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 WorkflowSuggestionKind {
|
pub enum WorkflowSuggestionToolKind {
|
||||||
/// 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 {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue