Skip to content

Remacs Types

brotzeit edited this page Dec 6, 2018 · 1 revision

Table of Contents

Intro

The struct ExternalPtr<T> allows a more flexible usage of various lisp types.

#[repr(transparent)]
#[derive(Debug)]
pub struct ExternalPtr<T>(*mut T);

This way we can add methods to multiple Emacs types. For example, we implemented PartialEq.

impl<T> PartialEq for ExternalPtr<T> {
    fn eq(&self, other: &Self) -> bool {
        self.as_ptr() == other.as_ptr()
    }
}

We have introduced aliases for the resulting types that follow Rust naming conventions and are consisting of the original type name + "Ref". In case of Lisp_Buffer the name of the type is LispBufferRef.

pub type LispBufferRef = ExternalPtr<Lisp_Buffer>;

There are also additional types for some of those types in order to reduce boilerplate code.

LispBuffer

LispBufferOrName

Since buffers in elisp are often accessed by name, the type LispBufferOrName is either a buffer's name or the buffer itself.

#[derive(Clone, Copy, PartialEq, Eq)]
pub enum LispBufferOrName {
    Buffer(LispObject),
    Name(LispObject),
}

Before we introduced this type, the function get_buffer looked like this:

#[lisp_fn]
pub fn get_buffer(buffer_or_name: LispObject) -> LispObject {
    if buffer_or_name.is_buffer() {
        buffer_or_name
    } else {
        buffer_or_name.as_string_or_error();
        cdr(assoc_ignore_text_properties(buffer_or_name, unsafe {
            Vbuffer_alist
        }))
    }
}

Now the lisp function takes a LispBufferOrName which can be casted to Option<LispBufferRef> by the method into().

#[lisp_fn]
pub fn get_buffer(buffer_or_name: LispBufferOrName) -> Option<LispBufferRef> {
    buffer_or_name.into()
}

LispBufferOrCurrent

In many lisp functions a LispObject can either be a Lisp_Buffer or Qnil. The type LispBufferOrCurrent takes care of this case.

pub struct LispBufferOrCurrent(LispBufferRef);

This check can be seen in the former version of the function buffer_file_name. When the argument is Qnil, the buffer will be the current buffer. Otherwise the argument's type will be checked with as_buffer_or_error().

#[lisp_fn(min = "0")]
pub fn buffer_file_name(buffer: LispObject) -> LispObject {
    let buf = if buffer.is_nil() {
        ThreadState::current_buffer()
    } else {
        buffer.as_buffer_or_error()
    };

    buf.filename_
}

The updated function looks like this:

#[lisp_fn(min = "0")]
pub fn buffer_file_name(buffer: LispBufferOrCurrent) -> LispObject {
    let buf = buffer.unwrap();

    buf.filename_
}

LispWindow

LispWindowOrSelected

pub struct LispWindowOrSelected(LispObject);

LispWindowLiveOrSelected

pub struct LispWindowLiveOrSelected(LispWindowRef);

LispWindowValidOrSelected

pub struct LispWindowValidOrSelected(LispWindowRef);

LispFrame

LispFrameOrSelected

#[derive(Clone, Copy)]
pub enum LispFrameOrSelected {
    Frame(LispFrameRef),
    Selected,
}

LispMisc

pub type LispMiscRef = ExternalPtr<Lisp_Misc_Any>;