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;
#[derive(Clone, Debug)]
pub enum RegisterContent {
Text(String),
Number(i64),
Position { buffer: String, point: usize },
Rectangle(Vec<String>),
FrameConfig(Value),
File(String),
KbdMacro(Vec<Value>),
}
impl RegisterContent {
fn description(&self) -> &str {
match self {
RegisterContent::Text(_) => "text",
RegisterContent::Number(_) => "number",
RegisterContent::Position { .. } => "position",
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)) => Some(s.as_str()),
_ => None,
}
}
pub fn append_text(&mut self, register: char, text: &str, prepend: bool) {
match self.registers.get_mut(®ister) {
Some(RegisterContent::Text(existing)) => {
if prepend {
let mut new = String::with_capacity(text.len() + existing.len());
new.push_str(text);
new.push_str(existing);
*existing = new;
} else {
existing.push_str(text);
}
}
_ => {
self.registers
.insert(register, RegisterContent::Text(text.to_string()));
}
}
}
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::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<String, Flow> {
match value.kind() {
ValueKind::String => Ok(value.as_str().unwrap().to_string()),
ValueKind::Symbol(id) => Ok(resolve_sym(id).to_owned()),
ValueKind::Nil => Ok("nil".to_string()),
ValueKind::T => Ok("t".to_string()),
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) => Ok(char::from_u32(c as u32).unwrap_or('\0')),
ValueKind::String => {
let st = value.as_str().unwrap();
let mut chars = st.chars();
match (chars.next(), chars.next()) {
(Some(c), None) => Ok(c),
_ => Err(signal(
"wrong-type-argument",
vec![Value::symbol("characterp"), *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::string(s.clone())),
Some(RegisterContent::Number(n)) => Ok(Value::string(n.to_string())),
Some(RegisterContent::Rectangle(lines)) => Ok(Value::string(lines.join("\n"))),
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 buffer_name = eval
.buffers
.current_buffer()
.map(|b| b.name.clone())
.unwrap_or_else(|| "*scratch*".to_string());
let point = eval
.buffers
.current_buffer()
.map(|b| b.point())
.unwrap_or(1);
eval.registers.set(
reg,
RegisterContent::Position {
buffer: buffer_name,
point,
},
);
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(mut s)) => {
s.push_str(&inc.to_string());
eval.registers.set(reg, RegisterContent::Text(s));
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 desc = if s.len() > 60 {
format!("Register {} contains text: {}...", reg, &s[..60])
} else {
format!("Register {} contains text: {}", reg, s)
};
Ok(Value::string(desc))
}
Some(RegisterContent::Number(n)) => Ok(Value::string(format!(
"Register {} contains the number {}",
reg, n
))),
Some(RegisterContent::Position { buffer, point }) => Ok(Value::string(format!(
"Register {} contains a position: buffer={} point={}",
reg, buffer, 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, 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::string(s.clone())),
Some(RegisterContent::Number(n)) => Ok(Value::fixnum(*n)),
Some(RegisterContent::Position { buffer, point }) => Ok(Value::cons(
Value::string(buffer.clone()),
Value::fixnum(*point as i64),
)),
Some(RegisterContent::Rectangle(lines)) => {
let vals: Vec<Value> = lines.iter().map(|l| Value::string(l.clone())).collect();
Ok(Value::list(vals))
}
Some(RegisterContent::File(f)) => Ok(Value::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::string(s.clone())),
Some(RegisterContent::Rectangle(lines)) => Ok(Value::string(lines.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_str().unwrap().to_string()),
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;