1use anyhow::{anyhow, Result};
2use assistant_tooling::{LanguageModelTool, ProjectContext, ToolOutput};
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_buffer".to_string()
39 }
40
41 fn description(&self) -> String {
42 "Create a new buffer in the current codebase".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 div().child("Opening a buffer")
65 }
66}
67
68impl ToolOutput 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 }
87
88 fn execute(&mut self, cx: &mut ViewContext<Self>) -> Task<Result<()>> {
89 cx.spawn({
90 let workspace = self.workspace.clone();
91 let project = self.project.clone();
92 let input = self.input.clone();
93 |_this, mut cx| async move {
94 let input = input.ok_or_else(|| anyhow!("no input"))?;
95
96 let text = input.text.clone();
97 let language_name = input.language.clone();
98 let language = cx
99 .update(|cx| {
100 project
101 .read(cx)
102 .languages()
103 .language_for_name(&language_name)
104 })?
105 .await?;
106
107 let buffer = cx
108 .update(|cx| project.update(cx, |project, cx| project.create_buffer(cx)))?
109 .await?;
110
111 buffer.update(&mut cx, |buffer, cx| {
112 buffer.edit([(0..0, text)], None, cx);
113 buffer.set_language(Some(language), cx)
114 })?;
115
116 workspace
117 .update(&mut cx, |workspace, cx| {
118 workspace.add_item_to_active_pane(
119 Box::new(
120 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project), cx)),
121 ),
122 None,
123 cx,
124 );
125 })
126 .log_err();
127
128 Ok(())
129 }
130 })
131 }
132
133 fn serialize(&self, _cx: &mut ViewContext<Self>) -> Self::SerializedState {
134 ()
135 }
136
137 fn deserialize(
138 &mut self,
139 _output: Self::SerializedState,
140 _cx: &mut ViewContext<Self>,
141 ) -> Result<()> {
142 Ok(())
143 }
144}