Compare commits
7 Commits
fix-git-ht
...
rework-inl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b53e7635d9 | ||
|
|
0d976fd773 | ||
|
|
97216bbf5c | ||
|
|
6b8c3d42b3 | ||
|
|
0d1a6b9c83 | ||
|
|
2373a60de5 | ||
|
|
f25e85b639 |
79
assets/prompts/insertion.hbs
Normal file
79
assets/prompts/insertion.hbs
Normal file
@@ -0,0 +1,79 @@
|
||||
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>
|
||||
73
assets/prompts/replacement.hbs
Normal file
73
assets/prompts/replacement.hbs
Normal file
@@ -0,0 +1,73 @@
|
||||
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):
|
||||
@@ -28,7 +28,7 @@ use gpui::{
|
||||
FontWeight, Global, HighlightStyle, Model, ModelContext, Subscription, Task, TextStyle,
|
||||
UpdateGlobal, View, ViewContext, WeakView, WindowContext,
|
||||
};
|
||||
use language::{Buffer, IndentKind, Point, TransactionId};
|
||||
use language::{Buffer, Point, TransactionId};
|
||||
use language_model::{
|
||||
LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage, Role,
|
||||
};
|
||||
@@ -46,7 +46,6 @@ 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};
|
||||
@@ -73,6 +72,7 @@ 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,40 +154,51 @@ impl InlineAssistant {
|
||||
.map(|range| range.to_point(&snapshot))
|
||||
.collect::<Vec<Range<Point>>>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if selection_ranges.len() == 1 {
|
||||
let selection_range = &selection_ranges[0];
|
||||
codegen_ranges.push(CodegenRange {
|
||||
transform_range,
|
||||
selection_ranges: vec![selection_range],
|
||||
focus_assist: selection_is_newest,
|
||||
})
|
||||
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;
|
||||
|
||||
// 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();
|
||||
@@ -1078,10 +1089,10 @@ impl InlineAssistant {
|
||||
let mut new_blocks = Vec::new();
|
||||
for (new_row, old_row_range) in deleted_row_ranges {
|
||||
let (_, buffer_start) = old_snapshot
|
||||
.point_to_buffer_offset(Point::new(*old_row_range.start(), 0))
|
||||
.position_to_buffer_offset(Point::new(*old_row_range.start(), 0))
|
||||
.unwrap();
|
||||
let (_, buffer_end) = old_snapshot
|
||||
.point_to_buffer_offset(Point::new(
|
||||
.position_to_buffer_offset(Point::new(
|
||||
*old_row_range.end(),
|
||||
old_snapshot.line_len(MultiBufferRow(*old_row_range.end())),
|
||||
))
|
||||
@@ -2303,6 +2314,12 @@ 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()
|
||||
@@ -2317,8 +2334,8 @@ impl Codegen {
|
||||
assistant_panel_context: Option<LanguageModelRequest>,
|
||||
cx: &AppContext,
|
||||
) -> Result<LanguageModelRequest> {
|
||||
let buffer = self.buffer.read(cx).snapshot(cx);
|
||||
let language = buffer.language_at(self.transform_range.start);
|
||||
let multi_buffer = self.buffer.read(cx).snapshot(cx);
|
||||
let language = multi_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
|
||||
@@ -2343,54 +2360,55 @@ impl Codegen {
|
||||
};
|
||||
|
||||
let language_name = language_name.as_deref();
|
||||
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 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 selected_ranges = self
|
||||
.selected_ranges
|
||||
.iter()
|
||||
.filter_map(|selected_range| {
|
||||
let start = buffer
|
||||
.point_to_buffer_offset(selected_range.start)
|
||||
let start = multi_buffer
|
||||
.position_to_buffer_point(selected_range.start)
|
||||
.map(|(_, offset)| offset)?;
|
||||
let end = buffer
|
||||
.point_to_buffer_offset(selected_range.end)
|
||||
let end = multi_buffer
|
||||
.position_to_buffer_point(selected_range.end)
|
||||
.map(|(_, offset)| offset)?;
|
||||
|
||||
Some(start..end)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
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 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 mut messages = Vec::new();
|
||||
if let Some(context_request) = assistant_panel_context {
|
||||
@@ -2399,7 +2417,7 @@ impl Codegen {
|
||||
|
||||
messages.push(LanguageModelRequestMessage {
|
||||
role: Role::User,
|
||||
content: vec![prompt.into()],
|
||||
content: vec![inline_assist_prompt.into()],
|
||||
cache: false,
|
||||
});
|
||||
|
||||
@@ -2423,25 +2441,15 @@ 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),
|
||||
};
|
||||
|
||||
// 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 mut generated_text = String::new();
|
||||
let mut raw_output = String::new();
|
||||
|
||||
let telemetry = self.telemetry.clone();
|
||||
self.diff = Diff::default();
|
||||
@@ -2467,7 +2475,17 @@ impl Codegen {
|
||||
response_latency = Some(request_start.elapsed());
|
||||
}
|
||||
let chunk = chunk?;
|
||||
let char_ops = diff.push_new(&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..]);
|
||||
line_diff.push_char_operations(&char_ops, &selected_text);
|
||||
diff_tx
|
||||
.send((char_ops, line_diff.line_operations()))
|
||||
@@ -2485,6 +2503,10 @@ 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,10 +4,23 @@ use futures::StreamExt;
|
||||
use handlebars::{Handlebars, RenderError, TemplateError};
|
||||
use language::BufferSnapshot;
|
||||
use parking_lot::Mutex;
|
||||
use rope::Point;
|
||||
use serde::Serialize;
|
||||
use std::{ops::Range, sync::Arc, time::Duration};
|
||||
use std::{cmp, 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,
|
||||
@@ -160,6 +173,7 @@ impl PromptBuilder {
|
||||
.map_err(Box::new)
|
||||
};
|
||||
|
||||
register_template("insertion")?;
|
||||
register_template("content_prompt")?;
|
||||
register_template("terminal_assistant_prompt")?;
|
||||
register_template("edit_workflow")?;
|
||||
@@ -168,6 +182,59 @@ 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,6 +152,25 @@ 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 {
|
||||
|
||||
@@ -196,6 +196,24 @@ 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();
|
||||
@@ -228,6 +246,20 @@ 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 {
|
||||
|
||||
@@ -2650,11 +2650,11 @@ impl MultiBufferSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn point_to_buffer_offset<T: ToOffset>(
|
||||
pub fn position_to_buffer_offset<T: ToOffset>(
|
||||
&self,
|
||||
point: T,
|
||||
position: T,
|
||||
) -> Option<(&BufferSnapshot, usize)> {
|
||||
let offset = point.to_offset(self);
|
||||
let offset = position.to_offset(self);
|
||||
let mut cursor = self.excerpts.cursor::<usize>();
|
||||
cursor.seek(&offset, Bias::Right, &());
|
||||
if cursor.item().is_none() {
|
||||
@@ -2668,6 +2668,24 @@ 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>,
|
||||
@@ -3448,12 +3466,12 @@ impl MultiBufferSnapshot {
|
||||
}
|
||||
|
||||
pub fn file_at<T: ToOffset>(&self, point: T) -> Option<&Arc<dyn File>> {
|
||||
self.point_to_buffer_offset(point)
|
||||
self.position_to_buffer_offset(point)
|
||||
.and_then(|(buffer, _)| buffer.file())
|
||||
}
|
||||
|
||||
pub fn language_at<T: ToOffset>(&self, point: T) -> Option<&Arc<Language>> {
|
||||
self.point_to_buffer_offset(point)
|
||||
self.position_to_buffer_offset(point)
|
||||
.and_then(|(buffer, offset)| buffer.language_at(offset))
|
||||
}
|
||||
|
||||
@@ -3464,7 +3482,7 @@ impl MultiBufferSnapshot {
|
||||
) -> &'a LanguageSettings {
|
||||
let mut language = None;
|
||||
let mut file = None;
|
||||
if let Some((buffer, offset)) = self.point_to_buffer_offset(point) {
|
||||
if let Some((buffer, offset)) = self.position_to_buffer_offset(point) {
|
||||
language = buffer.language_at(offset);
|
||||
file = buffer.file();
|
||||
}
|
||||
@@ -3472,7 +3490,7 @@ impl MultiBufferSnapshot {
|
||||
}
|
||||
|
||||
pub fn language_scope_at<T: ToOffset>(&self, point: T) -> Option<LanguageScope> {
|
||||
self.point_to_buffer_offset(point)
|
||||
self.position_to_buffer_offset(point)
|
||||
.and_then(|(buffer, offset)| buffer.language_scope_at(offset))
|
||||
}
|
||||
|
||||
@@ -3481,7 +3499,7 @@ impl MultiBufferSnapshot {
|
||||
position: T,
|
||||
cx: &AppContext,
|
||||
) -> Option<IndentSize> {
|
||||
let (buffer_snapshot, offset) = self.point_to_buffer_offset(position)?;
|
||||
let (buffer_snapshot, offset) = self.position_to_buffer_offset(position)?;
|
||||
Some(buffer_snapshot.language_indent_size_at(offset, cx))
|
||||
}
|
||||
|
||||
|
||||
@@ -1275,6 +1275,15 @@ 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.point_to_buffer_offset(selection.head())?;
|
||||
multi_buffer_snapshot.position_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 {
|
||||
|
||||
Reference in New Issue
Block a user