commit 844350bac1840dd4a7eb575ced064e8eec2ee55b
parent 540845230bff53d867b8dce329c67b95c36af83f
Author: Sylvia Ivory <git@sivory.net>
Date: Mon, 23 Jun 2025 23:48:54 -0700
Begin work on autocompletion
Diffstat:
4 files changed, 143 insertions(+), 1 deletion(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -600,6 +600,7 @@ dependencies = [
"mlua",
"nu-ansi-term",
"reedline",
+ "tree-sitter",
"tree-sitter-highlight",
"tree-sitter-lua",
]
diff --git a/Cargo.toml b/Cargo.toml
@@ -27,5 +27,6 @@ lazy_static = "1.5.0"
mlua = { version = "0.10.5", features = ["anyhow", "send", "async"] }
nu-ansi-term = "0.50.1"
reedline = "0.40.0"
+tree-sitter = "0.25.6"
tree-sitter-highlight = "0.25.6"
tree-sitter-lua = "0.2.0"
diff --git a/src/completion.rs b/src/completion.rs
@@ -0,0 +1,138 @@
+use std::collections::HashSet;
+
+use mlua::prelude::*;
+use tree_sitter::{Parser, Point, Query, QueryCursor, StreamingIterator, Tree};
+
+#[derive(Debug)]
+struct Scope {
+ start: Point,
+ end: Point,
+ variables: HashSet<String>,
+}
+
+pub struct LuaCompleter {
+ lua: Lua,
+ parser: Parser,
+ tree: Tree,
+
+ locals_query: Query,
+ text: String,
+}
+
+impl LuaCompleter {
+ pub fn new(lua: Lua) -> Self {
+ let mut parser = Parser::new();
+
+ parser
+ .set_language(&tree_sitter_lua::LANGUAGE.into())
+ .unwrap();
+
+ let tree = parser.parse("", None).unwrap();
+
+ let locals_query = Query::new(
+ &tree_sitter_lua::LANGUAGE.into(),
+ tree_sitter_lua::LOCALS_QUERY,
+ )
+ .unwrap();
+
+ Self {
+ lua,
+ parser,
+ tree,
+ locals_query,
+ text: String::new(),
+ }
+ }
+
+ fn refresh_tree(&mut self, text: &str) {
+ self.tree = self.parser.parse(text, None).unwrap();
+ self.text = text.to_string();
+ }
+
+ fn globals(&self) -> Vec<LuaValue> {
+ self.lua
+ .globals()
+ .pairs()
+ .flatten()
+ .map(|(k, _): (LuaValue, LuaValue)| k)
+ .collect()
+ }
+
+ fn locals(&self, point: Point) -> Vec<String> {
+ let mut cursor = QueryCursor::new();
+
+ let matches = cursor.matches(
+ &self.locals_query,
+ self.tree.root_node(),
+ self.text.as_bytes(),
+ );
+ let names = self.locals_query.capture_names();
+
+ let mut scopes = Vec::new();
+ let mut variables = Vec::new();
+
+ matches.for_each(|m| {
+ for capture in m.captures {
+ let name = names[capture.index as usize];
+
+ match name {
+ "local.definition" => {
+ variables.push((
+ capture.node.start_position(),
+ capture.node.utf8_text(self.text.as_bytes()).unwrap(),
+ ));
+ }
+ "local.scope" => {
+ scopes.push((capture.node.start_position(), capture.node.end_position()));
+ }
+ _ => {}
+ }
+ }
+ });
+
+ let new_scopes = scopes
+ .into_iter()
+ .map(|(start, end)| {
+ let variables = variables
+ .iter()
+ .filter(|v| v.0 >= start && v.0 <= end)
+ .map(|v| v.1.to_string())
+ .collect::<HashSet<_>>();
+
+ Scope {
+ start,
+ end,
+ variables,
+ }
+ })
+ .collect::<Vec<_>>();
+
+ dbg!(&new_scopes);
+
+ Vec::new()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn locals() {
+ let mut completer = LuaCompleter::new(Lua::new());
+
+ completer.refresh_tree(
+ r#"
+ local function foo(a, b)
+ print(a, b)
+ end
+
+ local function bar(c)
+ print(c)
+ end
+ "#,
+ );
+
+ completer.locals(Point { row: 1, column: 0 });
+ }
+}
diff --git a/src/main.rs b/src/main.rs
@@ -11,8 +11,10 @@ use highlight::LuaHighlighter;
use mlua::prelude::*;
use reedline::Highlighter;
-use crate::{format::comfy_table, inspect::inspect};
+use format::comfy_table;
+use inspect::inspect;
+mod completion;
mod editor;
mod format;
mod highlight;