manen

Fancy Lua REPL
Log | Files | Refs | README | LICENSE

commit ddd2f6ff5cf2dc33405aa3c56e49b38ab9497310
parent f3b29c1b0f85b44ab148dcd044784f31ddb9ed03
Author: Sylvia Ivory <git@sivory.net>
Date:   Tue, 24 Jun 2025 01:14:38 -0700

Allow querying upvalues

Diffstat:
Msrc/completion.rs | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 81 insertions(+), 2 deletions(-)

diff --git a/src/completion.rs b/src/completion.rs @@ -55,12 +55,12 @@ impl LuaCompleter { self.scopes = self.resolve_scopes(); } - fn globals(&self) -> Vec<LuaValue> { + fn globals(&self) -> Vec<String> { self.lua .globals() .pairs() .flatten() - .map(|(k, _): (LuaValue, LuaValue)| k) + .map(|(k, _): (String, LuaValue)| k) .collect() } @@ -144,6 +144,61 @@ impl LuaCompleter { variables } + + // okay not the correct terminology + // + // there are 3 kinds of variable + // - local (current scope) + // - global (_G/_ENV) + // - upvalue (local of parent scope(s)) + // + // well in 5.2+ its only local and upvalue since you upvalue _ENV + // then you get the individual global variable + // + // in the code + // + // ```lua + // local a = 1 + // b = 2 + // + // local function _() + // local c = 3 + // print(a, b, c) + // end + // ``` + // + // the bytecode for the function is + // + // 1 [5] LOADI 0 3 + // 2 [6] GETTABUP 1 0 0 ; _ENV "print" + // 3 [6] GETUPVAL 2 1 ; a + // 4 [6] GETTABUP 3 0 1 ; _ENV "b" + // + // the local can be loaded with LOADI (load integer) while a and b + // both have to be upvalued + // + // this is different in 5.1 + // + // 1 [5] LOADK 0 -1 ; 3 + // 2 [6] GETGLOBAL 1 -2 ; print + // 3 [6] GETUPVAL 2 0 ; a + // 4 [6] GETGLOBAL 3 -3 ; b + // + // in 5.1, globals are treated uniquely and given their own opcode + // + // to summarize, this function is not properly named + // + // globals either exist or are an extension of _ENV + fn autocomplete_upvalue(&self, query: &str, point: Point) -> Vec<String> { + let mut upvalues = self.locals(point); + upvalues.extend(self.globals()); + upvalues.sort(); + + upvalues + .into_iter() + .filter(|s| s.starts_with(query)) + .collect() + } } #[cfg(test)] @@ -192,4 +247,28 @@ mod tests { &completer.locals(Point { row: 13, column: 0 }), ); } + + #[test] + fn upvalues() { + let lua = Lua::new(); + lua.globals().set("foobar", "").unwrap(); + + let mut completer = LuaCompleter::new(lua); + + completer.refresh_tree( + r#" + local function foo(a, fooing) + local foobaz = 3 + -- 3: foo, foobar, fooing, foobaz + end + "#, + ); + + assert_eq!( + &["foo", "foobar", "foobaz", "fooing"] + .map(|s| s.to_string()) + .as_slice(), + &completer.autocomplete_upvalue("foo", Point { row: 3, column: 0 }) + ); + } }