1use anyhow::{anyhow, Result};
2use assistant_tooling::{LanguageModelTool, ProjectContext, ToolView};
3use editor::Editor;
4use gpui::{prelude::*, Model, Task, View, WeakView};
5use project::Project;
6use schemars::JsonSchema;
7use serde::Deserialize;
8use ui::prelude::*;
9use util::ResultExt;
10use workspace::Workspace;
11
12pub struct CreateBufferTool {
13 workspace: WeakView<Workspace>,
14 project: Model<Project>,
15}
16
17impl CreateBufferTool {
18 pub fn new(workspace: WeakView<Workspace>, project: Model<Project>) -> Self {
19 Self { workspace, project }
20 }
21}
22
23#[derive(Debug, Clone, Deserialize, JsonSchema)]
24pub struct CreateBufferInput {
25 /// The contents of the buffer.
26 text: String,
27
28 /// The name of the language to use for the buffer.
29 ///
30 /// This should be a human-readable name, like "Rust", "JavaScript", or "Python".
31 language: String,
32}
33
34impl LanguageModelTool for CreateBufferTool {
35 type View = CreateBufferView;
36
37 fn name(&self) -> String {
38 "create_file".to_string()
39 }
40
41 fn description(&self) -> String {
42 "Create a new untitled file in the current codebase. Side effect: opens it in a new pane/tab for the user to edit.".to_string()
43 }
44
45 fn view(&self, cx: &mut WindowContext) -> View<Self::View> {
46 cx.new_view(|_cx| CreateBufferView {
47 workspace: self.workspace.clone(),
48 project: self.project.clone(),
49 input: None,
50 error: None,
51 })
52 }
53}
54
55pub struct CreateBufferView {
56 workspace: WeakView<Workspace>,
57 project: Model<Project>,
58 input: Option<CreateBufferInput>,
59 error: Option<anyhow::Error>,
60}
61
62impl Render for CreateBufferView {
63 fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
64 ui::Label::new("Opening a buffer")
65 }
66}
67
68impl ToolView for CreateBufferView {
69 type Input = CreateBufferInput;
70
71 type SerializedState = ();
72
73 fn generate(&self, _project: &mut ProjectContext, _cx: &mut ViewContext<Self>) -> String {
74 let Some(input) = self.input.as_ref() else {
75 return "No input".to_string();
76 };
77
78 match &self.error {
79 None => format!("Created a new {} buffer", input.language),
80 Some(err) => format!("Failed to create buffer: {err:?}"),
81 }
82 }
83
84 fn set_input(&mut self, input: Self::Input, cx: &mut ViewContext<Self>) {
85 self.input = Some(input);
86 cx.notify();
87 }
88
89 fn execute(&mut self, cx: &mut ViewContext<Self>) -> Task<Result<()>> {
90 cx.spawn({
91 let workspace = self.workspace.clone();
92 let project = self.project.clone();
93 let input = self.input.clone();
94 |_this, mut cx| async move {
95 let input = input.ok_or_else(|| anyhow!("no input"))?;
96
97 let text = input.text.clone();
98 let language_name = input.language.clone();
99 let language = cx
100 .update(|cx| {
101 project
102 .read(cx)
103 .languages()
104 .language_for_name(&language_name)
105 })?
106 .await?;
107
108 let buffer = cx
109 .update(|cx| project.update(cx, |project, cx| project.create_buffer(cx)))?
110 .await?;
111
112 buffer.update(&mut cx, |buffer, cx| {
113 buffer.edit([(0..0, text)], None, cx);
114 buffer.set_language(Some(language), cx)
115 })?;
116
117 workspace
118 .update(&mut cx, |workspace, cx| {
119 workspace.add_item_to_active_pane(
120 Box::new(
121 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project), cx)),
122 ),
123 None,
124 cx,
125 );
126 })
127 .log_err();
128
129 Ok(())
130 }
131 })
132 }
133
134 fn serialize(&self, _cx: &mut ViewContext<Self>) -> Self::SerializedState {
135 ()
136 }
137
138 fn deserialize(
139 &mut self,
140 _output: Self::SerializedState,
141 _cx: &mut ViewContext<Self>,
142 ) -> Result<()> {
143 Ok(())
144 }
145}