From f3f97895a93681ca0e27195ecb1b0fdd21e01042 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 12 Mar 2025 08:58:11 +0100 Subject: [PATCH] Improve script tool description and add lines iterator to Lua file objects (#26529) Release Notes: - N/A --------- Co-authored-by: Agus Zubiaga --- .../scripting_tool/src/scripting_session.rs | 45 ++++++++++++++++++- crates/scripting_tool/src/scripting_tool.rs | 2 +- .../src/scripting_tool_description.md | 21 +++++++++ .../src/scripting_tool_description.txt | 22 --------- 4 files changed, 66 insertions(+), 24 deletions(-) create mode 100644 crates/scripting_tool/src/scripting_tool_description.md delete mode 100644 crates/scripting_tool/src/scripting_tool_description.txt diff --git a/crates/scripting_tool/src/scripting_session.rs b/crates/scripting_tool/src/scripting_session.rs index a7ba633659ede33f85d7f7adcd31083cd0b57f0b..3da14e5aa8e05f0be683b02d0be9d499d76e53d6 100644 --- a/crates/scripting_tool/src/scripting_session.rs +++ b/crates/scripting_tool/src/scripting_session.rs @@ -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 { + let read_perm = file_userdata.get::("__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::Result { 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#" diff --git a/crates/scripting_tool/src/scripting_tool.rs b/crates/scripting_tool/src/scripting_tool.rs index ac5531eb34ac803dba4e51763ab8e1c547deab72..d4239fa6691de4d67026873ba1e79e6e5b0898aa 100644 --- a/crates/scripting_tool/src/scripting_tool.rs +++ b/crates/scripting_tool/src/scripting_tool.rs @@ -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); diff --git a/crates/scripting_tool/src/scripting_tool_description.md b/crates/scripting_tool/src/scripting_tool_description.md new file mode 100644 index 0000000000000000000000000000000000000000..4888898d6ed5d2e16f2b36b5e33d43552c4d493e --- /dev/null +++ b/crates/scripting_tool/src/scripting_tool_description.md @@ -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 +``` diff --git a/crates/scripting_tool/src/scripting_tool_description.txt b/crates/scripting_tool/src/scripting_tool_description.txt deleted file mode 100644 index 04238c00dd4c941d2bf1ad08eb9f4b19933b5400..0000000000000000000000000000000000000000 --- a/crates/scripting_tool/src/scripting_tool_description.txt +++ /dev/null @@ -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).