use super::*;
use crate::buffer::BufferId;
use crate::emacs_core::intern::intern;
use crate::heap_types::LispString;
fn ls(text: &str) -> LispString {
LispString::from_utf8(text)
}
#[test]
fn normalize_symbol_reader_default_uses_list_head_and_symbol_name() {
crate::test_utils::init_test_tracing();
assert_eq!(
normalize_symbol_reader_default(Value::list(vec![
Value::symbol("forward-char"),
Value::symbol("backward-char"),
])),
Value::string("forward-char")
);
assert_eq!(
normalize_symbol_reader_default(Value::symbol("fill-column")),
Value::string("fill-column")
);
}
#[test]
fn normalize_buffer_reader_default_uses_list_head_and_live_buffer_name() {
crate::test_utils::init_test_tracing();
let mut eval = crate::emacs_core::eval::Context::new();
let buf_id = eval.buffers.create_buffer(" minibuffer-default ");
assert_eq!(
normalize_buffer_reader_default(
&eval.buffers,
Value::list(vec![Value::make_buffer(buf_id), Value::string("fallback")]),
),
Value::string(" minibuffer-default ")
);
assert_eq!(
normalize_buffer_reader_default(&eval.buffers, Value::make_buffer(buf_id)),
Value::string(" minibuffer-default ")
);
}
#[test]
fn read_buffer_completing_args_use_live_buffer_names_and_normalized_default() {
crate::test_utils::init_test_tracing();
let mut eval = crate::emacs_core::eval::Context::new();
let buf_id = eval.buffers.create_buffer(" target-buffer ");
let args = read_buffer_completing_args(
&eval.obarray,
&eval.buffers,
&[
Value::string("Buffer: "),
Value::list(vec![Value::make_buffer(buf_id), Value::string("fallback")]),
Value::T,
Value::symbol("predicate"),
],
);
assert_eq!(args[0], Value::string("Buffer (default target-buffer ): "));
assert_eq!(args[2], Value::symbol("predicate"));
assert_eq!(args[3], Value::T);
assert_eq!(args[6], Value::string(" target-buffer "));
let names = super::value_to_string_list(&args[1]);
assert!(names.contains(&" target-buffer ".to_string()));
}
#[test]
fn read_buffer_completing_args_formats_default_prompt_like_gnu() {
crate::test_utils::init_test_tracing();
let mut eval = crate::emacs_core::eval::Context::new();
eval.obarray
.set_symbol_value("minibuffer-default-prompt-format", Value::string(" [%s]"));
let with_default = read_buffer_completing_args(
&eval.obarray,
&eval.buffers,
&[
Value::string("Switch to buffer: "),
Value::string("*Messages*"),
],
);
assert_eq!(
with_default[0],
Value::string("Switch to buffer [*Messages*]: ")
);
let without_default = read_buffer_completing_args(
&eval.obarray,
&eval.buffers,
&[Value::string("Switch to buffer: "), Value::NIL],
);
assert_eq!(without_default[0], Value::string("Switch to buffer: "));
let empty_default = read_buffer_completing_args(
&eval.obarray,
&eval.buffers,
&[Value::string("Switch to buffer: "), Value::string("")],
);
assert_eq!(empty_default[0], Value::string("Switch to buffer: "));
}
#[test]
fn finish_read_command_with_minibuffer_normalizes_default_and_interns_result() {
crate::test_utils::init_test_tracing();
let result = finish_read_command_with_minibuffer(
&[
Value::string("Command: "),
Value::list(vec![
Value::symbol("forward-char"),
Value::symbol("backward-char"),
]),
],
|minibuffer_args| {
assert_eq!(
minibuffer_args,
&[
Value::string("Command: "),
Value::NIL,
Value::NIL,
Value::NIL,
Value::NIL,
Value::string("forward-char"),
]
);
Ok(Value::string("next-line"))
},
)
.unwrap();
assert_eq!(result, Value::symbol("next-line"));
}
#[test]
fn finish_read_variable_with_minibuffer_normalizes_default_and_interns_result() {
crate::test_utils::init_test_tracing();
let result = finish_read_variable_with_minibuffer(
&[Value::string("Variable: "), Value::symbol("fill-column")],
|minibuffer_args| {
assert_eq!(
minibuffer_args,
&[
Value::string("Variable: "),
Value::NIL,
Value::NIL,
Value::NIL,
Value::NIL,
Value::string("fill-column"),
]
);
Ok(Value::string("tab-width"))
},
)
.unwrap();
assert_eq!(result, Value::symbol("tab-width"));
}
#[test]
fn prefix_match_basic() {
crate::test_utils::init_test_tracing();
let candidates = vec![ls("apple"), ls("application"), ls("banana"), ls("apply")];
let result = prefix_match(&ls("app"), &candidates);
assert_eq!(result.len(), 3);
assert!(result.contains(&ls("apple")));
assert!(result.contains(&ls("application")));
assert!(result.contains(&ls("apply")));
}
#[test]
fn prefix_match_case_insensitive() {
crate::test_utils::init_test_tracing();
let candidates = vec![ls("Apple"), ls("APPLY"), ls("banana")];
let result = prefix_match(&ls("app"), &candidates);
assert_eq!(result.len(), 2);
}
#[test]
fn prefix_match_empty_input() {
crate::test_utils::init_test_tracing();
let candidates = vec![ls("a"), ls("b"), ls("c")];
let result = prefix_match(&ls(""), &candidates);
assert_eq!(result.len(), 3);
}
#[test]
fn prefix_match_no_matches() {
crate::test_utils::init_test_tracing();
let candidates = vec![ls("apple"), ls("banana")];
let result = prefix_match(&ls("zz"), &candidates);
assert!(result.is_empty());
}
#[test]
fn substring_match_basic() {
crate::test_utils::init_test_tracing();
let candidates = vec![ls("foobar"), ls("bazfoo"), ls("hello"), ls("food")];
let result = substring_match(&ls("foo"), &candidates);
assert_eq!(result.len(), 3);
assert!(result.contains(&ls("foobar")));
assert!(result.contains(&ls("bazfoo")));
assert!(result.contains(&ls("food")));
}
#[test]
fn flex_match_basic() {
crate::test_utils::init_test_tracing();
let candidates = vec![
ls("find-file"),
ls("flycheck"),
ls("first-foo"),
ls("hello"),
];
let result = flex_match(&ls("ff"), &candidates);
assert!(result.contains(&ls("find-file")));
assert!(result.contains(&ls("first-foo")));
assert!(!result.contains(&ls("hello")));
}
#[test]
fn flex_match_all_chars_in_order() {
crate::test_utils::init_test_tracing();
let candidates = vec![ls("abcdef"), ls("axbycz"), ls("zzz")];
let result = flex_match(&ls("abc"), &candidates);
assert_eq!(result.len(), 2);
assert!(result.contains(&ls("abcdef")));
assert!(result.contains(&ls("axbycz")));
}
#[test]
fn flex_match_case_insensitive() {
crate::test_utils::init_test_tracing();
let candidates = vec![ls("FindFile")];
let result = flex_match(&ls("ff"), &candidates);
assert_eq!(result.len(), 1);
}
#[test]
fn basic_match_case_sensitive() {
crate::test_utils::init_test_tracing();
let candidates = vec![ls("Apple"), ls("apple"), ls("application")];
let result = basic_match(&ls("app"), &candidates);
assert_eq!(result.len(), 2);
assert!(result.contains(&ls("apple")));
assert!(result.contains(&ls("application")));
assert!(!result.contains(&ls("Apple")));
}
#[test]
fn common_prefix_empty() {
crate::test_utils::init_test_tracing();
assert!(compute_common_prefix(&[]).is_none());
}
#[test]
fn common_prefix_single() {
crate::test_utils::init_test_tracing();
let strings = vec![ls("hello")];
assert_eq!(compute_common_prefix(&strings), Some(ls("hello")));
}
#[test]
fn common_prefix_multiple() {
crate::test_utils::init_test_tracing();
let strings = vec![ls("application"), ls("apple"), ls("apply")];
assert_eq!(compute_common_prefix(&strings), Some(ls("appl")));
}
#[test]
fn common_prefix_no_overlap() {
crate::test_utils::init_test_tracing();
let strings = vec![ls("abc"), ls("xyz")];
assert_eq!(compute_common_prefix(&strings), Some(ls("")));
}
#[test]
fn common_prefix_identical() {
crate::test_utils::init_test_tracing();
let strings = vec![ls("test"), ls("test")];
assert_eq!(compute_common_prefix(&strings), Some(ls("test")));
}
#[test]
fn history_navigation() {
crate::test_utils::init_test_tracing();
let mut mgr = MinibufferManager::new();
mgr.add_to_history(intern("test-history"), "first", 100);
mgr.add_to_history(intern("test-history"), "second", 100);
mgr.add_to_history(intern("test-history"), "third", 100);
mgr.read_from_minibuffer(BufferId(1), "prompt: ", None, Some(intern("test-history")))
.unwrap();
let prev = mgr.history_previous();
assert_eq!(prev, Some(ls("third")));
let prev = mgr.history_previous();
assert_eq!(prev, Some(ls("second")));
let next = mgr.history_next();
assert_eq!(next, Some(ls("third")));
let next = mgr.history_next();
assert_eq!(next, Some(ls("")));
let next = mgr.history_next();
assert_eq!(next, None);
mgr.exit_minibuffer();
}
#[test]
fn history_dedup() {
crate::test_utils::init_test_tracing();
let mut mgr = MinibufferManager::new();
mgr.add_to_history(intern("h"), "same", 100);
mgr.add_to_history(intern("h"), "same", 100);
mgr.add_to_history(intern("h"), "same", 100);
assert_eq!(mgr.history.get(intern("h")).len(), 1);
mgr.add_to_history(intern("h"), "different", 100);
assert_eq!(mgr.history.get(intern("h")).len(), 2);
assert_eq!(
crate::emacs_core::string_escape::emacs_bytes_to_storage_string(
mgr.history.get(intern("h"))[0].as_bytes(),
mgr.history.get(intern("h"))[0].is_multibyte(),
)
.as_str(),
"different"
);
assert_eq!(
crate::emacs_core::string_escape::emacs_bytes_to_storage_string(
mgr.history.get(intern("h"))[1].as_bytes(),
mgr.history.get(intern("h"))[1].is_multibyte(),
)
.as_str(),
"same"
);
}
#[test]
fn recursive_depth() {
crate::test_utils::init_test_tracing();
let mut mgr = MinibufferManager::new();
assert_eq!(mgr.depth(), 0);
assert!(!mgr.is_active());
mgr.read_from_minibuffer(BufferId(1), "1: ", None, None)
.unwrap();
assert_eq!(mgr.depth(), 1);
assert!(mgr.is_active());
mgr.read_from_minibuffer(BufferId(2), "2: ", None, None)
.unwrap();
assert_eq!(mgr.depth(), 2);
mgr.exit_minibuffer();
assert_eq!(mgr.depth(), 1);
mgr.exit_minibuffer();
assert_eq!(mgr.depth(), 0);
assert!(!mgr.is_active());
}
#[test]
fn recursive_depth_limit() {
crate::test_utils::init_test_tracing();
let mut mgr = MinibufferManager::new();
mgr.max_depth = 2;
mgr.read_from_minibuffer(BufferId(1), "1: ", None, None)
.unwrap();
mgr.read_from_minibuffer(BufferId(2), "2: ", None, None)
.unwrap();
let result = mgr.read_from_minibuffer(BufferId(3), "3: ", None, None);
assert!(result.is_err());
}
#[test]
fn exit_recursive_edit_rejects_top_level_command_loop_like_gnu() {
crate::test_utils::init_test_tracing();
let mut eval = crate::emacs_core::eval::Context::new();
eval.command_loop.recursive_depth = 1;
let result = builtin_exit_recursive_edit(&mut eval, vec![]);
assert!(matches!(
result,
Err(crate::emacs_core::error::Flow::Signal(sig))
if sig.symbol_name() == "user-error"
));
}
#[test]
fn abort_recursive_edit_rejects_top_level_command_loop_like_gnu() {
crate::test_utils::init_test_tracing();
let mut eval = crate::emacs_core::eval::Context::new();
eval.command_loop.recursive_depth = 1;
let result = builtin_abort_recursive_edit(&mut eval, vec![]);
assert!(matches!(
result,
Err(crate::emacs_core::error::Flow::Signal(sig))
if sig.symbol_name() == "user-error"
));
}
#[test]
fn recursive_disabled() {
crate::test_utils::init_test_tracing();
let mut mgr = MinibufferManager::new();
mgr.set_enable_recursive(false);
mgr.read_from_minibuffer(BufferId(1), "1: ", None, None)
.unwrap();
let result = mgr.read_from_minibuffer(BufferId(2), "2: ", None, None);
assert!(result.is_err());
}
#[test]
fn enter_exit_lifecycle() {
crate::test_utils::init_test_tracing();
let mut mgr = MinibufferManager::new();
{
let state = mgr
.read_from_minibuffer(BufferId(1), "Enter: ", Some("init"), None)
.unwrap();
assert_eq!(
state.prompt,
crate::heap_types::LispString::from_utf8("Enter: ")
);
assert_eq!(
super::super::builtins::runtime_string_from_lisp_string(&state.content),
"init"
);
assert!(state.active);
assert_eq!(state.depth, 1);
}
{
let state = mgr.current_mut().unwrap();
state.content = super::super::builtins::runtime_string_to_lisp_string("modified", true);
}
let result = mgr.exit_minibuffer();
assert_eq!(result, Some(ls("modified")));
assert_eq!(mgr.depth(), 0);
}
#[test]
fn exit_with_default() {
crate::test_utils::init_test_tracing();
let mut mgr = MinibufferManager::new();
{
let state = mgr
.read_from_minibuffer(BufferId(1), "Enter: ", None, None)
.unwrap();
state.default_value = Some(crate::heap_types::LispString::from_utf8("fallback"));
}
let result = mgr.exit_minibuffer();
assert_eq!(result, Some(ls("fallback")));
}
#[test]
fn abort_minibuffer_clears_state() {
crate::test_utils::init_test_tracing();
let mut mgr = MinibufferManager::new();
mgr.read_from_minibuffer(BufferId(1), "Enter: ", None, None)
.unwrap();
assert_eq!(mgr.depth(), 1);
mgr.abort_minibuffer();
assert_eq!(mgr.depth(), 0);
assert!(!mgr.is_active());
}
#[test]
fn exit_empty_stack() {
crate::test_utils::init_test_tracing();
let mut mgr = MinibufferManager::new();
assert_eq!(mgr.exit_minibuffer(), None);
}
#[test]
fn try_complete_with_table() {
crate::test_utils::init_test_tracing();
let mut mgr = MinibufferManager::new();
{
let state = mgr
.read_from_minibuffer(BufferId(1), "M-x ", Some("find"), None)
.unwrap();
state.completion_table = Some(CompletionTable::List(vec![
ls("find-file"),
ls("find-file-other-window"),
ls("find-tag"),
ls("forward-char"),
]));
}
let state = mgr.current().unwrap();
let result = mgr.try_complete(state);
assert_eq!(result.matches.len(), 3); assert_eq!(result.common_prefix, Some(ls("find-")));
mgr.exit_minibuffer();
}
#[test]
fn test_completion_exact_match() {
crate::test_utils::init_test_tracing();
let mgr = MinibufferManager::new();
let table = CompletionTable::List(vec![ls("apple"), ls("banana"), ls("cherry")]);
assert!(mgr.test_completion(&ls("apple"), &table));
assert!(mgr.test_completion(&ls("banana"), &table));
assert!(!mgr.test_completion(&ls("app"), &table));
assert!(!mgr.test_completion(&ls("APPLE"), &table));
}
#[test]
fn try_completion_string_result() {
crate::test_utils::init_test_tracing();
let mgr = MinibufferManager::new();
let table = CompletionTable::List(vec![ls("application"), ls("apple"), ls("apply")]);
let result = mgr.try_completion_string(&ls("app"), &table);
assert_eq!(result, Some(ls("appl")));
}
#[test]
fn all_completions_empty() {
crate::test_utils::init_test_tracing();
let mgr = MinibufferManager::new();
let table = CompletionTable::List(vec![ls("foo"), ls("bar")]);
let result = mgr.all_completions(&ls("zzz"), &table);
assert!(result.is_empty());
}
#[test]
fn completion_style_substring() {
crate::test_utils::init_test_tracing();
let mut mgr = MinibufferManager::new();
mgr.set_completion_style(CompletionStyle::Substring);
let table = CompletionTable::List(vec![ls("find-file"), ls("describe-file"), ls("file-name")]);
let result = mgr.all_completions(&ls("file"), &table);
assert_eq!(result.len(), 3); }
#[test]
fn completion_style_flex() {
crate::test_utils::init_test_tracing();
let mut mgr = MinibufferManager::new();
mgr.set_completion_style(CompletionStyle::Flex);
let table = CompletionTable::List(vec![ls("find-file"), ls("forward-char"), ls("flycheck")]);
let result = mgr.all_completions(&ls("ff"), &table);
assert!(result.contains(&ls("find-file")));
assert!(!result.contains(&ls("flycheck")));
}
#[test]
fn completion_style_basic_case_sensitive() {
crate::test_utils::init_test_tracing();
let mut mgr = MinibufferManager::new();
mgr.set_completion_style(CompletionStyle::Basic);
let table = CompletionTable::List(vec![ls("Apple"), ls("apple"), ls("application")]);
let result = mgr.all_completions(&ls("app"), &table);
assert_eq!(result.len(), 2);
assert!(result.contains(&ls("apple")));
assert!(result.contains(&ls("application")));
}
#[test]
fn alist_completion() {
crate::test_utils::init_test_tracing();
let mgr = MinibufferManager::new();
let table = CompletionTable::Alist(vec![
(ls("alpha"), Value::fixnum(1)),
(ls("beta"), Value::fixnum(2)),
(ls("alphabetical"), Value::fixnum(3)),
]);
let result = mgr.all_completions(&ls("alph"), &table);
assert_eq!(result.len(), 2);
}
#[test]
fn builtin_try_completion_unique_exact() {
crate::test_utils::init_test_tracing();
let mut eval = crate::emacs_core::eval::Context::new();
let coll = Value::list(vec![Value::string("unique"), Value::string("other")]);
let result = builtin_try_completion(&mut eval, vec![Value::string("unique"), coll]).unwrap();
assert!(result.is_t());
}
#[test]
fn builtin_try_completion_common_prefix() {
crate::test_utils::init_test_tracing();
let mut eval = crate::emacs_core::eval::Context::new();
let coll = Value::list(vec![Value::string("application"), Value::string("apple")]);
let result = builtin_try_completion(&mut eval, vec![Value::string("app"), coll]).unwrap();
assert!(result.as_utf8_str().unwrap() == "appl");
}
#[test]
fn builtin_try_completion_no_match() {
crate::test_utils::init_test_tracing();
let mut eval = crate::emacs_core::eval::Context::new();
let coll = Value::list(vec![Value::string("foo"), Value::string("bar")]);
let result = builtin_try_completion(&mut eval, vec![Value::string("zzz"), coll]).unwrap();
assert!(result.is_nil());
}
#[test]
fn builtin_try_completion_handles_raw_unibyte_candidates_without_panicking() {
crate::test_utils::init_test_tracing();
let mut eval = crate::emacs_core::eval::Context::new();
let raw = Value::heap_string(crate::heap_types::LispString::from_unibyte(vec![0xFF]));
let coll = Value::list(vec![raw]);
let result = builtin_try_completion(&mut eval, vec![Value::string(""), coll]);
assert!(result.is_ok(), "try-completion should return a Lisp result");
}
#[test]
fn builtin_try_completion_rejects_more_than_three_args() {
crate::test_utils::init_test_tracing();
let mut eval = crate::emacs_core::eval::Context::new();
let coll = Value::list(vec![Value::string("a")]);
let result = builtin_try_completion(
&mut eval,
vec![Value::string(""), coll, Value::NIL, Value::NIL],
);
assert!(matches!(
result,
Err(Flow::Signal(sig)) if sig.symbol_name() == "wrong-number-of-arguments"
));
}
#[test]
fn builtin_all_completions_returns_list() {
crate::test_utils::init_test_tracing();
let mut eval = crate::emacs_core::eval::Context::new();
let coll = Value::list(vec![
Value::string("apple"),
Value::string("application"),
Value::string("banana"),
]);
let result = builtin_all_completions(&mut eval, vec![Value::string("app"), coll]).unwrap();
let items = super::super::value::list_to_vec(&result).unwrap();
assert_eq!(items.len(), 2);
}
#[test]
fn builtin_all_completions_rejects_more_than_four_args() {
crate::test_utils::init_test_tracing();
let mut eval = crate::emacs_core::eval::Context::new();
let coll = Value::list(vec![Value::string("a")]);
let result = builtin_all_completions(
&mut eval,
vec![Value::string(""), coll, Value::NIL, Value::NIL, Value::NIL],
);
assert!(matches!(
result,
Err(Flow::Signal(sig)) if sig.symbol_name() == "wrong-number-of-arguments"
));
}
#[test]
fn builtin_test_completion_match() {
crate::test_utils::init_test_tracing();
let mut eval = crate::emacs_core::eval::Context::new();
let coll = Value::list(vec![Value::string("alpha"), Value::string("beta")]);
let result = builtin_test_completion(&mut eval, vec![Value::string("alpha"), coll]).unwrap();
assert!(result.is_t());
}
#[test]
fn builtin_test_completion_no_match() {
crate::test_utils::init_test_tracing();
let mut eval = crate::emacs_core::eval::Context::new();
let coll = Value::list(vec![Value::string("alpha"), Value::string("beta")]);
let result = builtin_test_completion(&mut eval, vec![Value::string("alp"), coll]).unwrap();
assert!(result.is_nil());
}
#[test]
fn builtin_test_completion_rejects_more_than_three_args() {
crate::test_utils::init_test_tracing();
let mut eval = crate::emacs_core::eval::Context::new();
let coll = Value::list(vec![Value::string("a")]);
let result = builtin_test_completion(
&mut eval,
vec![Value::string(""), coll, Value::NIL, Value::NIL],
);
assert!(matches!(
result,
Err(Flow::Signal(sig)) if sig.symbol_name() == "wrong-number-of-arguments"
));
}
#[test]
fn builtin_try_completion_returns_raw_unibyte_common_prefix() {
crate::test_utils::init_test_tracing();
let mut eval = crate::emacs_core::eval::Context::new();
let input = Value::heap_string(LispString::from_unibyte(vec![0xFF]));
let coll = Value::list(vec![
Value::heap_string(LispString::from_unibyte(vec![0xFF, b'A'])),
Value::heap_string(LispString::from_unibyte(vec![0xFF, b'B'])),
]);
let result = builtin_try_completion(&mut eval, vec![input, coll]).unwrap();
let string = result
.as_lisp_string()
.expect("raw completion result string");
assert_eq!(
crate::emacs_core::builtins::lisp_string_char_codes(string),
vec![0xFF]
);
}
#[test]
fn builtin_all_completions_preserves_raw_unibyte_candidates() {
crate::test_utils::init_test_tracing();
let mut eval = crate::emacs_core::eval::Context::new();
let input = Value::heap_string(LispString::from_unibyte(vec![0xFF]));
let coll = Value::list(vec![
Value::heap_string(LispString::from_unibyte(vec![0xFF, b'A'])),
Value::heap_string(LispString::from_unibyte(vec![0xFF, b'B'])),
Value::heap_string(LispString::from_unibyte(vec![0xFE, b'C'])),
]);
let result = builtin_all_completions(&mut eval, vec![input, coll]).unwrap();
let items = crate::emacs_core::value::list_to_vec(&result).expect("completion list");
assert_eq!(items.len(), 2);
assert_eq!(
crate::emacs_core::builtins::lisp_string_char_codes(
items[0].as_lisp_string().expect("first raw completion")
),
vec![0xFF, b'A' as u32]
);
assert_eq!(
crate::emacs_core::builtins::lisp_string_char_codes(
items[1].as_lisp_string().expect("second raw completion")
),
vec![0xFF, b'B' as u32]
);
}
#[test]
fn builtin_all_completions_honors_raw_unibyte_completion_regexps() {
crate::test_utils::init_test_tracing();
let mut eval = crate::emacs_core::eval::Context::new();
eval.obarray.set_symbol_value(
"completion-regexp-list",
Value::list(vec![Value::heap_string(LispString::from_unibyte(vec![
0xFF,
]))]),
);
let coll = Value::list(vec![
Value::heap_string(LispString::from_unibyte(vec![0xFF])),
Value::heap_string(LispString::from_unibyte(vec![0xFE])),
]);
let result = builtin_all_completions(&mut eval, vec![Value::string(""), coll]).unwrap();
let items = crate::emacs_core::value::list_to_vec(&result).expect("filtered completion list");
assert_eq!(items.len(), 1);
assert_eq!(
crate::emacs_core::builtins::lisp_string_char_codes(
items[0]
.as_lisp_string()
.expect("raw regexp-matched completion")
),
vec![0xFF]
);
}
#[test]
fn builtin_minibuffer_depth_returns_zero() {
crate::test_utils::init_test_tracing();
let result = builtin_minibuffer_depth(vec![]).unwrap();
assert!(result.is_fixnum());
}
#[test]
fn builtin_minibufferp_returns_nil() {
crate::test_utils::init_test_tracing();
let result = builtin_minibufferp(vec![]).unwrap();
assert!(result.is_nil());
}
#[test]
fn eval_minibuffer_runtime_state_tracks_active_prompt_and_contents() {
crate::test_utils::init_test_tracing();
let mut eval = crate::emacs_core::eval::Context::new();
let minibuf_id = eval.buffers.create_buffer(" *Minibuf-1*");
{
let buf = eval.buffers.get_mut(minibuf_id).expect("minibuffer buffer");
crate::emacs_core::minibuffer::install_minibuffer_buffer_text(
buf,
&crate::heap_types::LispString::from_utf8("Prompt: "),
Some(&crate::heap_types::LispString::from_utf8("value")),
);
}
eval.buffers.set_current(minibuf_id);
eval.minibuffers
.read_from_minibuffer(minibuf_id, "Prompt: ", Some("value"), None)
.expect("enter minibuffer");
assert_eq!(
builtin_minibuffer_prompt_ctx(&mut eval, vec![]).unwrap(),
Value::heap_string(crate::heap_types::LispString::from_utf8("Prompt: "))
);
assert_eq!(
builtin_minibuffer_contents_ctx(&mut eval, vec![])
.unwrap()
.as_utf8_str(),
Some("value")
);
assert_eq!(
builtin_minibuffer_contents_no_properties_ctx(&mut eval, vec![])
.unwrap()
.as_utf8_str(),
Some("value")
);
assert_eq!(
builtin_minibuffer_depth_ctx(&mut eval, vec![]).unwrap(),
Value::fixnum(1)
);
assert_eq!(
builtin_minibufferp_ctx(&mut eval, vec![]).unwrap(),
Value::T
);
assert_eq!(
builtin_minibufferp_ctx(&mut eval, vec![Value::NIL, Value::T]).unwrap(),
Value::T
);
assert!(matches!(
builtin_abort_minibuffers_ctx(&mut eval, vec![]),
Err(Flow::Throw { tag, value }) if tag.is_symbol_named("exit") && value == Value::T
));
}
#[test]
fn eval_minibuffer_runtime_state_preserves_raw_unibyte_prompt_and_contents() {
crate::test_utils::init_test_tracing();
let mut eval = crate::emacs_core::eval::Context::new();
let minibuf_id = eval.buffers.create_buffer(" *Minibuf-raw*");
let raw_prompt = crate::heap_types::LispString::from_unibyte(vec![0xFF, b':', b' ']);
{
let buf = eval.buffers.get_mut(minibuf_id).expect("minibuffer buffer");
crate::emacs_core::minibuffer::install_minibuffer_buffer_text(
buf,
&raw_prompt,
Some(&crate::heap_types::LispString::from_utf8("value")),
);
}
eval.buffers.set_current(minibuf_id);
eval.minibuffers
.read_from_minibuffer_lisp(
minibuf_id,
&raw_prompt,
Some(&crate::heap_types::LispString::from_utf8("value")),
None,
)
.expect("enter raw minibuffer");
let prompt = builtin_minibuffer_prompt_ctx(&mut eval, vec![]).unwrap();
assert_eq!(prompt.as_lisp_string().expect("prompt string"), &raw_prompt);
let contents = builtin_minibuffer_contents_ctx(&mut eval, vec![]).unwrap();
assert_eq!(contents.as_utf8_str(), Some("value"));
}
#[test]
fn builtin_minibuffer_prompt_end_falls_back_to_point_min_without_prompt_field() {
crate::test_utils::init_test_tracing();
let mut eval = crate::emacs_core::eval::Context::new();
let minibuf_id = eval.buffers.create_buffer(" *Minibuf-1*");
{
let buf = eval.buffers.get_mut(minibuf_id).expect("minibuffer buffer");
let prompt_end = crate::emacs_core::minibuffer::install_minibuffer_buffer_text(
buf,
&crate::heap_types::LispString::from_utf8("Prompt: "),
Some(&crate::heap_types::LispString::from_utf8("vm-mini")),
);
let _ = buf
.text
.text_props_remove_property(0, prompt_end, Value::symbol("field"));
}
eval.buffers.set_current(minibuf_id);
eval.minibuffers
.read_from_minibuffer(minibuf_id, "Prompt: ", Some("vm-mini"), None)
.expect("enter minibuffer");
assert_eq!(
builtin_minibuffer_prompt_end_ctx(&mut eval, vec![]).unwrap(),
Value::fixnum(1)
);
assert_eq!(
builtin_minibuffer_contents_ctx(&mut eval, vec![])
.unwrap()
.as_utf8_str(),
Some("Prompt: vm-mini")
);
}
#[test]
fn install_minibuffer_buffer_text_reuses_existing_buffer_via_buffer_edit_pipeline() {
crate::test_utils::init_test_tracing();
let mut eval = crate::emacs_core::eval::Context::new();
let minibuf_id = eval.buffers.create_buffer(" *Minibuf-reinstall*");
let buf = eval.buffers.get_mut(minibuf_id).expect("minibuffer buffer");
let first_prompt_end = crate::emacs_core::minibuffer::install_minibuffer_buffer_text(
buf,
&crate::heap_types::LispString::from_utf8("Prompt: "),
Some(&crate::heap_types::LispString::from_utf8("stale")),
);
assert_eq!(first_prompt_end, "Prompt: ".len());
assert_eq!(buf.point_byte(), "Prompt: stale".len());
let second_prompt_end = crate::emacs_core::minibuffer::install_minibuffer_buffer_text(
buf,
&crate::heap_types::LispString::from_utf8("Switch to buffer: "),
Some(&crate::heap_types::LispString::from_utf8("*Messages*")),
);
assert_eq!(second_prompt_end, "Switch to buffer: ".len());
assert_eq!(buf.buffer_string(), "Switch to buffer: *Messages*");
assert_eq!(buf.point_byte(), "Switch to buffer: *Messages*".len());
}
#[test]
fn builtin_minibufferp_accepts_string_and_second_arg() {
crate::test_utils::init_test_tracing();
let result = builtin_minibufferp(vec![Value::string("x"), Value::NIL]).unwrap();
assert!(result.is_nil());
}
#[test]
fn builtin_minibufferp_rejects_non_buffer_like_values() {
crate::test_utils::init_test_tracing();
let result = builtin_minibufferp(vec![Value::fixnum(1)]);
assert!(matches!(
result,
Err(Flow::Signal(sig)) if sig.symbol_name() == "wrong-type-argument"
));
}
#[test]
fn builtin_minibufferp_rejects_more_than_two_args() {
crate::test_utils::init_test_tracing();
let result = builtin_minibufferp(vec![Value::NIL, Value::NIL, Value::NIL]);
assert!(matches!(
result,
Err(Flow::Signal(sig)) if sig.symbol_name() == "wrong-number-of-arguments"
));
}
#[test]
fn builtin_recursive_edit_returns_nil() {
crate::test_utils::init_test_tracing();
let mut eval = crate::emacs_core::eval::Context::new();
let result = builtin_recursive_edit(&mut eval, vec![]).unwrap();
assert!(result.is_nil());
}
#[test]
fn builtin_recursive_edit_rejects_args() {
crate::test_utils::init_test_tracing();
let mut eval = crate::emacs_core::eval::Context::new();
let result = builtin_recursive_edit(&mut eval, vec![Value::NIL]);
assert!(matches!(
result,
Err(Flow::Signal(sig)) if sig.symbol_name() == "wrong-number-of-arguments"
));
}
#[test]
fn builtin_top_level_throws_top_level_tag() {
crate::test_utils::init_test_tracing();
let result = builtin_top_level(vec![]);
assert!(matches!(
result,
Err(Flow::Throw { tag, value })
if tag.is_symbol_named("top-level") && value.is_nil()
));
}
#[test]
fn builtin_top_level_rejects_args() {
crate::test_utils::init_test_tracing();
let result = builtin_top_level(vec![Value::NIL]);
assert!(matches!(
result,
Err(Flow::Signal(sig)) if sig.symbol_name() == "wrong-number-of-arguments"
));
}
#[test]
fn builtin_exit_recursive_edit_signals_user_error() {
crate::test_utils::init_test_tracing();
let mut eval = super::super::eval::Context::new();
let result = builtin_exit_recursive_edit(&mut eval, vec![]);
assert!(matches!(
result,
Err(Flow::Signal(sig)) if sig.symbol_name() == "user-error"
));
}
#[test]
fn builtin_exit_recursive_edit_rejects_args() {
crate::test_utils::init_test_tracing();
let mut eval = super::super::eval::Context::new();
let result = builtin_exit_recursive_edit(&mut eval, vec![Value::NIL]);
assert!(matches!(
result,
Err(Flow::Signal(sig)) if sig.symbol_name() == "wrong-number-of-arguments"
));
}
#[test]
fn builtin_minibuffer_contents_returns_current_buffer_text() {
crate::test_utils::init_test_tracing();
let mut eval = super::super::eval::Context::new();
eval.buffers
.current_buffer_mut()
.expect("scratch buffer")
.insert("probe");
let result = builtin_minibuffer_contents_ctx(&mut eval, vec![]).unwrap();
assert!(result.as_utf8_str().unwrap() == "probe");
}
#[test]
fn builtin_minibuffer_contents_no_properties_returns_current_buffer_text() {
crate::test_utils::init_test_tracing();
let mut eval = super::super::eval::Context::new();
eval.buffers
.current_buffer_mut()
.expect("scratch buffer")
.insert("probe");
let result = builtin_minibuffer_contents_no_properties_ctx(&mut eval, vec![]).unwrap();
assert!(result.as_utf8_str().unwrap() == "probe");
}
#[test]
fn builtin_minibuffer_contents_no_properties_rejects_args() {
crate::test_utils::init_test_tracing();
let mut eval = super::super::eval::Context::new();
let result = builtin_minibuffer_contents_no_properties_ctx(&mut eval, vec![Value::NIL]);
assert!(matches!(
result,
Err(Flow::Signal(sig)) if sig.symbol_name() == "wrong-number-of-arguments"
));
}
#[test]
fn builtin_exit_minibuffer_throws_exit_tag() {
crate::test_utils::init_test_tracing();
let result = builtin_exit_minibuffer(vec![]);
assert!(matches!(
result,
Err(Flow::Throw { tag, value })
if tag.is_symbol_named("exit") && value.is_nil()
));
}
#[test]
fn builtin_abort_minibuffers_signals_not_in_minibuffer_error() {
crate::test_utils::init_test_tracing();
let result = builtin_abort_minibuffers(vec![]);
assert!(matches!(
result,
Err(Flow::Signal(sig))
if sig.symbol_name() == "error"
&& matches!(sig.data.as_slice(), [val] if val.as_utf8_str().map(|s| s == "Not in a minibuffer").unwrap_or(false))
));
}
#[test]
fn builtin_abort_minibuffers_rejects_args() {
crate::test_utils::init_test_tracing();
let result = builtin_abort_minibuffers(vec![Value::NIL]);
assert!(matches!(
result,
Err(Flow::Signal(sig)) if sig.symbol_name() == "wrong-number-of-arguments"
));
}
#[test]
fn builtin_abort_recursive_edit_signals_user_error() {
crate::test_utils::init_test_tracing();
let mut eval = super::super::eval::Context::new();
let result = builtin_abort_recursive_edit(&mut eval, vec![]);
assert!(matches!(
result,
Err(Flow::Signal(sig)) if sig.symbol_name() == "user-error"
));
}
#[test]
fn builtin_abort_recursive_edit_rejects_args() {
crate::test_utils::init_test_tracing();
let mut eval = super::super::eval::Context::new();
let result = builtin_abort_recursive_edit(&mut eval, vec![Value::NIL]);
assert!(matches!(
result,
Err(Flow::Signal(sig)) if sig.symbol_name() == "wrong-number-of-arguments"
));
}
#[test]
fn builtin_read_file_name_signals_end_of_file() {
crate::test_utils::init_test_tracing();
let mut eval = super::super::eval::Context::new();
let result = builtin_read_file_name(
&mut eval,
vec![
Value::string("File: "),
Value::NIL,
Value::NIL,
Value::NIL,
Value::string("/tmp/test.txt"),
],
);
assert!(matches!(
result,
Err(Flow::Signal(sig))
if sig.symbol_name() == "end-of-file"
&& matches!(sig.data.as_slice(), [val] if val.as_utf8_str().map(|s| s == "Error reading from stdin").unwrap_or(false))
));
}
#[test]
fn builtin_read_file_name_validates_dir_default_and_initial() {
crate::test_utils::init_test_tracing();
let mut eval = super::super::eval::Context::new();
let bad_dir =
builtin_read_file_name(&mut eval, vec![Value::string("File: "), Value::fixnum(1)]);
assert!(matches!(
bad_dir,
Err(Flow::Signal(sig)) if sig.symbol_name() == "wrong-type-argument"
));
let bad_default = builtin_read_file_name(
&mut eval,
vec![Value::string("File: "), Value::NIL, Value::fixnum(1)],
);
assert!(matches!(
bad_default,
Err(Flow::Signal(sig)) if sig.symbol_name() == "wrong-type-argument"
));
let bad_initial = builtin_read_file_name(
&mut eval,
vec![
Value::string("File: "),
Value::NIL,
Value::NIL,
Value::NIL,
Value::fixnum(1),
],
);
assert!(matches!(
bad_initial,
Err(Flow::Signal(sig)) if sig.symbol_name() == "wrong-type-argument"
));
}
#[test]
fn builtin_read_file_name_rejects_more_than_six_args() {
crate::test_utils::init_test_tracing();
let mut eval = super::super::eval::Context::new();
let result = builtin_read_file_name(
&mut eval,
vec![
Value::string("File: "),
Value::NIL,
Value::NIL,
Value::NIL,
Value::NIL,
Value::NIL,
Value::NIL,
],
);
assert!(matches!(
result,
Err(Flow::Signal(sig)) if sig.symbol_name() == "wrong-number-of-arguments"
));
}
#[test]
fn builtin_read_buffer_signals_end_of_file() {
crate::test_utils::init_test_tracing();
let mut eval = super::super::eval::Context::new();
let result = builtin_read_buffer(
&mut eval,
vec![Value::string("Buffer: "), Value::string("*scratch*")],
);
assert!(matches!(
result,
Err(Flow::Signal(sig)) if sig.symbol_name() == "end-of-file"
));
}
#[test]
fn builtin_read_directory_name_rejects_more_than_five_args() {
crate::test_utils::init_test_tracing();
let mut eval = super::super::eval::Context::new();
let result = builtin_read_directory_name(
&mut eval,
vec![
Value::string("Directory: "),
Value::NIL,
Value::NIL,
Value::NIL,
Value::NIL,
Value::NIL,
],
);
assert!(matches!(
result,
Err(Flow::Signal(sig)) if sig.symbol_name() == "wrong-number-of-arguments"
));
}
#[test]
fn builtin_read_directory_name_validates_dir_default_and_initial() {
crate::test_utils::init_test_tracing();
let mut eval = super::super::eval::Context::new();
let bad_dir = builtin_read_directory_name(
&mut eval,
vec![Value::string("Directory: "), Value::fixnum(1)],
);
assert!(matches!(
bad_dir,
Err(Flow::Signal(sig)) if sig.symbol_name() == "wrong-type-argument"
));
let bad_default = builtin_read_directory_name(
&mut eval,
vec![Value::string("Directory: "), Value::NIL, Value::fixnum(1)],
);
assert!(matches!(
bad_default,
Err(Flow::Signal(sig)) if sig.symbol_name() == "wrong-type-argument"
));
let bad_initial = builtin_read_directory_name(
&mut eval,
vec![
Value::string("Directory: "),
Value::NIL,
Value::NIL,
Value::NIL,
Value::fixnum(1),
],
);
assert!(matches!(
bad_initial,
Err(Flow::Signal(sig)) if sig.symbol_name() == "wrong-type-argument"
));
}
#[test]
fn builtin_read_buffer_rejects_more_than_four_args() {
crate::test_utils::init_test_tracing();
let mut eval = super::super::eval::Context::new();
let result = builtin_read_buffer(
&mut eval,
vec![
Value::string("Buffer: "),
Value::NIL,
Value::NIL,
Value::NIL,
Value::NIL,
],
);
assert!(matches!(
result,
Err(Flow::Signal(sig)) if sig.symbol_name() == "wrong-number-of-arguments"
));
}
#[test]
fn builtin_read_command_rejects_more_than_two_args() {
crate::test_utils::init_test_tracing();
let mut eval = super::super::eval::Context::new();
let result = builtin_read_command(
&mut eval,
vec![Value::string("Command: "), Value::NIL, Value::NIL],
);
assert!(matches!(
result,
Err(Flow::Signal(sig)) if sig.symbol_name() == "wrong-number-of-arguments"
));
}
#[test]
fn builtin_read_variable_rejects_more_than_two_args() {
crate::test_utils::init_test_tracing();
let mut eval = super::super::eval::Context::new();
let result = builtin_read_variable(
&mut eval,
vec![Value::string("Variable: "), Value::NIL, Value::NIL],
);
assert!(matches!(
result,
Err(Flow::Signal(sig)) if sig.symbol_name() == "wrong-number-of-arguments"
));
}
#[test]
fn value_to_string_list_from_list() {
crate::test_utils::init_test_tracing();
let list = Value::list(vec![
Value::string("foo"),
Value::string("bar"),
Value::string("baz"),
]);
let result = value_to_string_list(&list);
assert_eq!(result, vec!["foo", "bar", "baz"]);
}
#[test]
fn value_to_string_list_from_alist() {
crate::test_utils::init_test_tracing();
let alist = Value::list(vec![
Value::cons(Value::string("key1"), Value::fixnum(1)),
Value::cons(Value::string("key2"), Value::fixnum(2)),
]);
let result = value_to_string_list(&alist);
assert_eq!(result, vec!["key1", "key2"]);
}
#[test]
fn value_to_string_list_from_nil() {
crate::test_utils::init_test_tracing();
let result = value_to_string_list(&Value::NIL);
assert!(result.is_empty());
}
#[test]
fn value_to_string_list_from_vector() {
crate::test_utils::init_test_tracing();
let vec = Value::vector(vec![Value::string("a"), Value::string("b")]);
let result = value_to_string_list(&vec);
assert_eq!(result, vec!["a", "b"]);
}