use crate::lua_engine::LuaEngine;
use crate::state::{Mode, TuiState};
pub fn calculate_common_prefix(completions: &[String]) -> String {
if completions.is_empty() {
return String::new();
}
if completions.len() == 1 {
return completions[0].clone();
}
let mut common_prefix = String::new();
let mut pos = 0;
let mut done = false;
while !done {
let mut c: Option<char> = None;
for completion in completions {
if pos >= completion.len() {
done = true;
break;
}
let current_char = completion.chars().nth(pos).unwrap();
if c.is_none() {
c = Some(current_char);
} else if c.unwrap() != current_char {
done = true;
break;
}
}
if !done {
common_prefix.push(c.unwrap());
pos += 1;
}
}
common_prefix
}
pub fn format_completions_table(completions: &[String]) -> Vec<String> {
if completions.is_empty() {
return vec!["No completions found".to_string()];
}
if completions.len() == 1 {
return vec![completions[0].clone()];
}
let max_width = completions.iter().map(|c| c.len()).max().unwrap_or(0);
let terminal_width = 80; let padding = 2;
let column_width = max_width + padding;
let num_columns = (terminal_width / column_width).max(1);
let mut lines = Vec::new();
let mut current_line = String::new();
for (i, completion) in completions.iter().enumerate() {
let padded_completion = format!("{:<width$}", completion, width = max_width);
if i % num_columns == 0 && !current_line.is_empty() {
lines.push(current_line.trim_end().to_string());
current_line.clear();
}
current_line.push_str(&padded_completion);
current_line.push(' ');
}
if !current_line.is_empty() {
lines.push(current_line.trim_end().to_string());
}
lines
}
pub fn handle_command_completion(state: &mut TuiState, lua_engine: &mut LuaEngine) {
let current = state.command.trim();
let completions = match lua_engine.get_completions_from_lua(current) {
Ok(lua_completions) => lua_completions,
Err(_) => {
state.next_mode = Mode::Command;
state.set_warning("Completion system unavailable".to_string());
return;
}
};
if completions.is_empty() {
state.next_mode = Mode::Command;
state.set_warning("No completions found".to_string());
return;
}
let common_prefix = calculate_common_prefix(&completions);
if common_prefix != state.command {
state.command = common_prefix;
state.text_edit_position = state.command.len();
} else if completions.len() == 1 {
state.command = completions[0].clone();
state.text_edit_position = state.command.len();
} else {
let formatted_lines = format_completions_table(&completions);
let display_text = formatted_lines.join("\n");
state.next_mode = Mode::Command;
state.set_warning(display_text);
}
}
pub fn handle_repl_completion(state: &mut TuiState, lua_engine: &mut LuaEngine) {
let current = state.lua_console.input.trim();
let completions = match lua_engine.get_completions_from_lua(current) {
Ok(lua_completions) => lua_completions,
Err(_) => {
state.lua_console.add_error(
"Completion system unavailable".to_string(),
state.visible_width,
);
let visible_lines = state.visible_height.saturating_sub(2);
let total_lines = state.lua_console.output_history.len() + 1; if total_lines > visible_lines {
state.lua_console.scroll_offset = total_lines.saturating_sub(visible_lines);
}
return;
}
};
if completions.is_empty() {
state
.lua_console
.add_output("No completions found".to_string(), state.visible_width);
let visible_lines = state.visible_height.saturating_sub(2);
let total_lines = state.lua_console.output_history.len() + 1; if total_lines > visible_lines {
state.lua_console.scroll_offset = total_lines.saturating_sub(visible_lines);
}
return;
}
let common_prefix = calculate_common_prefix(&completions);
if common_prefix != state.lua_console.input {
state.lua_console.input = common_prefix;
state.text_edit_position = state.lua_console.input.len();
} else if completions.len() == 1 {
state.lua_console.input = completions[0].clone();
state.text_edit_position = state.lua_console.input.len();
} else {
let formatted_lines = format_completions_table(&completions);
for line in formatted_lines {
state.lua_console.add_output(line, state.visible_width);
}
let visible_lines = state.visible_height.saturating_sub(2);
let total_lines = state.lua_console.output_history.len() + 1; if total_lines > visible_lines {
state.lua_console.scroll_offset = total_lines.saturating_sub(visible_lines);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_calculate_common_prefix() {
assert_eq!(calculate_common_prefix(&[]), "");
assert_eq!(calculate_common_prefix(&["hello()".to_string()]), "hello()");
let completions = vec!["get_record()".to_string(), "get_position()".to_string()];
assert_eq!(calculate_common_prefix(&completions), "get_");
let completions = vec!["quit()".to_string(), "warning()".to_string()];
assert_eq!(calculate_common_prefix(&completions), "");
let completions = vec!["vmove()".to_string(), "vgoto()".to_string()];
assert_eq!(calculate_common_prefix(&completions), "v");
}
#[test]
fn test_format_completions_table() {
let result = format_completions_table(&[]);
assert_eq!(result, vec!["No completions found"]);
let result = format_completions_table(&["quit()".to_string()]);
assert_eq!(result, vec!["quit()"]);
let completions = vec![
"quit()".to_string(),
"warning()".to_string(),
"vmove()".to_string(),
"vgoto()".to_string(),
];
let result = format_completions_table(&completions);
assert!(result.len() >= 1);
assert!(result[0].contains("quit()"));
assert!(result[0].contains("warning()"));
}
}