use std::collections::HashMap;
use super::error::{EvalResult, Flow, signal};
use super::intern::resolve_sym;
use super::value::{Value, ValueKind, next_float_id};
use crate::gc_trace::GcTrace;
use crate::heap_types::LispString;
use crate::tagged::header::VecLikeType;
#[derive(Clone, Debug)]
pub enum RegisterContent {
Text(LispString),
Number(i64),
Marker(Value),
Rectangle(Vec<LispString>),
FrameConfig(Value),
File(LispString),
KbdMacro(Vec<Value>),
}
impl RegisterContent {
fn description(&self) -> &str {
match self {
RegisterContent::Text(_) => "text",
RegisterContent::Number(_) => "number",
RegisterContent::Marker(_) => "marker",
RegisterContent::Rectangle(_) => "rectangle",
RegisterContent::FrameConfig(_) => "frame-config",
RegisterContent::File(_) => "file",
RegisterContent::KbdMacro(_) => "kbd-macro",
}
}
}
#[derive(Clone, Debug)]
pub struct RegisterManager {
registers: HashMap<char, RegisterContent>,
}
impl Default for RegisterManager {
fn default() -> Self {
Self::new()
}
}
impl RegisterManager {
pub fn new() -> Self {
Self {
registers: HashMap::new(),
}
}
pub fn set(&mut self, register: char, content: RegisterContent) {
self.registers.insert(register, content);
}
pub fn get(&self, register: char) -> Option<&RegisterContent> {
self.registers.get(®ister)
}
pub fn clear(&mut self, register: char) {
self.registers.remove(®ister);
}
pub fn clear_all(&mut self) {
self.registers.clear();
}
pub fn list(&self) -> Vec<(char, &str)> {
let mut entries: Vec<(char, &str)> = self
.registers
.iter()
.map(|(&ch, content)| (ch, content.description()))
.collect();
entries.sort_by_key(|(ch, _)| *ch);
entries
}
pub fn get_text(&self, register: char) -> Option<&str> {
match self.registers.get(®ister) {
Some(RegisterContent::Text(s)) => s.as_utf8_str(),
_ => None,
}
}
pub fn append_text(&mut self, register: char, text: &str, prepend: bool) {
let make_text =
|multibyte: bool| super::builtins::runtime_string_to_lisp_string(text, multibyte);
match self.registers.get_mut(®ister) {
Some(RegisterContent::Text(existing)) => {
let new = make_text(existing.is_multibyte() || !text.is_ascii());
if prepend {
*existing = new.concat(existing);
} else {
*existing = existing.concat(&new);
}
}
_ => {
self.registers
.insert(register, RegisterContent::Text(make_text(!text.is_ascii())));
}
}
}
pub(crate) fn dump_registers(&self) -> &HashMap<char, RegisterContent> {
&self.registers
}
pub(crate) fn from_dump(registers: HashMap<char, RegisterContent>) -> Self {
Self { registers }
}
}
impl GcTrace for RegisterManager {
fn trace_roots(&self, roots: &mut Vec<Value>) {
for content in self.registers.values() {
match content {
RegisterContent::Marker(v) | RegisterContent::FrameConfig(v) => {
roots.push(*v);
}
RegisterContent::KbdMacro(keys) => {
for v in keys {
roots.push(*v);
}
}
_ => {}
}
}
}
}
fn expect_args(name: &str, args: &[Value], n: usize) -> Result<(), Flow> {
if args.len() != n {
Err(signal(
"wrong-number-of-arguments",
vec![Value::symbol(name), Value::fixnum(args.len() as i64)],
))
} else {
Ok(())
}
}
fn expect_min_args(name: &str, args: &[Value], min: usize) -> Result<(), Flow> {
if args.len() < min {
Err(signal(
"wrong-number-of-arguments",
vec![Value::symbol(name), Value::fixnum(args.len() as i64)],
))
} else {
Ok(())
}
}
fn expect_max_args(name: &str, args: &[Value], max: usize) -> Result<(), Flow> {
if args.len() > max {
Err(signal(
"wrong-number-of-arguments",
vec![Value::symbol(name), Value::fixnum(args.len() as i64)],
))
} else {
Ok(())
}
}
fn expect_string(value: &Value) -> Result<LispString, Flow> {
match value.kind() {
ValueKind::String => Ok(value.as_lisp_string().expect("string").clone()),
ValueKind::Symbol(id) => Ok(LispString::from_unibyte(
resolve_sym(id).as_bytes().to_vec(),
)),
ValueKind::Nil => Ok(LispString::from_unibyte(b"nil".to_vec())),
ValueKind::T => Ok(LispString::from_unibyte(b"t".to_vec())),
other => Err(signal(
"wrong-type-argument",
vec![Value::symbol("stringp"), *value],
)),
}
}
fn expect_int(value: &Value) -> Result<i64, Flow> {
match value.kind() {
ValueKind::Fixnum(n) => Ok(n),
other => Err(signal(
"wrong-type-argument",
vec![Value::symbol("integerp"), *value],
)),
}
}
fn expect_register(value: &Value) -> Result<char, Flow> {
match value.kind() {
ValueKind::Fixnum(c) => super::builtins::character_code_to_rust_char(c).ok_or_else(|| {
signal(
"error",
vec![Value::string("Invalid character code"), *value],
)
}),
ValueKind::String => {
let string = value.as_lisp_string().expect("string");
if string.schars() != 1 {
return Err(signal(
"wrong-type-argument",
vec![Value::symbol("characterp"), *value],
));
}
let code = if string.is_multibyte() {
crate::emacs_core::emacs_char::string_char(string.as_bytes()).0
} else {
string.as_bytes()[0] as u32
};
super::builtins::character_code_to_rust_char(code as i64).ok_or_else(|| {
signal(
"error",
vec![Value::string("Invalid character code"), *value],
)
})
}
other => Err(signal(
"wrong-type-argument",
vec![Value::symbol("characterp"), *value],
)),
}
}
pub(crate) fn builtin_copy_to_register(
eval: &mut super::eval::Context,
args: Vec<Value>,
) -> EvalResult {
expect_min_args("copy-to-register", &args, 2)?;
expect_max_args("copy-to-register", &args, 5)?;
let reg = expect_register(&args[0])?;
let text = expect_string(&args[1])?;
eval.registers.set(reg, RegisterContent::Text(text));
Ok(Value::NIL)
}
pub(crate) fn builtin_insert_register(
eval: &mut super::eval::Context,
args: Vec<Value>,
) -> EvalResult {
expect_min_args("insert-register", &args, 1)?;
expect_max_args("insert-register", &args, 2)?;
let reg = expect_register(&args[0])?;
match eval.registers.get(reg) {
Some(RegisterContent::Text(s)) => Ok(Value::heap_string(s.clone())),
Some(RegisterContent::Number(n)) => Ok(Value::string(n.to_string())),
Some(RegisterContent::Rectangle(lines)) => {
let rendered = lines
.iter()
.map(super::builtins::runtime_string_from_lisp_string)
.collect::<Vec<_>>()
.join("\n");
Ok(Value::string(rendered))
}
Some(_) => Err(signal(
"error",
vec![Value::string(format!(
"Register does not contain text: {}",
reg
))],
)),
None => Err(signal(
"error",
vec![Value::string(format!("Register '{}' is empty", reg))],
)),
}
}
pub(crate) fn builtin_point_to_register(
eval: &mut super::eval::Context,
args: Vec<Value>,
) -> EvalResult {
expect_args("point-to-register", &args, 1)?;
let reg = expect_register(&args[0])?;
let marker = match eval
.buffers
.current_buffer()
.map(|buffer| (buffer.id, buffer.point()))
{
Some((buffer_id, point)) => super::marker::make_registered_buffer_marker(
&mut eval.buffers,
buffer_id,
point as i64,
false,
),
None => super::marker::make_marker_value(None, None, false),
};
eval.registers.set(reg, RegisterContent::Marker(marker));
Ok(Value::NIL)
}
pub(crate) fn builtin_number_to_register(
eval: &mut super::eval::Context,
args: Vec<Value>,
) -> EvalResult {
expect_args("number-to-register", &args, 2)?;
let num = expect_int(&args[0])?;
let reg = expect_register(&args[1])?;
eval.registers.set(reg, RegisterContent::Number(num));
Ok(Value::NIL)
}
pub(crate) fn builtin_increment_register(
eval: &mut super::eval::Context,
args: Vec<Value>,
) -> EvalResult {
expect_args("increment-register", &args, 2)?;
let inc = expect_int(&args[0])?;
let reg = expect_register(&args[1])?;
match eval.registers.get(reg).cloned() {
Some(RegisterContent::Number(n)) => {
eval.registers.set(reg, RegisterContent::Number(n + inc));
Ok(Value::NIL)
}
Some(RegisterContent::Text(s)) => {
let digits = LispString::from_unibyte(inc.to_string().into_bytes());
eval.registers
.set(reg, RegisterContent::Text(s.concat(&digits)));
Ok(Value::NIL)
}
Some(_) => Err(signal(
"error",
vec![Value::string(format!(
"Register does not contain a number or text: {}",
reg
))],
)),
None => {
eval.registers.set(reg, RegisterContent::Number(inc));
Ok(Value::NIL)
}
}
}
pub(crate) fn builtin_view_register(
eval: &mut super::eval::Context,
args: Vec<Value>,
) -> EvalResult {
expect_args("view-register", &args, 1)?;
let reg = expect_register(&args[0])?;
match eval.registers.get(reg) {
Some(RegisterContent::Text(s)) => {
let rendered = super::builtins::runtime_string_from_lisp_string(s);
let desc = if rendered.len() > 60 {
format!("Register {} contains text: {}...", reg, &rendered[..60])
} else {
format!("Register {} contains text: {}", reg, rendered)
};
Ok(Value::string(desc))
}
Some(RegisterContent::Number(n)) => Ok(Value::string(format!(
"Register {} contains the number {}",
reg, n
))),
Some(RegisterContent::Marker(marker)) => {
let (buffer_id, _, _) =
super::marker::marker_logical_fields(marker).expect("register marker");
let buffer_desc = buffer_id
.and_then(|id| eval.buffers.get(id))
.map(|buffer| buffer.name_runtime_string_owned())
.unwrap_or_else(|| "no buffer".to_string());
let point = super::marker::marker_position_as_int_with_buffers(&eval.buffers, marker)
.ok()
.map(|pos| pos.to_string())
.unwrap_or_else(|| "nowhere".to_string());
Ok(Value::string(format!(
"Register {} contains a marker: buffer={} point={}",
reg, buffer_desc, point
)))
}
Some(RegisterContent::Rectangle(lines)) => Ok(Value::string(format!(
"Register {} contains a rectangle ({} lines)",
reg,
lines.len()
))),
Some(RegisterContent::FrameConfig(_)) => Ok(Value::string(format!(
"Register {} contains a frame configuration",
reg
))),
Some(RegisterContent::File(f)) => Ok(Value::string(format!(
"Register {} contains file: {}",
reg,
super::builtins::runtime_string_from_lisp_string(f)
))),
Some(RegisterContent::KbdMacro(keys)) => Ok(Value::string(format!(
"Register {} contains a keyboard macro ({} keys)",
reg,
keys.len()
))),
None => Ok(Value::string(format!("Register {} is empty", reg))),
}
}
pub(crate) fn builtin_get_register(
eval: &mut super::eval::Context,
args: Vec<Value>,
) -> EvalResult {
expect_args("get-register", &args, 1)?;
let reg = expect_register(&args[0])?;
match eval.registers.get(reg) {
Some(RegisterContent::Text(s)) => Ok(Value::heap_string(s.clone())),
Some(RegisterContent::Number(n)) => Ok(Value::fixnum(*n)),
Some(RegisterContent::Marker(marker)) => Ok(*marker),
Some(RegisterContent::Rectangle(lines)) => {
let vals: Vec<Value> = lines.iter().cloned().map(Value::heap_string).collect();
Ok(Value::list(vals))
}
Some(RegisterContent::File(f)) => Ok(Value::heap_string(f.clone())),
Some(RegisterContent::FrameConfig(v)) => Ok(*v),
Some(RegisterContent::KbdMacro(keys)) => Ok(Value::list(keys.clone())),
None => Ok(Value::NIL),
}
}
#[cfg(test)]
pub(crate) fn builtin_register_to_string(
eval: &mut super::eval::Context,
args: Vec<Value>,
) -> EvalResult {
expect_args("register-to-string", &args, 1)?;
let reg = expect_register(&args[0])?;
match eval.registers.get(reg) {
Some(RegisterContent::Text(s)) => Ok(Value::heap_string(s.clone())),
Some(RegisterContent::Rectangle(lines)) => Ok(Value::string(
lines
.iter()
.map(super::builtins::runtime_string_from_lisp_string)
.collect::<Vec<_>>()
.join("\n"),
)),
_ => Ok(Value::NIL),
}
}
pub(crate) fn builtin_set_register(
eval: &mut super::eval::Context,
args: Vec<Value>,
) -> EvalResult {
expect_args("set-register", &args, 2)?;
let reg = expect_register(&args[0])?;
let content = match args[1].kind() {
ValueKind::String => {
RegisterContent::Text(args[1].as_lisp_string().expect("string").clone())
}
ValueKind::Veclike(VecLikeType::Marker) => RegisterContent::Marker(args[1]),
ValueKind::Fixnum(n) => RegisterContent::Number(n),
ValueKind::Nil => {
eval.registers.clear(reg);
return Ok(Value::NIL);
}
other => RegisterContent::FrameConfig(args[1]),
};
eval.registers.set(reg, content);
Ok(Value::NIL)
}
#[cfg(test)]
#[path = "register_test.rs"]
mod tests;