Improve script tool description and add lines iterator to Lua file objects (#26529)

Release Notes:

- N/A

---------

Co-authored-by: Agus Zubiaga <hi@aguz.me>
This commit is contained in:
Antonio Scandurra 2025-03-12 08:58:11 +01:00 committed by GitHub
parent 30afba50a9
commit f3f97895a9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 66 additions and 24 deletions

View file

@ -7,7 +7,7 @@ use futures::{
};
use gpui::{AppContext, AsyncApp, Context, Entity, Task, WeakEntity};
use language::Buffer;
use mlua::{ExternalResult, Lua, MultiValue, Table, UserData, UserDataMethods};
use mlua::{ExternalResult, Lua, MultiValue, ObjectLike, Table, UserData, UserDataMethods};
use parking_lot::Mutex;
use project::{search::SearchQuery, Fs, Project, ProjectPath, WorktreeId};
use regex::Regex;
@ -308,6 +308,10 @@ impl ScriptingSession {
let read_fn = lua.create_function(Self::io_file_read)?;
file.set("read", read_fn)?;
// lines method
let lines_fn = lua.create_function(Self::io_file_lines)?;
file.set("lines", lines_fn)?;
// write method
let write_fn = lua.create_function(Self::io_file_write)?;
file.set("write", write_fn)?;
@ -566,6 +570,17 @@ impl ScriptingSession {
}
}
fn io_file_lines(lua: &Lua, file_userdata: Table) -> mlua::Result<mlua::Function> {
let read_perm = file_userdata.get::<bool>("__read_perm")?;
if !read_perm {
return Err(mlua::Error::runtime("File not open for reading"));
}
lua.create_function::<_, _, mlua::Value>(move |lua, _: ()| {
file_userdata.call_method("read", lua.create_string("*l")?)
})
}
fn io_file_read_format(format: Option<mlua::Value>) -> mlua::Result<FileReadFormat> {
let format = match format {
Some(mlua::Value::String(s)) => {
@ -991,6 +1006,34 @@ mod tests {
assert_eq!(test_session.diff(cx), Vec::new());
}
#[gpui::test]
async fn test_lines_iterator(cx: &mut TestAppContext) {
let script = r#"
-- Create a test file with multiple lines
local file = io.open("lines_test.txt", "w")
file:write("Line 1\nLine 2\nLine 3\nLine 4\nLine 5")
file:close()
-- Read it back using the lines iterator
local read_file = io.open("lines_test.txt", "r")
local count = 0
for line in read_file:lines() do
count = count + 1
print(count .. ": " .. line)
end
read_file:close()
print("Total lines:", count)
"#;
let test_session = TestSession::init(cx).await;
let output = test_session.test_success(script, cx).await;
assert_eq!(
output,
"1: Line 1\n2: Line 2\n3: Line 3\n4: Line 4\n5: Line 5\nTotal lines:\t5\n"
);
}
#[gpui::test]
async fn test_read_write_roundtrip(cx: &mut TestAppContext) {
let script = r#"

View file

@ -15,7 +15,7 @@ pub struct ScriptingTool;
impl ScriptingTool {
pub const NAME: &str = "lua-interpreter";
pub const DESCRIPTION: &str = include_str!("scripting_tool_description.txt");
pub const DESCRIPTION: &str = include_str!("scripting_tool_description.md");
pub fn input_schema() -> serde_json::Value {
let schema = schemars::schema_for!(ScriptingToolInput);

View file

@ -0,0 +1,21 @@
Evaluates the given Lua script in an interpreter with access to the Lua standard library. The tool returns the scripts output to stdout and any error that may have occurred.
Use this tool to explore the current project and edit the user's codebase or operating system as requested.
Additional functions provided:
```lua
--- Search for matches of a regular expression in files.
-- @param pattern The regex pattern to search for (uses Rust's regex syntax)
-- @return An array of tables with 'path' (file path) and 'matches' (array of matching strings)
-- @usage local results = search("function\\s+\\w+")
function search(pattern)
-- Implementation provided by the tool
end
--- Generates an outline for the given file path, extracting top-level symbols such as functions, classes, exports, and other significant declarations. This provides a structural overview of the file's contents.
-- @param path
function outline(path)
-- Implementation provided by the tool
end
```

View file

@ -1,22 +0,0 @@
You can write a Lua script and I'll run it on my codebase and tell you what its
output was, including both stdout as well as the git diff of changes it made to
the filesystem. That way, you can get more information about the code base, or
make changes to the code base directly.
The Lua script will have access to `io` and it will run with the current working
directory being in the root of the code base, so you can use it to explore,
search, make changes, etc. You can also have the script print things, and I'll
tell you what the output was. Note that `io` only has `open`, and then the file
it returns only has the methods read, write, and close - it doesn't have popen
or anything else.
Also, I'm going to be putting this Lua script into JSON, so please don't use
Lua's double quote syntax for string literals - use one of Lua's other syntaxes
for string literals, so I don't have to escape the double quotes.
There will be a global called `search` which accepts a regex (it's implemented
using Rust's regex crate, so use that regex syntax) and runs that regex on the
contents of every file in the code base (aside from gitignored files), then
returns an array of tables with two fields: "path" (the path to the file that
had the matches) and "matches" (an array of strings, with each string being a
match that was found within the file).