use super::error::{EvalResult, Flow, signal};
use super::value::*;
use crate::buffer::{BufferId, BufferManager, InsertionType};
#[allow(dead_code)]
pub(crate) struct Marker {
pub buffer: Option<BufferId>,
pub position: Option<i64>,
pub insertion_type: bool, }
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_range_args(name: &str, args: &[Value], min: usize, max: usize) -> Result<(), Flow> {
if args.len() < min || args.len() > max {
Err(signal(
"wrong-number-of-arguments",
vec![Value::symbol(name), Value::fixnum(args.len() as i64)],
))
} else {
Ok(())
}
}
const MARK_MARKER_ID: u64 = i64::MAX as u64;
pub(crate) fn is_marker(v: &Value) -> bool {
v.is_marker()
}
pub(crate) fn make_marker_value(
buffer_id: Option<BufferId>,
position: Option<i64>,
insertion_type: bool,
) -> Value {
make_marker_value_with_id(buffer_id, position, insertion_type, None)
}
pub(crate) fn make_marker_value_with_id(
buffer_id: Option<BufferId>,
position: Option<i64>,
insertion_type: bool,
marker_id: Option<u64>,
) -> Value {
Value::make_marker(crate::heap_types::MarkerData {
buffer: buffer_id,
position,
insertion_type,
marker_id,
})
}
pub(crate) fn make_registered_buffer_marker(
buffers: &mut BufferManager,
buffer_id: BufferId,
position: i64,
insertion_type: bool,
) -> Value {
let byte_pos = match buffers.get(buffer_id) {
Some(buffer) => lisp_pos_to_byte(buffer, position),
None => 0,
};
let marker = make_marker_value(Some(buffer_id), Some(position), insertion_type);
let marker_id = buffers.create_marker(
buffer_id,
byte_pos,
if insertion_type {
InsertionType::After
} else {
InsertionType::Before
},
);
set_marker_id(&marker, marker_id);
marker
}
pub(crate) fn marker_logical_fields(v: &Value) -> Option<(Option<BufferId>, Option<i64>, bool)> {
if !v.is_marker() {
return None;
};
let marker = v.as_marker_data().unwrap().clone();
Some((marker.buffer, marker.position, marker.insertion_type))
}
pub(crate) fn marker_equal_hash_key_value(v: &Value) -> HashKey {
if let Some(marker) = v.as_marker_data() {
HashKey::Text(format!(
"marker:{:?}:{:?}:{}",
marker.buffer.map(|buffer| buffer.0),
marker.position,
marker.insertion_type
))
} else {
HashKey::Ptr(v.bits())
}
}
fn marker_id_value(v: &Value) -> Option<u64> {
if !v.is_marker() {
return None;
};
v.as_marker_data().unwrap().marker_id
}
fn is_mark_marker(v: &Value) -> bool {
marker_id_value(v) == Some(MARK_MARKER_ID)
}
fn set_marker_id(v: &Value, mid: u64) {
if v.is_marker() {
let _ = v.with_marker_data_mut(|data| {
data.marker_id = Some(mid);
});
}
}
fn expect_marker(_name: &str, v: &Value) -> Result<(), Flow> {
if is_marker(v) {
Ok(())
} else {
Err(signal(
"wrong-type-argument",
vec![Value::symbol("markerp"), *v],
))
}
}
fn marker_position_value(v: &Value) -> Value {
if !v.is_marker() {
return Value::NIL;
};
match v.as_marker_data().unwrap().position {
Some(position) => Value::fixnum(position),
None => Value::NIL,
}
}
pub(crate) fn marker_position_as_int(v: &Value) -> Result<i64, Flow> {
expect_marker("marker-position", v)?;
match marker_position_value(v).kind() {
ValueKind::Fixnum(n) => Ok(n),
_ => Err(signal(
"error",
vec![Value::string("Marker does not point anywhere")],
)),
}
}
pub(crate) fn marker_position_as_int_with_buffers(
buffers: &BufferManager,
v: &Value,
) -> Result<i64, Flow> {
expect_marker("marker-position", v)?;
if is_mark_marker(v) {
if let Some(buf_id) = marker_buffer_id(v)
&& let Some(buf) = buffers.get(buf_id)
{
return match buf.mark_char() {
Some(char_pos) => Ok(char_pos as i64 + 1),
None => Err(signal(
"error",
vec![Value::string("Marker does not point anywhere")],
)),
};
}
}
if let Some(mid) = marker_id_value(v) {
if let Some(buf_id) = marker_buffer_id(v)
&& let Some(buf) = buffers.get(buf_id)
&& let Some(marker_entry) = buf.marker_entry(mid)
{
return Ok(marker_entry.char_pos as i64 + 1);
}
}
marker_position_as_int(v)
}
pub(crate) fn marker_position_as_int_eval(
eval: &super::eval::Context,
v: &Value,
) -> Result<i64, Flow> {
marker_position_as_int_with_buffers(&eval.buffers, v)
}
fn marker_buffer_value(v: &Value) -> Value {
if !v.is_marker() {
return Value::NIL;
};
match v.as_marker_data().unwrap().buffer {
Some(buffer_id) => Value::make_buffer(buffer_id),
None => Value::NIL,
}
}
fn marker_insertion_type_value(v: &Value) -> Value {
if !v.is_marker() {
return Value::NIL;
};
Value::bool_val(v.as_marker_data().unwrap().insertion_type)
}
fn marker_buffer_id(v: &Value) -> Option<BufferId> {
if !v.is_marker() {
return None;
};
v.as_marker_data().unwrap().buffer
}
fn lisp_pos_to_byte(buf: &crate::buffer::Buffer, lisp_pos: i64) -> usize {
buf.lisp_pos_to_full_buffer_byte(lisp_pos)
}
fn marker_targets_current_mark(marker: &Value) -> bool {
is_mark_marker(marker)
}
pub(crate) fn builtin_markerp(args: Vec<Value>) -> EvalResult {
expect_args("markerp", &args, 1)?;
Ok(Value::bool_val(is_marker(&args[0])))
}
pub(crate) fn builtin_marker_position(
eval: &mut super::eval::Context,
args: Vec<Value>,
) -> EvalResult {
builtin_marker_position_in_buffers(&eval.buffers, args)
}
pub(crate) fn builtin_marker_position_in_buffers(
buffers: &BufferManager,
args: Vec<Value>,
) -> EvalResult {
expect_args("marker-position", &args, 1)?;
expect_marker("marker-position", &args[0])?;
if let Some(mid) = marker_id_value(&args[0]) {
if let Some(buf_id) = marker_buffer_id(&args[0])
&& let Some(buf) = buffers.get(buf_id)
&& let Some(marker_entry) = buf.marker_entry(mid)
{
return Ok(Value::fixnum(marker_entry.char_pos as i64 + 1));
}
}
Ok(marker_position_value(&args[0]))
}
pub(crate) fn builtin_marker_buffer(
eval: &mut super::eval::Context,
args: Vec<Value>,
) -> EvalResult {
expect_args("marker-buffer", &args, 1)?;
expect_marker("marker-buffer", &args[0])?;
if let Some(buffer_id) = marker_buffer_id(&args[0])
&& eval.buffers.get(buffer_id).is_some()
{
return Ok(Value::make_buffer(buffer_id));
}
Ok(Value::NIL)
}
pub(crate) fn builtin_marker_buffer_in_buffers(
buffers: &BufferManager,
args: Vec<Value>,
) -> EvalResult {
expect_args("marker-buffer", &args, 1)?;
expect_marker("marker-buffer", &args[0])?;
if let Some(buffer_id) = marker_buffer_id(&args[0])
&& buffers.get(buffer_id).is_some()
{
return Ok(Value::make_buffer(buffer_id));
}
Ok(Value::NIL)
}
pub(crate) fn builtin_marker_insertion_type(args: Vec<Value>) -> EvalResult {
expect_args("marker-insertion-type", &args, 1)?;
expect_marker("marker-insertion-type", &args[0])?;
Ok(marker_insertion_type_value(&args[0]))
}
pub(crate) fn builtin_set_marker_insertion_type(
eval: &mut super::eval::Context,
args: Vec<Value>,
) -> EvalResult {
builtin_set_marker_insertion_type_in_buffers(&mut eval.buffers, args)
}
pub(crate) fn builtin_set_marker_insertion_type_in_buffers(
buffers: &mut BufferManager,
args: Vec<Value>,
) -> EvalResult {
expect_args("set-marker-insertion-type", &args, 2)?;
expect_marker("set-marker-insertion-type", &args[0])?;
let new_type = args[1].is_truthy();
if args[0].is_marker() {
let _ = args[0].with_marker_data_mut(|data| {
data.insertion_type = new_type;
});
}
if let Some(mid) = marker_id_value(&args[0]) {
let ins_type = if new_type {
InsertionType::After
} else {
InsertionType::Before
};
buffers.update_marker_insertion_type(mid, ins_type);
}
Ok(args[1])
}
pub(crate) fn builtin_make_marker(args: Vec<Value>) -> EvalResult {
expect_args("make-marker", &args, 0)?;
Ok(make_marker_value(None, None, false))
}
pub(crate) fn builtin_copy_marker(eval: &mut super::eval::Context, args: Vec<Value>) -> EvalResult {
builtin_copy_marker_in_buffers(&mut eval.buffers, args)
}
pub(crate) fn builtin_copy_marker_in_buffers(
buffers: &mut BufferManager,
args: Vec<Value>,
) -> EvalResult {
expect_range_args("copy-marker", &args, 1, 2)?;
let insertion_type = if args.len() > 1 {
args[1].is_truthy()
} else {
false
};
let src = &args[0];
match src.kind() {
ValueKind::Veclike(VecLikeType::Marker) => {
let buffer_id = marker_buffer_id(src);
let position = if is_mark_marker(src) {
buffer_id
.and_then(|buf_id| buffers.get(buf_id))
.and_then(|buf| buf.mark_char().map(|m| m as i64 + 1))
} else if let Some(mid) = marker_id_value(src) {
buffer_id
.and_then(|buf_id| buffers.get(buf_id))
.and_then(|buf| buf.marker_entry(mid))
.map(|marker| marker.char_pos as i64 + 1)
} else {
match marker_position_value(src).kind() {
ValueKind::Fixnum(n) => Some(n),
_ => None,
}
};
let marker = make_marker_value(buffer_id, position, insertion_type);
register_marker_in_buffers(buffers, &marker, buffer_id, position);
Ok(marker)
}
ValueKind::Fixnum(n) => {
let buffer_id = buffers.current_buffer().map(|b| b.id);
let marker = make_marker_value(buffer_id, Some(n), insertion_type);
register_marker_in_buffers(buffers, &marker, buffer_id, Some(n));
Ok(marker)
}
ValueKind::Nil => Ok(make_marker_value(None, None, insertion_type)),
_other => Err(signal(
"wrong-type-argument",
vec![Value::symbol("integer-or-marker-p"), args[0]],
)),
}
}
pub(crate) fn builtin_set_marker(eval: &mut super::eval::Context, args: Vec<Value>) -> EvalResult {
builtin_set_marker_in_buffers(&mut eval.buffers, args)
}
pub(crate) fn builtin_set_marker_in_buffers(
buffers: &mut BufferManager,
args: Vec<Value>,
) -> EvalResult {
expect_range_args("set-marker", &args, 2, 3)?;
expect_marker("set-marker", &args[0])?;
let targets_current_mark = marker_targets_current_mark(&args[0]);
let buffer_id: Option<BufferId> = if args.len() > 2 && args[2].is_truthy() {
match args[2].kind() {
ValueKind::String => {
let name = args[2].as_str().unwrap().to_owned();
buffers.find_buffer_by_name(&name)
}
ValueKind::Veclike(VecLikeType::Buffer) => args[2].as_buffer_id(),
_other => {
return Err(signal(
"wrong-type-argument",
vec![Value::symbol("stringp"), args[2]],
));
}
}
} else {
buffers.current_buffer().map(|b| b.id)
};
let position: Option<i64> = match args[1].kind() {
ValueKind::Nil => None,
ValueKind::Fixnum(n) => Some(n),
ValueKind::Veclike(VecLikeType::Marker) => {
marker_position_as_int_with_buffers(buffers, &args[1]).ok()
}
_other => {
return Err(signal(
"wrong-type-argument",
vec![Value::symbol("integer-or-marker-p"), args[1]],
));
}
};
let buffer_id = position.and(buffer_id);
let position = match (position, buffer_id) {
(Some(pos), Some(buf_id)) => {
if let Some(buf) = buffers.get(buf_id) {
let max_pos = buf.total_chars() as i64 + 1;
Some(pos.clamp(1, max_pos))
} else {
Some(pos)
}
}
(other, _) => other,
};
register_marker_in_buffers(buffers, &args[0], buffer_id, position);
if args[0].is_marker() {
let _ = args[0].with_marker_data_mut(|data| {
data.buffer = buffer_id;
data.position = position;
});
}
if targets_current_mark {
let target_buf_id = buffer_id.or_else(|| buffers.current_buffer().map(|buf| buf.id));
if let Some(buf_id) = target_buf_id {
match position {
Some(pos) => {
if let Some(byte_pos) =
buffers.get(buf_id).map(|buf| lisp_pos_to_byte(buf, pos))
{
let _ = buffers.set_buffer_mark(buf_id, byte_pos);
}
}
None => {
let _ = buffers.clear_buffer_mark(buf_id);
}
}
}
}
Ok(args[0])
}
pub(crate) fn builtin_move_marker(eval: &mut super::eval::Context, args: Vec<Value>) -> EvalResult {
builtin_move_marker_in_buffers(&mut eval.buffers, args)
}
pub(crate) fn builtin_move_marker_in_buffers(
buffers: &mut BufferManager,
args: Vec<Value>,
) -> EvalResult {
builtin_set_marker_in_buffers(buffers, args)
}
#[allow(dead_code)]
fn register_marker_in_buffer(
eval: &mut super::eval::Context,
marker: &Value,
buffer_id: Option<BufferId>,
position: Option<i64>,
) {
register_marker_in_buffers(&mut eval.buffers, marker, buffer_id, position);
}
fn register_marker_in_buffers(
buffers: &mut BufferManager,
marker: &Value,
buffer_id: Option<BufferId>,
position: Option<i64>,
) {
if is_mark_marker(marker) {
return;
}
let insertion_type_val = marker_insertion_type_value(marker);
let ins_type = if insertion_type_val.is_truthy() {
crate::buffer::InsertionType::After
} else {
crate::buffer::InsertionType::Before
};
let existing_mid = marker_id_value(marker);
if let Some(mid) = existing_mid {
buffers.remove_marker(mid);
}
if let (Some(buf_id), Some(pos)) = (buffer_id, position) {
let mid = existing_mid.unwrap_or_else(|| buffers.allocate_marker_id());
set_marker_id(marker, mid);
if let Some(byte_pos) = buffers.get(buf_id).map(|buf| lisp_pos_to_byte(buf, pos)) {
let _ = buffers.register_marker_id(buf_id, mid, byte_pos, ins_type);
}
}
}
pub(crate) fn builtin_point_marker(
eval: &mut super::eval::Context,
args: Vec<Value>,
) -> EvalResult {
builtin_point_marker_in_buffers(&mut eval.buffers, args)
}
pub(crate) fn builtin_point_marker_in_buffers(
buffers: &mut BufferManager,
args: Vec<Value>,
) -> EvalResult {
expect_args("point-marker", &args, 0)?;
let buf = buffers
.current_buffer()
.ok_or_else(|| signal("error", vec![Value::string("No current buffer")]))?;
let pos = buf.point_char() as i64 + 1; let buffer_id = buf.id;
let marker = make_marker_value(Some(buffer_id), Some(pos), false);
register_marker_in_buffers(buffers, &marker, Some(buffer_id), Some(pos));
Ok(marker)
}
pub(crate) fn builtin_point_min_marker(
eval: &mut super::eval::Context,
args: Vec<Value>,
) -> EvalResult {
builtin_point_min_marker_in_buffers(&mut eval.buffers, args)
}
pub(crate) fn builtin_point_min_marker_in_buffers(
buffers: &mut BufferManager,
args: Vec<Value>,
) -> EvalResult {
expect_args("point-min-marker", &args, 0)?;
let buf = buffers
.current_buffer()
.ok_or_else(|| signal("error", vec![Value::string("No current buffer")]))?;
let pos = buf.point_min_char() as i64 + 1; let buffer_id = buf.id;
let marker = make_marker_value(Some(buffer_id), Some(pos), false);
register_marker_in_buffers(buffers, &marker, Some(buffer_id), Some(pos));
Ok(marker)
}
pub(crate) fn builtin_point_max_marker(
eval: &mut super::eval::Context,
args: Vec<Value>,
) -> EvalResult {
builtin_point_max_marker_in_buffers(&mut eval.buffers, args)
}
pub(crate) fn builtin_point_max_marker_in_buffers(
buffers: &mut BufferManager,
args: Vec<Value>,
) -> EvalResult {
expect_args("point-max-marker", &args, 0)?;
let buf = buffers
.current_buffer()
.ok_or_else(|| signal("error", vec![Value::string("No current buffer")]))?;
let pos = buf.point_max_char() as i64 + 1; let buffer_id = buf.id;
let marker = make_marker_value(Some(buffer_id), Some(pos), false);
register_marker_in_buffers(buffers, &marker, Some(buffer_id), Some(pos));
Ok(marker)
}
pub(crate) fn builtin_mark_marker(eval: &mut super::eval::Context, args: Vec<Value>) -> EvalResult {
builtin_mark_marker_in_buffers(&eval.buffers, args)
}
pub(crate) fn builtin_mark_marker_in_buffers(
buffers: &BufferManager,
args: Vec<Value>,
) -> EvalResult {
expect_args("mark-marker", &args, 0)?;
let buf = buffers
.current_buffer()
.ok_or_else(|| signal("error", vec![Value::string("No current buffer")]))?;
let buffer_id = buf.id;
match buf.mark_char() {
Some(char_pos) => {
let pos = char_pos as i64 + 1; Ok(make_marker_value_with_id(
Some(buffer_id),
Some(pos),
false,
Some(MARK_MARKER_ID),
))
}
None => Ok(make_marker_value_with_id(
Some(buffer_id),
None,
false,
Some(MARK_MARKER_ID),
)),
}
}
#[cfg(test)]
#[path = "marker_test.rs"]
mod tests;