Skip to main content

luaur_repl_cli/functions/
complete_partial_matches.rs

1use alloc::string::String;
2use core::ffi::CStr;
3
4use luaur_common::functions::starts_with::startsWith;
5use luaur_vm::enums::lua_type::lua_Type;
6use luaur_vm::functions::lua_next::lua_next;
7use luaur_vm::functions::lua_pushnil::lua_pushnil;
8use luaur_vm::functions::lua_type::lua_type;
9use luaur_vm::macros::lua_istable::lua_istable;
10use luaur_vm::macros::lua_pop::lua_pop;
11use luaur_vm::macros::lua_tostring::lua_tostring;
12use luaur_vm::type_aliases::lua_state::lua_State;
13
14use crate::functions::try_replace_top_with_index::try_replace_top_with_index;
15
16// Mirrors MaxTraversalLimit in Repl.cpp.
17const MAX_TRAVERSAL_LIMIT: i32 = 50;
18
19// completePartialMatches finds keys that match the specified 'prefix'
20// Note: the table/object to be searched must be on the top of the Lua stack
21pub unsafe fn complete_partial_matches(
22    l: *mut lua_State,
23    complete_only_functions: bool,
24    edit_buffer: &str,
25    prefix: &str,
26    add_completion_callback: &dyn Fn(&str, &str),
27) {
28    let mut i = 0;
29    while i < MAX_TRAVERSAL_LIMIT && lua_istable!(l, -1) {
30        // table, key
31        lua_pushnil(l);
32
33        // Loop over all the keys in the current table
34        while lua_next(l, -2) != 0 {
35            if lua_type(l, -2) == lua_Type::LUA_TSTRING as i32 {
36                // table, key, value
37                let key_ptr = lua_tostring!(l, -2);
38                let key = CStr::from_ptr(key_ptr).to_string_lossy();
39                let value_type = lua_type(l, -1);
40
41                // If the last separator was a ':' (i.e. a method call) then only functions should be completed.
42                let required_value_type =
43                    !complete_only_functions || value_type == lua_Type::LUA_TFUNCTION as i32;
44
45                if !key.is_empty() && required_value_type && startsWith(&key, prefix) {
46                    let completed_component = &key[prefix.len()..];
47                    let mut completion =
48                        String::with_capacity(edit_buffer.len() + completed_component.len() + 1);
49                    completion.push_str(edit_buffer);
50                    completion.push_str(completed_component);
51                    if value_type == lua_Type::LUA_TFUNCTION as i32 {
52                        // Add an opening paren for function calls by default.
53                        completion.push('(');
54                    }
55                    add_completion_callback(&completion, &key);
56                }
57            }
58            lua_pop(l, 1);
59        }
60
61        // Replace the current table being searched with an __index table if one exists
62        if !try_replace_top_with_index(l) {
63            break;
64        }
65
66        i += 1;
67    }
68}