Compare commits
3 Commits
rework-inl
...
workflow-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9404251dd0 | ||
|
|
289ba8fb34 | ||
|
|
ba7e894a6e |
@@ -1,12 +1,11 @@
|
||||
<workflow>
|
||||
Guide the user through code changes in numbered steps that focus on individual functions, type definitions, etc.
|
||||
Guide the user through code changes in numbered steps where each step focuses on a single individual function, type definition, etc.
|
||||
Surround each distinct step in a <step></step> XML tag. The user will be performing these steps in a code editor
|
||||
named Zed, which is where they will have entered this prompt and will be seeing the response.
|
||||
|
||||
<instructions>
|
||||
- Use the language of the file for code fence blocks unless otherwise specified.
|
||||
- Include a code or file action in each step.
|
||||
- Only put code in separate steps if it should either go in separate files, or in different (non-contiguous) places in the same file.
|
||||
- Provide error handling and input validation where appropriate.
|
||||
- Adapt explanations based on the user's perceived level of expertise.
|
||||
- Include comments in code examples to enhance understanding.
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
You are an assistant in a code editor, helping a developer to edit their code.
|
||||
|
||||
<task>
|
||||
Output {{ content_type }} to insert into the given `<document>` in accordance with the given `<prompt>` tag.
|
||||
Generate output to be inserted prior to the special ⎀ character.
|
||||
</task>
|
||||
|
||||
<rule>
|
||||
You are only to insert NEW {{{ content_type }}} based on the specified `<prompt>` tag.
|
||||
DO NOT repeat any content that surrounds the ⎀ character
|
||||
DO NOT repeat the ⎀ character itself.
|
||||
|
||||
<rule_example>
|
||||
<example_input>
|
||||
<document language="Rust">
|
||||
impl Foo {
|
||||
⎀
|
||||
fn bar() {}
|
||||
}
|
||||
</document>
|
||||
<prompt>Document method</prompt>
|
||||
</example_input>
|
||||
<bad_output failure="Not a pure insert. Repeated content surrounding ⎀">
|
||||
// Example method
|
||||
// This is an example of a doc commment
|
||||
// It could be multiple lines
|
||||
fn bar() {}
|
||||
</bad_output>
|
||||
<bad_output failure="Repeated ⎀ in output">
|
||||
// Example method
|
||||
// This is an example of a doc commment
|
||||
// It could be multiple lines⎀
|
||||
</bad_output>
|
||||
<good_output success="Generated ONLY new code based on the prompt tag">
|
||||
// Example method
|
||||
// This is an example of a doc commment
|
||||
// It could be multiple lines
|
||||
</good_output>
|
||||
</rule_example>
|
||||
</rule>
|
||||
|
||||
{{#if truncated}}
|
||||
<document language="{{{ language }}}" truncated="true">
|
||||
{{else}}
|
||||
<document language="{{{ language }}}">
|
||||
{{/if}}
|
||||
{{{ document_prefix }}}⎀{{{ document_suffix }}}
|
||||
</document>
|
||||
|
||||
{{#if truncated}}
|
||||
<note>
|
||||
The contents of the document tag above have been truncated for brevity.
|
||||
</note>
|
||||
{{/if}}
|
||||
|
||||
<instruction>
|
||||
Focus on inserting {{{ content_type }}} at the location prior to the ⎀ character based on the `<prompt>` below.
|
||||
</instruction>
|
||||
|
||||
Here's the excerpt from the document where you will perform the edit.
|
||||
|
||||
<document_excerpt>
|
||||
{{{ context_prefix }}}⎀{{{ context_suffix }}}
|
||||
</document_excerpt>
|
||||
|
||||
<prompt>
|
||||
{{{ prompt }}}
|
||||
</prompt>
|
||||
|
||||
<rules>
|
||||
- Your first token should be {{{ content_type }}} written in {{{ language }}}.
|
||||
- DO NOT repeat existing {{{ content_type }}}.
|
||||
- DO NOT output the ⎀ character.
|
||||
- DO NOT add extra indentation to the generated content.
|
||||
</rules>
|
||||
|
||||
<output_format>
|
||||
Output pure {{{ language }}} with no commentary.
|
||||
</output_format>
|
||||
@@ -1,73 +0,0 @@
|
||||
You are an assistant in a code editor, helping a developer to edit their code.
|
||||
<task>
|
||||
Output code according to the given `<prompt>` tag, which should be inserted into the given `<document>` tag replacing the contents of the `<replaced_text>` tag.
|
||||
</task>
|
||||
|
||||
<instructions>
|
||||
* Maintain the original indentation level of the file in rewritten sections.
|
||||
</instructions>
|
||||
|
||||
<document language="Python">
|
||||
import pygame
|
||||
import random
|
||||
|
||||
# Initialize Pygame
|
||||
pygame.init()
|
||||
|
||||
# Set up the game window
|
||||
width = 800
|
||||
height = 600
|
||||
window = pygame.display.set_mode((width, height))
|
||||
pygame.display.set_caption("Snake Game")
|
||||
|
||||
# Colors
|
||||
BLACK = (0, 0, 0)
|
||||
WHITE = (255, 255, 255)
|
||||
RED = (255, 0, 0)
|
||||
GREEN = (0, 255, 0)
|
||||
|
||||
# Snake properties
|
||||
snake_block = 20
|
||||
snake_speed = 15
|
||||
|
||||
# Initialize the snake
|
||||
snake = [(width // 2, height // 2)]
|
||||
snake_direction = (0, -snake_block)
|
||||
|
||||
# Initialize the food
|
||||
food = (random.randrange(0, width - snake_block, snake_block),
|
||||
random.randrange(0, height - snake_block, snake_block))
|
||||
|
||||
# Set up the game clock
|
||||
clock = pygame.time.Clock()
|
||||
|
||||
# Game loop
|
||||
running = True
|
||||
while running:
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
running = False
|
||||
elif event.type == pygame.KEYDOWN:
|
||||
if <replaced_text>event.key == pygame.K_UP and snake_direction != (0, snake_block):
|
||||
snake_direction = (0, -snake_block)
|
||||
elif event.key == pygame.K_DOWN and snake_direction != (0, -snake_block):</replaced_text>
|
||||
snake_direction = (0, snake_block)
|
||||
elif event.key == pygame.K_LEFT and snake_direction != (snake_block, 0):
|
||||
snake_direction = (-snake_block, 0)
|
||||
elif event.key == pygame.K_RIGHT and snake_direction != (-snake_block, 0):
|
||||
snake_direction = (snake_block, 0)</replaced_text>
|
||||
|
||||
# Move the snake
|
||||
new_head = (snake[0][0] + snake_direction[0], snake[0][1] +
|
||||
</document>
|
||||
|
||||
<prompt>
|
||||
Apply demorgans
|
||||
</prompt>
|
||||
|
||||
<directive>
|
||||
Output code immediately. No commentary. Your first token should be in Python.
|
||||
</directive>
|
||||
event.key == pygame.K_UP and not snake_direction == (0, snake_block):
|
||||
snake_direction = (0, -snake_block)
|
||||
elif event.key == pygame.K_DOWN and not snake_direction == (0, -snake_block):
|
||||
@@ -38,8 +38,6 @@ pub enum Model {
|
||||
Custom {
|
||||
name: String,
|
||||
max_tokens: usize,
|
||||
/// The name displayed in the UI, such as in the assistant panel model dropdown menu.
|
||||
display_name: Option<String>,
|
||||
/// Override this model with a different Anthropic model for tool calls.
|
||||
tool_override: Option<String>,
|
||||
/// Indicates whether this custom model supports caching.
|
||||
@@ -79,9 +77,7 @@ impl Model {
|
||||
Self::Claude3Opus => "Claude 3 Opus",
|
||||
Self::Claude3Sonnet => "Claude 3 Sonnet",
|
||||
Self::Claude3Haiku => "Claude 3 Haiku",
|
||||
Self::Custom {
|
||||
name, display_name, ..
|
||||
} => display_name.as_ref().unwrap_or(name),
|
||||
Self::Custom { name, .. } => name,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3506,8 +3506,7 @@ impl ContextEditor {
|
||||
};
|
||||
Some(
|
||||
h_flex()
|
||||
.px_3()
|
||||
.py_2()
|
||||
.p_3()
|
||||
.border_b_1()
|
||||
.border_color(cx.theme().colors().border_variant)
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
@@ -3656,8 +3655,8 @@ impl Render for ContextEditor {
|
||||
this.child(
|
||||
div()
|
||||
.absolute()
|
||||
.right_3()
|
||||
.bottom_12()
|
||||
.right_4()
|
||||
.bottom_10()
|
||||
.max_w_96()
|
||||
.py_2()
|
||||
.px_3()
|
||||
@@ -3671,8 +3670,8 @@ impl Render for ContextEditor {
|
||||
this.child(
|
||||
div()
|
||||
.absolute()
|
||||
.right_3()
|
||||
.bottom_12()
|
||||
.right_4()
|
||||
.bottom_10()
|
||||
.max_w_96()
|
||||
.py_2()
|
||||
.px_3()
|
||||
@@ -3683,12 +3682,12 @@ impl Render for ContextEditor {
|
||||
.gap_0p5()
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1p5()
|
||||
.gap_1()
|
||||
.items_center()
|
||||
.child(Icon::new(IconName::XCircle).color(Color::Error))
|
||||
.child(
|
||||
Label::new("Error interacting with language model")
|
||||
.weight(FontWeight::MEDIUM),
|
||||
.weight(FontWeight::SEMIBOLD),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
|
||||
@@ -28,7 +28,7 @@ use gpui::{
|
||||
FontWeight, Global, HighlightStyle, Model, ModelContext, Subscription, Task, TextStyle,
|
||||
UpdateGlobal, View, ViewContext, WeakView, WindowContext,
|
||||
};
|
||||
use language::{Buffer, Point, TransactionId};
|
||||
use language::{Buffer, IndentKind, Point, TransactionId};
|
||||
use language_model::{
|
||||
LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage, Role,
|
||||
};
|
||||
@@ -46,6 +46,7 @@ use std::{
|
||||
task::{self, Poll},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use text::OffsetRangeExt as _;
|
||||
use theme::ThemeSettings;
|
||||
use ui::{prelude::*, CheckboxWithLabel, IconButtonShape, Popover, Tooltip};
|
||||
use util::{RangeExt, ResultExt};
|
||||
@@ -72,7 +73,6 @@ const PROMPT_HISTORY_MAX_LEN: usize = 20;
|
||||
pub struct InlineAssistant {
|
||||
next_assist_id: InlineAssistId,
|
||||
next_assist_group_id: InlineAssistGroupId,
|
||||
|
||||
assists: HashMap<InlineAssistId, InlineAssist>,
|
||||
assists_by_editor: HashMap<WeakView<Editor>, EditorInlineAssists>,
|
||||
assist_groups: HashMap<InlineAssistGroupId, InlineAssistGroup>,
|
||||
@@ -154,51 +154,40 @@ impl InlineAssistant {
|
||||
.map(|range| range.to_point(&snapshot))
|
||||
.collect::<Vec<Range<Point>>>();
|
||||
|
||||
if selection_ranges.len() == 1 {
|
||||
let selection_range = &selection_ranges[0];
|
||||
codegen_ranges.push(CodegenRange {
|
||||
transform_range: selection_range.clone(),
|
||||
selection_ranges: vec![selection_range.clone()],
|
||||
focus_assist: true,
|
||||
});
|
||||
} else {
|
||||
for selection_range in selection_ranges {
|
||||
let selection_is_newest =
|
||||
newest_selection_range.contains_inclusive(&selection_range);
|
||||
let mut transform_range = selection_range.start..selection_range.end;
|
||||
for selection_range in selection_ranges {
|
||||
let selection_is_newest = newest_selection_range.contains_inclusive(&selection_range);
|
||||
let mut transform_range = selection_range.start..selection_range.end;
|
||||
|
||||
// Expand the transform range to start/end of lines.
|
||||
// If a non-empty selection ends at the start of the last line, clip at the end of the penultimate line.
|
||||
transform_range.start.column = 0;
|
||||
if transform_range.end.column == 0 && transform_range.end > transform_range.start {
|
||||
transform_range.end.row -= 1;
|
||||
}
|
||||
transform_range.end.column =
|
||||
snapshot.line_len(MultiBufferRow(transform_range.end.row));
|
||||
let selection_range =
|
||||
selection_range.start..selection_range.end.min(transform_range.end);
|
||||
|
||||
// If we intersect the previous transform range,
|
||||
if let Some(CodegenRange {
|
||||
transform_range: prev_transform_range,
|
||||
selection_ranges,
|
||||
focus_assist,
|
||||
}) = codegen_ranges.last_mut()
|
||||
{
|
||||
if transform_range.start <= prev_transform_range.end {
|
||||
prev_transform_range.end = transform_range.end;
|
||||
selection_ranges.push(selection_range);
|
||||
*focus_assist |= selection_is_newest;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
codegen_ranges.push(CodegenRange {
|
||||
transform_range,
|
||||
selection_ranges: vec![selection_range],
|
||||
focus_assist: selection_is_newest,
|
||||
})
|
||||
// Expand the transform range to start/end of lines.
|
||||
// If a non-empty selection ends at the start of the last line, clip at the end of the penultimate line.
|
||||
transform_range.start.column = 0;
|
||||
if transform_range.end.column == 0 && transform_range.end > transform_range.start {
|
||||
transform_range.end.row -= 1;
|
||||
}
|
||||
transform_range.end.column = snapshot.line_len(MultiBufferRow(transform_range.end.row));
|
||||
let selection_range =
|
||||
selection_range.start..selection_range.end.min(transform_range.end);
|
||||
|
||||
// If we intersect the previous transform range,
|
||||
if let Some(CodegenRange {
|
||||
transform_range: prev_transform_range,
|
||||
selection_ranges,
|
||||
focus_assist,
|
||||
}) = codegen_ranges.last_mut()
|
||||
{
|
||||
if transform_range.start <= prev_transform_range.end {
|
||||
prev_transform_range.end = transform_range.end;
|
||||
selection_ranges.push(selection_range);
|
||||
*focus_assist |= selection_is_newest;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
codegen_ranges.push(CodegenRange {
|
||||
transform_range,
|
||||
selection_ranges: vec![selection_range],
|
||||
focus_assist: selection_is_newest,
|
||||
})
|
||||
}
|
||||
|
||||
let assist_group_id = self.next_assist_group_id.post_inc();
|
||||
@@ -1089,10 +1078,10 @@ impl InlineAssistant {
|
||||
let mut new_blocks = Vec::new();
|
||||
for (new_row, old_row_range) in deleted_row_ranges {
|
||||
let (_, buffer_start) = old_snapshot
|
||||
.position_to_buffer_offset(Point::new(*old_row_range.start(), 0))
|
||||
.point_to_buffer_offset(Point::new(*old_row_range.start(), 0))
|
||||
.unwrap();
|
||||
let (_, buffer_end) = old_snapshot
|
||||
.position_to_buffer_offset(Point::new(
|
||||
.point_to_buffer_offset(Point::new(
|
||||
*old_row_range.end(),
|
||||
old_snapshot.line_len(MultiBufferRow(*old_row_range.end())),
|
||||
))
|
||||
@@ -2314,12 +2303,6 @@ impl Codegen {
|
||||
} else {
|
||||
let request = self.build_request(user_prompt, assistant_panel_context, cx)?;
|
||||
|
||||
// todo!
|
||||
// println!(
|
||||
// "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n{}",
|
||||
// request
|
||||
// );
|
||||
|
||||
let chunks =
|
||||
cx.spawn(|_, cx| async move { model.stream_completion(request, &cx).await });
|
||||
async move { Ok(chunks.await?.boxed()) }.boxed_local()
|
||||
@@ -2334,8 +2317,8 @@ impl Codegen {
|
||||
assistant_panel_context: Option<LanguageModelRequest>,
|
||||
cx: &AppContext,
|
||||
) -> Result<LanguageModelRequest> {
|
||||
let multi_buffer = self.buffer.read(cx).snapshot(cx);
|
||||
let language = multi_buffer.language_at(self.transform_range.start);
|
||||
let buffer = self.buffer.read(cx).snapshot(cx);
|
||||
let language = buffer.language_at(self.transform_range.start);
|
||||
let language_name = if let Some(language) = language.as_ref() {
|
||||
if Arc::ptr_eq(language, &language::PLAIN_TEXT) {
|
||||
None
|
||||
@@ -2360,55 +2343,54 @@ impl Codegen {
|
||||
};
|
||||
|
||||
let language_name = language_name.as_deref();
|
||||
let start = multi_buffer.position_to_buffer_offset(self.transform_range.start);
|
||||
let end = multi_buffer.position_to_buffer_offset(self.transform_range.end);
|
||||
let buffer = start
|
||||
.zip(end)
|
||||
.and_then(|((start_buffer, _), (end_buffer, _))| {
|
||||
if start_buffer.remote_id() == end_buffer.remote_id() {
|
||||
Some(start_buffer.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.ok_or_else(|| anyhow::anyhow!("invalid transformation range"))?;
|
||||
let start = buffer.point_to_buffer_offset(self.transform_range.start);
|
||||
let end = buffer.point_to_buffer_offset(self.transform_range.end);
|
||||
let (transform_buffer, transform_range) = if let Some((start, end)) = start.zip(end) {
|
||||
let (start_buffer, start_buffer_offset) = start;
|
||||
let (end_buffer, end_buffer_offset) = end;
|
||||
if start_buffer.remote_id() == end_buffer.remote_id() {
|
||||
(start_buffer.clone(), start_buffer_offset..end_buffer_offset)
|
||||
} else {
|
||||
return Err(anyhow::anyhow!("invalid transformation range"));
|
||||
}
|
||||
} else {
|
||||
return Err(anyhow::anyhow!("invalid transformation range"));
|
||||
};
|
||||
|
||||
let mut transform_context_range = transform_range.to_point(&transform_buffer);
|
||||
transform_context_range.start.row = transform_context_range.start.row.saturating_sub(3);
|
||||
transform_context_range.start.column = 0;
|
||||
transform_context_range.end =
|
||||
(transform_context_range.end + Point::new(3, 0)).min(transform_buffer.max_point());
|
||||
transform_context_range.end.column =
|
||||
transform_buffer.line_len(transform_context_range.end.row);
|
||||
let transform_context_range = transform_context_range.to_offset(&transform_buffer);
|
||||
|
||||
let selected_ranges = self
|
||||
.selected_ranges
|
||||
.iter()
|
||||
.filter_map(|selected_range| {
|
||||
let start = multi_buffer
|
||||
.position_to_buffer_point(selected_range.start)
|
||||
let start = buffer
|
||||
.point_to_buffer_offset(selected_range.start)
|
||||
.map(|(_, offset)| offset)?;
|
||||
let end = multi_buffer
|
||||
.position_to_buffer_point(selected_range.end)
|
||||
let end = buffer
|
||||
.point_to_buffer_offset(selected_range.end)
|
||||
.map(|(_, offset)| offset)?;
|
||||
|
||||
Some(start..end)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let inline_assist_prompt = if selected_ranges.len() == 1 {
|
||||
let selected_range = &selected_ranges[0];
|
||||
if selected_range.start == selected_range.end {
|
||||
self.prompt_builder.build_insertion_prompt(
|
||||
user_prompt,
|
||||
language_name,
|
||||
buffer,
|
||||
selected_range.start,
|
||||
)?
|
||||
} else {
|
||||
// Single non-empty range
|
||||
// Implement logic for replacement
|
||||
// TODO: Add replacement logic here
|
||||
anyhow::bail!("Unsupported transformation!")
|
||||
}
|
||||
} else {
|
||||
// Multiple ranges
|
||||
// Implement logic for handling multiple ranges
|
||||
// TODO: Add multiple range handling logic here
|
||||
anyhow::bail!("Unsupported transformation!")
|
||||
};
|
||||
let prompt = self
|
||||
.prompt_builder
|
||||
.generate_content_prompt(
|
||||
user_prompt,
|
||||
language_name,
|
||||
transform_buffer,
|
||||
transform_range,
|
||||
selected_ranges,
|
||||
transform_context_range,
|
||||
)
|
||||
.map_err(|e| anyhow::anyhow!("Failed to generate content prompt: {}", e))?;
|
||||
|
||||
let mut messages = Vec::new();
|
||||
if let Some(context_request) = assistant_panel_context {
|
||||
@@ -2417,7 +2399,7 @@ impl Codegen {
|
||||
|
||||
messages.push(LanguageModelRequestMessage {
|
||||
role: Role::User,
|
||||
content: vec![inline_assist_prompt.into()],
|
||||
content: vec![prompt.into()],
|
||||
cache: false,
|
||||
});
|
||||
|
||||
@@ -2441,15 +2423,25 @@ impl Codegen {
|
||||
.collect::<Rope>();
|
||||
|
||||
let selection_start = edit_range.start.to_point(&snapshot);
|
||||
let base_indent_size = snapshot.indent_size_for_line(MultiBufferRow(selection_start.row));
|
||||
let indent_len_before_selection = base_indent_size.len.min(selection_start.column);
|
||||
let base_indent = match base_indent_size.kind {
|
||||
language::IndentKind::Space => " ".repeat(indent_len_before_selection as usize),
|
||||
language::IndentKind::Tab => "\t".repeat(indent_len_before_selection as usize),
|
||||
};
|
||||
|
||||
let mut generated_text = String::new();
|
||||
let mut raw_output = String::new();
|
||||
// Start with the indentation of the first line in the selection
|
||||
let mut suggested_line_indent = snapshot
|
||||
.suggested_indents(selection_start.row..=selection_start.row, cx)
|
||||
.into_values()
|
||||
.next()
|
||||
.unwrap_or_else(|| snapshot.indent_size_for_line(MultiBufferRow(selection_start.row)));
|
||||
|
||||
// If the first line in the selection does not have indentation, check the following lines
|
||||
if suggested_line_indent.len == 0 && suggested_line_indent.kind == IndentKind::Space {
|
||||
for row in selection_start.row..=edit_range.end.to_point(&snapshot).row {
|
||||
let line_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
|
||||
// Prefer tabs if a line in the selection uses tabs as indentation
|
||||
if line_indent.kind == IndentKind::Tab {
|
||||
suggested_line_indent.kind = IndentKind::Tab;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let telemetry = self.telemetry.clone();
|
||||
self.diff = Diff::default();
|
||||
@@ -2475,17 +2467,7 @@ impl Codegen {
|
||||
response_latency = Some(request_start.elapsed());
|
||||
}
|
||||
let chunk = chunk?;
|
||||
raw_output.push_str(&chunk);
|
||||
|
||||
let start_offset = generated_text.len();
|
||||
for line in chunk.split_inclusive('\n') {
|
||||
if generated_text.ends_with('\n') {
|
||||
generated_text.push_str(&base_indent);
|
||||
}
|
||||
generated_text.push_str(line);
|
||||
}
|
||||
|
||||
let char_ops = diff.push_new(&generated_text[start_offset..]);
|
||||
let char_ops = diff.push_new(&chunk);
|
||||
line_diff.push_char_operations(&char_ops, &selected_text);
|
||||
diff_tx
|
||||
.send((char_ops, line_diff.line_operations()))
|
||||
@@ -2503,10 +2485,6 @@ impl Codegen {
|
||||
};
|
||||
|
||||
let result = diff.await;
|
||||
// todo!
|
||||
// println!("Base indent: {:?}", base_indent_size);
|
||||
// println!("Raw output: {:?}", raw_output);
|
||||
// println!("Generated text: {:?}", generated_text);
|
||||
|
||||
let error_message =
|
||||
result.as_ref().err().map(|error| error.to_string());
|
||||
|
||||
@@ -4,23 +4,10 @@ use futures::StreamExt;
|
||||
use handlebars::{Handlebars, RenderError, TemplateError};
|
||||
use language::BufferSnapshot;
|
||||
use parking_lot::Mutex;
|
||||
use rope::Point;
|
||||
use serde::Serialize;
|
||||
use std::{cmp, ops::Range, sync::Arc, time::Duration};
|
||||
use std::{ops::Range, sync::Arc, time::Duration};
|
||||
use util::ResultExt;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct InsertionContext {
|
||||
pub document_prefix: String,
|
||||
pub document_suffix: String,
|
||||
pub context_prefix: String,
|
||||
pub context_suffix: String,
|
||||
pub prompt: String,
|
||||
pub language: String,
|
||||
pub content_type: String,
|
||||
pub truncated: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct ContentPromptContext {
|
||||
pub content_type: String,
|
||||
@@ -173,7 +160,6 @@ impl PromptBuilder {
|
||||
.map_err(Box::new)
|
||||
};
|
||||
|
||||
register_template("insertion")?;
|
||||
register_template("content_prompt")?;
|
||||
register_template("terminal_assistant_prompt")?;
|
||||
register_template("edit_workflow")?;
|
||||
@@ -182,59 +168,6 @@ impl PromptBuilder {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn build_insertion_prompt(
|
||||
&self,
|
||||
user_prompt: String,
|
||||
language_name: Option<&str>,
|
||||
buffer: BufferSnapshot,
|
||||
position: Point,
|
||||
) -> Result<String, RenderError> {
|
||||
let mut document_prefix = String::new();
|
||||
for chunk in buffer.text_for_range(Point::zero()..position) {
|
||||
document_prefix.push_str(chunk);
|
||||
}
|
||||
|
||||
let mut document_suffix = String::new();
|
||||
for chunk in buffer.text_for_range(position..buffer.max_point()) {
|
||||
document_suffix.push_str(chunk);
|
||||
}
|
||||
|
||||
// Get 5 lines of context above and below the insertion
|
||||
let mut context_range = position..position;
|
||||
context_range.start.row = context_range.start.row.saturating_sub(5);
|
||||
context_range.start.column = 0;
|
||||
context_range.end = cmp::min(context_range.end + Point::new(5, 0), buffer.max_point());
|
||||
context_range.end.column = buffer.line_len(context_range.end.row);
|
||||
let mut context_prefix = String::new();
|
||||
let mut context_suffix = String::new();
|
||||
for chunk in buffer.text_for_range(context_range.start..position) {
|
||||
context_prefix.push_str(chunk);
|
||||
}
|
||||
for chunk in buffer.text_for_range(position..context_range.end) {
|
||||
context_suffix.push_str(chunk);
|
||||
}
|
||||
|
||||
let language = language_name.unwrap_or("Unknown").to_string();
|
||||
let content_type = match language_name {
|
||||
None | Some("Markdown" | "Plain Text") => "text".to_string(),
|
||||
Some(_) => "code".to_string(),
|
||||
};
|
||||
let truncated = false; // Assuming no truncation for now
|
||||
|
||||
let context = InsertionContext {
|
||||
document_prefix,
|
||||
document_suffix,
|
||||
context_prefix,
|
||||
context_suffix,
|
||||
prompt: user_prompt,
|
||||
language,
|
||||
content_type,
|
||||
truncated,
|
||||
};
|
||||
|
||||
self.handlebars.lock().render("insertion", &context)
|
||||
}
|
||||
|
||||
pub fn generate_content_prompt(
|
||||
&self,
|
||||
user_prompt: String,
|
||||
|
||||
@@ -152,25 +152,6 @@ pub struct IndentSize {
|
||||
pub kind: IndentKind,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for IndentSize {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self.kind {
|
||||
IndentKind::Space => {
|
||||
for _ in 0..self.len {
|
||||
write!(f, " ")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
IndentKind::Tab => {
|
||||
for _ in 0..self.len {
|
||||
write!(f, "\t")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A whitespace character that's used for indentation.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
|
||||
pub enum IndentKind {
|
||||
|
||||
@@ -88,7 +88,12 @@ impl<T> Outline<T> {
|
||||
}
|
||||
|
||||
/// Find the most similar symbol to the provided query using normalized Levenshtein distance.
|
||||
pub fn find_most_similar(&self, query: &str) -> Option<(SymbolPath, &OutlineItem<T>)> {
|
||||
pub fn find_most_similar(&self, symbol: &str) -> Option<(SymbolPath, &OutlineItem<T>)> {
|
||||
// Sometimes the model incorrectly includes a space or colon in the query,
|
||||
// e.g. in Elm, Roc, etc. it might say `foo : ...` because it's trying to include the symbol's type,
|
||||
// which isn't part of the symbol and won't be in the outline. Trim the first space and anything after it.
|
||||
let symbol = &symbol[..symbol.find(' ').unwrap_or(symbol.len())];
|
||||
|
||||
const SIMILARITY_THRESHOLD: f64 = 0.6;
|
||||
|
||||
let (position, similarity) = self
|
||||
@@ -96,7 +101,7 @@ impl<T> Outline<T> {
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, candidate)| {
|
||||
let similarity = strsim::normalized_levenshtein(&candidate.string, query);
|
||||
let similarity = strsim::normalized_levenshtein(&candidate.string, symbol);
|
||||
(index, similarity)
|
||||
})
|
||||
.max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap())?;
|
||||
|
||||
@@ -29,22 +29,15 @@ const PROVIDER_NAME: &str = "Anthropic";
|
||||
pub struct AnthropicSettings {
|
||||
pub api_url: String,
|
||||
pub low_speed_timeout: Option<Duration>,
|
||||
/// Extend Zed's list of Anthropic models.
|
||||
pub available_models: Vec<AvailableModel>,
|
||||
pub needs_setting_migration: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct AvailableModel {
|
||||
/// The model's name in the Anthropic API. e.g. claude-3-5-sonnet-20240620
|
||||
pub name: String,
|
||||
/// The model's name in Zed's UI, such as in the model selector dropdown menu in the assistant panel.
|
||||
pub display_name: Option<String>,
|
||||
/// The model's context window size.
|
||||
pub max_tokens: usize,
|
||||
/// A model `name` to substitute when calling tools, in case the primary model doesn't support tool calling.
|
||||
pub tool_override: Option<String>,
|
||||
/// Configuration of Anthropic's caching API.
|
||||
pub cache_configuration: Option<LanguageModelCacheConfiguration>,
|
||||
pub max_output_tokens: Option<u32>,
|
||||
}
|
||||
@@ -178,7 +171,6 @@ impl LanguageModelProvider for AnthropicLanguageModelProvider {
|
||||
model.name.clone(),
|
||||
anthropic::Model::Custom {
|
||||
name: model.name.clone(),
|
||||
display_name: model.display_name.clone(),
|
||||
max_tokens: model.max_tokens,
|
||||
tool_override: model.tool_override.clone(),
|
||||
cache_configuration: model.cache_configuration.as_ref().map(|config| {
|
||||
|
||||
@@ -52,20 +52,12 @@ pub enum AvailableProvider {
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct AvailableModel {
|
||||
/// The provider of the language model.
|
||||
pub provider: AvailableProvider,
|
||||
/// The model's name in the provider's API. e.g. claude-3-5-sonnet-20240620
|
||||
pub name: String,
|
||||
/// The name displayed in the UI, such as in the assistant panel model dropdown menu.
|
||||
pub display_name: Option<String>,
|
||||
/// The size of the context window, indicating the maximum number of tokens the model can process.
|
||||
pub max_tokens: usize,
|
||||
/// The maximum number of output tokens allowed by the model.
|
||||
pub max_output_tokens: Option<u32>,
|
||||
/// Override this model with a different Anthropic model for tool calls.
|
||||
pub tool_override: Option<String>,
|
||||
/// Indicates whether this custom model supports caching.
|
||||
pub cache_configuration: Option<LanguageModelCacheConfiguration>,
|
||||
provider: AvailableProvider,
|
||||
name: String,
|
||||
max_tokens: usize,
|
||||
tool_override: Option<String>,
|
||||
cache_configuration: Option<LanguageModelCacheConfiguration>,
|
||||
max_output_tokens: Option<u32>,
|
||||
}
|
||||
|
||||
pub struct CloudLanguageModelProvider {
|
||||
@@ -210,7 +202,6 @@ impl LanguageModelProvider for CloudLanguageModelProvider {
|
||||
AvailableProvider::Anthropic => {
|
||||
CloudModel::Anthropic(anthropic::Model::Custom {
|
||||
name: model.name.clone(),
|
||||
display_name: model.display_name.clone(),
|
||||
max_tokens: model.max_tokens,
|
||||
tool_override: model.tool_override.clone(),
|
||||
cache_configuration: model.cache_configuration.as_ref().map(|config| {
|
||||
|
||||
@@ -196,24 +196,6 @@ pub struct LanguageModelRequestMessage {
|
||||
pub cache: bool,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for LanguageModelRequestMessage {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Role: {}\n", self.role)?;
|
||||
write!(f, "Content:\n")?;
|
||||
for (index, content) in self.content.iter().enumerate() {
|
||||
match content {
|
||||
MessageContent::Text(text) => write!(f, " {}: Text ({})\n", index, text)?,
|
||||
MessageContent::Image(image) => write!(
|
||||
f,
|
||||
" {}: Image ({}x{})\n",
|
||||
index, image.size.width.0, image.size.height.0
|
||||
)?,
|
||||
}
|
||||
}
|
||||
write!(f, "Cache: {}", self.cache)
|
||||
}
|
||||
}
|
||||
|
||||
impl LanguageModelRequestMessage {
|
||||
pub fn string_contents(&self) -> String {
|
||||
let mut string_buffer = String::new();
|
||||
@@ -246,20 +228,6 @@ pub struct LanguageModelRequest {
|
||||
pub temperature: f32,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for LanguageModelRequest {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(f, "LanguageModelRequest {{")?;
|
||||
writeln!(f, " messages: [")?;
|
||||
for message in &self.messages {
|
||||
writeln!(f, " {}", message)?;
|
||||
}
|
||||
writeln!(f, " ],")?;
|
||||
writeln!(f, " stop: {:?},", self.stop)?;
|
||||
writeln!(f, " temperature: {}", self.temperature)?;
|
||||
write!(f, "}}")
|
||||
}
|
||||
}
|
||||
|
||||
impl LanguageModelRequest {
|
||||
pub fn into_open_ai(self, model: String) -> open_ai::Request {
|
||||
open_ai::Request {
|
||||
|
||||
@@ -94,14 +94,12 @@ impl AnthropicSettingsContent {
|
||||
.filter_map(|model| match model {
|
||||
anthropic::Model::Custom {
|
||||
name,
|
||||
display_name,
|
||||
max_tokens,
|
||||
tool_override,
|
||||
cache_configuration,
|
||||
max_output_tokens,
|
||||
} => Some(provider::anthropic::AvailableModel {
|
||||
name,
|
||||
display_name,
|
||||
max_tokens,
|
||||
tool_override,
|
||||
cache_configuration: cache_configuration.as_ref().map(
|
||||
|
||||
@@ -2650,11 +2650,11 @@ impl MultiBufferSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn position_to_buffer_offset<T: ToOffset>(
|
||||
pub fn point_to_buffer_offset<T: ToOffset>(
|
||||
&self,
|
||||
position: T,
|
||||
point: T,
|
||||
) -> Option<(&BufferSnapshot, usize)> {
|
||||
let offset = position.to_offset(self);
|
||||
let offset = point.to_offset(self);
|
||||
let mut cursor = self.excerpts.cursor::<usize>();
|
||||
cursor.seek(&offset, Bias::Right, &());
|
||||
if cursor.item().is_none() {
|
||||
@@ -2668,24 +2668,6 @@ impl MultiBufferSnapshot {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn position_to_buffer_point<T: ToPoint>(
|
||||
&self,
|
||||
position: T,
|
||||
) -> Option<(&BufferSnapshot, Point)> {
|
||||
let point = position.to_point(self);
|
||||
let mut cursor = self.excerpts.cursor::<Point>();
|
||||
cursor.seek(&point, Bias::Right, &());
|
||||
if cursor.item().is_none() {
|
||||
cursor.prev(&());
|
||||
}
|
||||
|
||||
cursor.item().map(|excerpt| {
|
||||
let excerpt_start = excerpt.range.context.start.to_point(&excerpt.buffer);
|
||||
let buffer_point = excerpt_start + point - *cursor.start();
|
||||
(&excerpt.buffer, buffer_point)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn suggested_indents(
|
||||
&self,
|
||||
rows: impl IntoIterator<Item = u32>,
|
||||
@@ -3466,12 +3448,12 @@ impl MultiBufferSnapshot {
|
||||
}
|
||||
|
||||
pub fn file_at<T: ToOffset>(&self, point: T) -> Option<&Arc<dyn File>> {
|
||||
self.position_to_buffer_offset(point)
|
||||
self.point_to_buffer_offset(point)
|
||||
.and_then(|(buffer, _)| buffer.file())
|
||||
}
|
||||
|
||||
pub fn language_at<T: ToOffset>(&self, point: T) -> Option<&Arc<Language>> {
|
||||
self.position_to_buffer_offset(point)
|
||||
self.point_to_buffer_offset(point)
|
||||
.and_then(|(buffer, offset)| buffer.language_at(offset))
|
||||
}
|
||||
|
||||
@@ -3482,7 +3464,7 @@ impl MultiBufferSnapshot {
|
||||
) -> &'a LanguageSettings {
|
||||
let mut language = None;
|
||||
let mut file = None;
|
||||
if let Some((buffer, offset)) = self.position_to_buffer_offset(point) {
|
||||
if let Some((buffer, offset)) = self.point_to_buffer_offset(point) {
|
||||
language = buffer.language_at(offset);
|
||||
file = buffer.file();
|
||||
}
|
||||
@@ -3490,7 +3472,7 @@ impl MultiBufferSnapshot {
|
||||
}
|
||||
|
||||
pub fn language_scope_at<T: ToOffset>(&self, point: T) -> Option<LanguageScope> {
|
||||
self.position_to_buffer_offset(point)
|
||||
self.point_to_buffer_offset(point)
|
||||
.and_then(|(buffer, offset)| buffer.language_scope_at(offset))
|
||||
}
|
||||
|
||||
@@ -3499,7 +3481,7 @@ impl MultiBufferSnapshot {
|
||||
position: T,
|
||||
cx: &AppContext,
|
||||
) -> Option<IndentSize> {
|
||||
let (buffer_snapshot, offset) = self.position_to_buffer_offset(position)?;
|
||||
let (buffer_snapshot, offset) = self.point_to_buffer_offset(position)?;
|
||||
Some(buffer_snapshot.language_indent_size_at(offset, cx))
|
||||
}
|
||||
|
||||
|
||||
@@ -1275,15 +1275,6 @@ mod tests {
|
||||
max: u8,
|
||||
}
|
||||
|
||||
/// Represents a summary of integers with various properties.
|
||||
///
|
||||
/// This struct provides a summary of a collection of integers, including:
|
||||
/// - The count of integers
|
||||
/// - The sum of all integers
|
||||
/// - Whether the collection contains any even numbers
|
||||
/// - The maximum value in the collection
|
||||
///
|
||||
/// It is used as the summary type for the `SumTree` when storing `u8` values.
|
||||
#[derive(Ord, PartialOrd, Default, Eq, PartialEq, Clone, Debug)]
|
||||
struct Count(usize);
|
||||
|
||||
|
||||
@@ -166,7 +166,7 @@ fn active_item_selection_properties(
|
||||
let multi_buffer = editor.buffer().clone();
|
||||
let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
|
||||
let (buffer_snapshot, buffer_offset) =
|
||||
multi_buffer_snapshot.position_to_buffer_offset(selection.head())?;
|
||||
multi_buffer_snapshot.point_to_buffer_offset(selection.head())?;
|
||||
let buffer_anchor = buffer_snapshot.anchor_before(buffer_offset);
|
||||
let buffer = multi_buffer.read(cx).buffer(buffer_snapshot.remote_id())?;
|
||||
Some(Location {
|
||||
|
||||
@@ -943,31 +943,26 @@ impl Item for TerminalView {
|
||||
fn tab_content(&self, params: TabContentParams, cx: &WindowContext) -> AnyElement {
|
||||
let terminal = self.terminal().read(cx);
|
||||
let title = terminal.title(true);
|
||||
let rerun_button = |task_id: task::TaskId| {
|
||||
IconButton::new("rerun-icon", IconName::Rerun)
|
||||
.icon_size(IconSize::Small)
|
||||
.size(ButtonSize::Compact)
|
||||
.icon_color(Color::Default)
|
||||
.shape(ui::IconButtonShape::Square)
|
||||
.tooltip(|cx| Tooltip::text("Rerun task", cx))
|
||||
.on_click(move |_, cx| {
|
||||
cx.dispatch_action(Box::new(tasks_ui::Rerun {
|
||||
task_id: Some(task_id.clone()),
|
||||
..tasks_ui::Rerun::default()
|
||||
}));
|
||||
})
|
||||
};
|
||||
|
||||
let (icon, icon_color, rerun_button) = match terminal.task() {
|
||||
Some(terminal_task) => match &terminal_task.status {
|
||||
TaskStatus::Unknown => (IconName::ExclamationTriangle, Color::Warning, None),
|
||||
TaskStatus::Running => (IconName::Play, Color::Disabled, None),
|
||||
TaskStatus::Unknown => (
|
||||
IconName::ExclamationTriangle,
|
||||
Color::Warning,
|
||||
Some(rerun_button(terminal_task.id.clone())),
|
||||
),
|
||||
TaskStatus::Completed { success } => {
|
||||
let rerun_button = rerun_button(terminal_task.id.clone());
|
||||
let task_id = terminal_task.id.clone();
|
||||
let rerun_button = IconButton::new("rerun-icon", IconName::Rerun)
|
||||
.icon_size(IconSize::Small)
|
||||
.size(ButtonSize::Compact)
|
||||
.icon_color(Color::Default)
|
||||
.shape(ui::IconButtonShape::Square)
|
||||
.tooltip(|cx| Tooltip::text("Rerun task", cx))
|
||||
.on_click(move |_, cx| {
|
||||
cx.dispatch_action(Box::new(tasks_ui::Rerun {
|
||||
task_id: Some(task_id.clone()),
|
||||
..Default::default()
|
||||
}));
|
||||
});
|
||||
|
||||
if *success {
|
||||
(IconName::Check, Color::Success, Some(rerun_button))
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user