@@ -0,0 +1,87 @@
+Guide the user through code changes in numbered steps that focus on individual functions, type definitions, etc.
+Surround each distinct step in a <step></step> XML tag.
+
+Here's an example of a user-assistant dialog with step numbers and a follow-up question in Rust:
+
+1. User: I want to create a User struct with name and age fields, and a method to print the user's information. Can you help me with that?
+A: Certainly! Let's create a User struct with the specified fields and a method to print the information:
+<step>Define the User struct
+```rust
+struct User {
+ name: String,
+ age: u32,
+}
+```
+</step>
+<step>Implement methods for the User struct
+```rust
+impl User {
+ fn new(name: String, age: u32) -> Self {
+ User { name, age }
+ }
+ fn print_info(&self) {
+ println!("Name: {}, Age: {}", self.name, self.age);
+ }
+}
+```
+</step>
+
+This code defines a User struct and implements two methods:
+- `new`: a constructor to create a new User
+- `print_info`: a method to print the user's information
+
+2. User: Great! Now, how can I create a User instance and call the print_info method?
+A: You can create a User instance and call the print_info method like this:
+
+<step>Create a User instance and call print_info
+```rust
+fn main() {
+ let user = User::new(String::from("Alice"), 30);
+ user.print_info();
+}
+```
+</step>
+
+This will output: `Name: Alice, Age: 30`
+
+3. User: Can you add an email field to the User struct and update the print_info method to include it?
+A: Certainly! Let's modify the User struct and update the print_info method:
+
+<step>Add email field to User struct
+```rust
+struct User {
+ name: String,
+ age: u32,
+ email: String,
+}
+```
+</step>
+
+<step>Update the new method and print_info method
+```rust
+impl User {
+ fn new(name: String, age: u32, email: String) -> Self {
+ User { name, age, email }
+ }
+ fn print_info(&self) {
+ println!("Name: {}, Age: {}, Email: {}", self.name, self.age, self.email);
+ }
+}
+```
+</step>
+
+<step>Update the main function
+```rust
+fn main() {
+ let user = User::new(String::from("Alice"), 30, String::from("alice@example.com"));
+ user.print_info();
+}
+```
+</step>
+
+This will now output: `Name: Alice, Age: 30, Email: alice@example.com`
+The changes include:
+1. Adding the `email` field to the User struct
+2. Updating the `new` method to accept an email parameter
+3. Modifying the `print_info` method to include the email
+4. Updating the main function to provide an email when creating a User instance
@@ -66,6 +66,11 @@ pub fn init(cx: &mut AppContext) {
cx.set_global(GlobalPromptStore(prompt_store_future))
}
+const BUILT_IN_TOOLTIP_TEXT: &'static str = concat!(
+ "This prompt supports special functionality.\n",
+ "It's read-only, but you can remove it from your default prompt."
+);
+
/// This function opens a new prompt library window if one doesn't exist already.
/// If one exists, it brings it to the foreground.
///
@@ -233,15 +238,29 @@ impl PickerDelegate for PromptPickerDelegate {
.end_hover_slot(
h_flex()
.gap_2()
- .child(
+ .child(if prompt_id.is_built_in() {
+ div()
+ .id("built-in-prompt")
+ .child(Icon::new(IconName::FileLock).color(Color::Muted))
+ .tooltip(move |cx| {
+ Tooltip::with_meta(
+ "Built-in prompt",
+ None,
+ BUILT_IN_TOOLTIP_TEXT,
+ cx,
+ )
+ })
+ .into_any()
+ } else {
IconButton::new("delete-prompt", IconName::Trash)
.icon_color(Color::Muted)
.shape(IconButtonShape::Square)
.tooltip(move |cx| Tooltip::text("Delete Prompt", cx))
.on_click(cx.listener(move |_, _, cx| {
cx.emit(PromptPickerEvent::Deleted { prompt_id })
- })),
- )
+ }))
+ .into_any_element()
+ })
.child(
IconButton::new("toggle-default-prompt", IconName::Sparkle)
.selected(default)
@@ -354,6 +373,10 @@ impl PromptLibrary {
pub fn save_prompt(&mut self, prompt_id: PromptId, cx: &mut ViewContext<Self>) {
const SAVE_THROTTLE: Duration = Duration::from_millis(500);
+ if prompt_id.is_built_in() {
+ return;
+ }
+
let prompt_metadata = self.store.metadata(prompt_id).unwrap();
let prompt_editor = self.prompt_editors.get_mut(&prompt_id).unwrap();
let title = prompt_editor.title_editor.read(cx).text(cx);
@@ -463,6 +486,7 @@ impl PromptLibrary {
let mut editor = Editor::auto_width(cx);
editor.set_placeholder_text("Untitled", cx);
editor.set_text(prompt_metadata.title.unwrap_or_default(), cx);
+ editor.set_read_only(true);
editor
});
let body_editor = cx.new_view(|cx| {
@@ -474,6 +498,7 @@ impl PromptLibrary {
});
let mut editor = Editor::for_buffer(buffer, None, cx);
+ editor.set_read_only(true);
editor.set_soft_wrap_mode(SoftWrap::EditorWidth, cx);
editor.set_show_gutter(false, cx);
editor.set_show_wrap_guides(false, cx);
@@ -943,7 +968,23 @@ impl PromptLibrary {
)
},
))
- .child(
+ .child(if prompt_id.is_built_in() {
+ div()
+ .id("built-in-prompt")
+ .child(
+ Icon::new(IconName::FileLock)
+ .color(Color::Muted),
+ )
+ .tooltip(move |cx| {
+ Tooltip::with_meta(
+ "Built-in prompt",
+ None,
+ BUILT_IN_TOOLTIP_TEXT,
+ cx,
+ )
+ })
+ .into_any()
+ } else {
IconButton::new(
"delete-prompt",
IconName::Trash,
@@ -961,8 +1002,9 @@ impl PromptLibrary {
})
.on_click(|_, cx| {
cx.dispatch_action(Box::new(DeletePrompt));
- }),
- )
+ })
+ .into_any_element()
+ })
.child(
IconButton::new(
"duplicate-prompt",
@@ -1062,7 +1104,6 @@ pub struct PromptMetadata {
pub title: Option<SharedString>,
pub default: bool,
pub saved_at: DateTime<Utc>,
- pub built_in: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
@@ -1078,6 +1119,10 @@ impl PromptId {
uuid: Uuid::new_v4(),
}
}
+
+ pub fn is_built_in(&self) -> bool {
+ !matches!(self, PromptId::User { .. })
+ }
}
pub struct PromptStore {
@@ -1163,13 +1208,17 @@ impl PromptStore {
let metadata_cache = MetadataCache::from_db(metadata, &txn)?;
txn.commit()?;
- Ok(PromptStore {
+ let store = PromptStore {
executor,
env: db_env,
metadata_cache: RwLock::new(metadata_cache),
metadata,
bodies,
- })
+ };
+
+ store.save_built_in_prompts().log_err();
+
+ Ok(store)
}
})
}
@@ -1237,7 +1286,6 @@ impl PromptStore {
title: metadata_v1.title.clone(),
default: metadata_v1.default,
saved_at: metadata_v1.saved_at,
- built_in: false,
},
)?;
bodies_db.put(&mut txn, &prompt_id_v2, &body_v1)?;
@@ -1346,12 +1394,15 @@ impl PromptStore {
default: bool,
body: Rope,
) -> Task<Result<()>> {
+ if id.is_built_in() {
+ return Task::ready(Err(anyhow!("built-in prompts cannot be saved")));
+ }
+
let prompt_metadata = PromptMetadata {
id,
title,
default,
saved_at: Utc::now(),
- built_in: false,
};
self.metadata_cache.write().insert(prompt_metadata.clone());
@@ -1371,20 +1422,72 @@ impl PromptStore {
})
}
+ fn save_built_in_prompts(&self) -> Result<()> {
+ self.save_built_in_prompt(
+ PromptId::EditWorkflow,
+ "Built-in: Editing Workflow",
+ "prompts/edit_workflow.md",
+ )?;
+ Ok(())
+ }
+
+ /// Write a built-in prompt to the database, preserving the value of the default field
+ /// if a prompt with this id already exists. This method blocks.
+ fn save_built_in_prompt(
+ &self,
+ id: PromptId,
+ title: impl Into<SharedString>,
+ body_path: &str,
+ ) -> Result<()> {
+ let mut metadata_cache = self.metadata_cache.write();
+ let existing_metadata = metadata_cache.metadata_by_id.get(&id).cloned();
+
+ let prompt_metadata = PromptMetadata {
+ id,
+ title: Some(title.into()),
+ default: existing_metadata.map_or(true, |m| m.default),
+ saved_at: Utc::now(),
+ };
+
+ metadata_cache.insert(prompt_metadata.clone());
+
+ let db_connection = self.env.clone();
+ let bodies = self.bodies;
+ let metadata_db = self.metadata;
+
+ let mut txn = db_connection.write_txn()?;
+ metadata_db.put(&mut txn, &id, &prompt_metadata)?;
+
+ let body = String::from_utf8(Assets.load(body_path)?.unwrap().to_vec())?;
+ bodies.put(&mut txn, &id, &body)?;
+
+ txn.commit()?;
+ Ok(())
+ }
+
fn save_metadata(
&self,
id: PromptId,
- title: Option<SharedString>,
+ mut title: Option<SharedString>,
default: bool,
) -> Task<Result<()>> {
+ let mut cache = self.metadata_cache.write();
+
+ if id.is_built_in() {
+ title = cache
+ .metadata_by_id
+ .get(&id)
+ .and_then(|metadata| metadata.title.clone());
+ }
+
let prompt_metadata = PromptMetadata {
id,
title,
default,
saved_at: Utc::now(),
- built_in: false,
};
- self.metadata_cache.write().insert(prompt_metadata.clone());
+
+ cache.insert(prompt_metadata.clone());
let db_connection = self.env.clone();
let metadata = self.metadata;
@@ -1402,10 +1505,10 @@ impl PromptStore {
self.metadata_cache.read().metadata.first().cloned()
}
- pub fn operations_prompt(&self) -> String {
+ pub fn step_resolution_prompt(&self) -> String {
String::from_utf8(
Assets
- .load("prompts/operations.md")
+ .load("prompts/step_resolution.md")
.unwrap()
.unwrap()
.to_vec(),