Skip to content

Commit

Permalink
Add rust bindings (#28)
Browse files Browse the repository at this point in the history
  • Loading branch information
shsms authored Feb 28, 2023
2 parents 5c2437d + d4c5687 commit 7736ad9
Show file tree
Hide file tree
Showing 6 changed files with 359 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ examples/mimeout*
build
.ccls-cache
docs/playground/mime.*
target
Cargo.lock
20 changes: 20 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
name = "mime-rs"
description = "A text processing framework, inspired by Emacs lisp and keyboard macros."
version = "0.3.0"
edition = "2021"
repository = "https://github.com/shsms/mime"
license = "GPL-3.0"
keywords = ["scripting", "text-processing"]
categories = ["text-processing"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[lib]
name = "mime"
path = "mime-rs/lib.rs"

[dependencies]
cxx = "1.0"

[build-dependencies]
cxx-build = "1.0"
2 changes: 1 addition & 1 deletion README.org
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ but in a scripting environment, without an editor. This enables very
sophisticated transformations that are easy to do through tools
like Emacs Keyboard Macros, but hard to do in code.

- *Documentation*: https://mime.dev
- *Documentation*: https://shsms.github.io/mime

** Dependencies
For providing its functionalities, Mime depends on these amazing
Expand Down
11 changes: 11 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
fn main() {
println!("cargo:rerun-if-changed=src/ffi.rs");

cxx_build::bridge("mime-rs/ffi.rs")
.includes(["c++/vendor/immer", "c++/include"])
.file("c++/src/lib/mime.cc")
.warnings(false)
.flag_if_supported("-std=c++17")
.opt_level(3)
.compile("mime");
}
69 changes: 69 additions & 0 deletions mime-rs/ffi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#[cxx::bridge(namespace = "mime")]
pub(crate) mod cpp {

unsafe extern "C++" {
include!("mime/mime.hh");

type text;
type buffer;

fn new_buffer() -> UniquePtr<buffer>;
fn open_buffer(name: &CxxString) -> UniquePtr<buffer>;

fn empty(self: &buffer) -> bool;
fn size(self: &buffer) -> usize;
fn narrowed(self: &buffer) -> bool;

fn save_as(self: &buffer, name: &CxxString);

fn set_mark(self: Pin<&mut buffer>);
fn get_mark(self: &buffer) -> i64;

fn get_contents_box(self: &buffer) -> UniquePtr<text>;

fn find(self: Pin<&mut buffer>, t: &CxxString) -> i64;
// fn find_text(self: Pin<&mut buffer>, t: &text) -> i64;

fn rfind(self: Pin<&mut buffer>, t: &CxxString) -> i64;
// fn rfind_text(self: Pin<&mut buffer>, t: &text) -> i64;

fn replace(self: Pin<&mut buffer>, from: &CxxString, to: &CxxString, n: usize) -> i32;
// fn replace_all(self: Pin<&mut buffer>, from: &CxxString, to: &CxxString) -> i32;

fn copy_box(self: &buffer) -> UniquePtr<text>;
fn cut_box(self: Pin<&mut buffer>) -> UniquePtr<text>;

fn paste(self: Pin<&mut buffer>, t: &CxxString);
fn paste_text(self: Pin<&mut buffer>, t: &text);

fn erase_region(self: Pin<&mut buffer>);
fn clear(self: Pin<&mut buffer>);

fn del_backward(self: Pin<&mut buffer>, n: usize) -> usize;
fn del_forward(self: Pin<&mut buffer>, n: usize) -> usize;

fn new_cursor(self: Pin<&mut buffer>) -> usize;
fn use_cursor(self: Pin<&mut buffer>, c: usize);
fn get_pos(self: &buffer) -> usize;
fn goto_pos(self: Pin<&mut buffer>, pos: i64) -> bool;

fn forward(self: Pin<&mut buffer>, n: usize) -> usize;
fn backward(self: Pin<&mut buffer>, n: usize) -> usize;
fn next_line(self: Pin<&mut buffer>, n: usize) -> usize;
fn prev_line(self: Pin<&mut buffer>, n: usize) -> usize;

fn start_of_buffer(self: Pin<&mut buffer>);
fn end_of_buffer(self: Pin<&mut buffer>);
fn start_of_line(self: Pin<&mut buffer>);
fn end_of_line(self: Pin<&mut buffer>);

fn start_of_block(self: Pin<&mut buffer>) -> bool;
fn end_of_block(self: Pin<&mut buffer>) -> bool;

fn narrow_to_block(self: Pin<&mut buffer>) -> bool;
fn narrow_to_region(self: Pin<&mut buffer>) -> bool;
fn widen(self: Pin<&mut buffer>);

fn text_to_string(t: &text) -> UniquePtr<CxxString>;
}
}
256 changes: 256 additions & 0 deletions mime-rs/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
mod ffi;

use cxx::{let_cxx_string, UniquePtr};
use ffi::cpp;
use std::{cell::RefCell, fmt::Display, rc::Rc};

pub enum Text {
Text(UniquePtr<cpp::text>),
Str(String),
}

impl Display for Text {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Text::Text(t) => f.write_str(&cpp::text_to_string(t).to_string()),
Text::Str(t) => f.write_str(&t),
}
}
}

impl From<String> for Text {
fn from(value: String) -> Self {
Self::Str(value)
}
}

impl From<&str> for Text {
fn from(value: &str) -> Self {
Self::Str(value.to_string())
}
}

pub struct Window {
buffer: Rc<RefCell<UniquePtr<cpp::buffer>>>,
cursor: usize,
}

impl Clone for Window {
/// Returns a new window for the same buffer, effectively adding a new
/// cursor to the existing buffer.
fn clone(&self) -> Self {
let buffer = self.buffer.clone();
let cursor = buffer.borrow_mut().as_mut().unwrap().new_cursor();
Self { buffer, cursor }
}
}

impl Window {
pub fn new() -> Self {
let buffer = Rc::new(RefCell::new(cpp::new_buffer()));
let cursor = buffer.borrow_mut().as_mut().unwrap().new_cursor();
Self { buffer, cursor }
}

pub fn open(filename: &str) -> Self {
let_cxx_string!(filename = filename);
let buffer = Rc::new(RefCell::new(cpp::open_buffer(&filename)));
let cursor = buffer.borrow_mut().as_mut().unwrap().new_cursor();
Self { buffer, cursor }
}

pub fn empty(&self) -> bool {
self.buffer.borrow().empty()
}

pub fn size(&self) -> usize {
self.buffer.borrow().size()
}

pub fn narrowed(&self) -> bool {
self.update_cursor();
self.buffer.borrow().narrowed()
}

pub fn save_as(&self, filename: &str) {
let_cxx_string!(filename = filename);
self.buffer.borrow().save_as(&filename);
}

pub fn set_mark(&self) {
self.update_cursor();
self.buffer.borrow_mut().as_mut().unwrap().set_mark();
}

// TODO: add bookmark interface
pub fn get_mark(&self) -> Option<i64> {
self.update_cursor();
let pos = self.buffer.borrow().get_mark();
(pos >= 0).then(|| pos)
}

pub fn get_contents(&self) -> Text {
Text::Text(self.buffer.borrow().get_contents_box())
}

pub fn find(&self, text: &str) -> Option<i64> {
self.update_cursor();
let_cxx_string!(text = text);
let pos = self.buffer.borrow_mut().as_mut().unwrap().find(&text);
(pos >= 0).then(|| pos)
}

pub fn rfind(&self, text: &str) -> Option<i64> {
self.update_cursor();
let_cxx_string!(text = text);
let pos = self.buffer.borrow_mut().as_mut().unwrap().rfind(&text);
(pos >= 0).then(|| pos)
}

pub fn replace(&self, from: &str, to: &str, n: usize) -> i32 {
self.update_cursor();
let_cxx_string!(from = from);
let_cxx_string!(to = to);
self.buffer
.borrow_mut()
.as_mut()
.unwrap()
.replace(&from, &to, n)
}

pub fn copy(&self) -> Text {
self.update_cursor();
Text::Text(self.buffer.borrow().copy_box())
}

pub fn cut(&self) -> Text {
self.update_cursor();
Text::Text(self.buffer.borrow_mut().as_mut().unwrap().cut_box())
}

pub fn paste<T: Into<Text>>(&self, text: T) {
self.update_cursor();
match text.into() {
Text::Text(ref text) => self.buffer.borrow_mut().as_mut().unwrap().paste_text(text),
Text::Str(ref text) => {
let_cxx_string!(text = text);
self.buffer.borrow_mut().as_mut().unwrap().paste(&text)
}
}
}

pub fn erase_region(&self) {
self.update_cursor();
self.buffer.borrow_mut().as_mut().unwrap().erase_region()
}

pub fn clear(&self) {
self.buffer.borrow_mut().as_mut().unwrap().clear()
}

pub fn get_pos(&self) -> usize {
self.update_cursor();
self.buffer.borrow().get_pos()
}

pub fn goto_pos(&self, pos: i64) -> bool {
self.update_cursor();
self.buffer.borrow_mut().as_mut().unwrap().goto_pos(pos)
}

pub fn del_backward(&self, n: usize) -> usize {
self.update_cursor();
self.buffer.borrow_mut().as_mut().unwrap().del_backward(n)
}

pub fn del_forward(&self, n: usize) -> usize {
self.update_cursor();
self.buffer.borrow_mut().as_mut().unwrap().del_forward(n)
}

pub fn backward(&self, n: usize) -> usize {
self.update_cursor();
self.buffer.borrow_mut().as_mut().unwrap().backward(n)
}

pub fn forward(&self, n: usize) -> usize {
self.update_cursor();
self.buffer.borrow_mut().as_mut().unwrap().forward(n)
}

pub fn prev_line(&self, n: usize) -> usize {
self.update_cursor();
self.buffer.borrow_mut().as_mut().unwrap().prev_line(n)
}

pub fn next_line(&self, n: usize) -> usize {
self.update_cursor();
self.buffer.borrow_mut().as_mut().unwrap().next_line(n)
}

pub fn start_of_buffer(&self) {
self.update_cursor();
self.buffer.borrow_mut().as_mut().unwrap().start_of_buffer()
}

pub fn end_of_buffer(&self) {
self.update_cursor();
self.buffer.borrow_mut().as_mut().unwrap().end_of_buffer()
}

pub fn start_of_line(&self) {
self.update_cursor();
self.buffer.borrow_mut().as_mut().unwrap().start_of_line()
}

pub fn end_of_line(&self) {
self.update_cursor();
self.buffer.borrow_mut().as_mut().unwrap().end_of_line()
}

pub fn start_of_block(&self) -> bool {
self.update_cursor();
self.buffer.borrow_mut().as_mut().unwrap().start_of_block()
}

pub fn end_of_block(&self) -> bool {
self.update_cursor();
self.buffer.borrow_mut().as_mut().unwrap().end_of_block()
}

pub fn narrow_to_block(&self) -> bool {
self.update_cursor();
self.buffer.borrow_mut().as_mut().unwrap().narrow_to_block()
}

pub fn narrow_to_region(&self) -> bool {
self.update_cursor();
self.buffer
.borrow_mut()
.as_mut()
.unwrap()
.narrow_to_region()
}

pub fn widen(&self) {
self.update_cursor();
self.buffer.borrow_mut().as_mut().unwrap().widen()
}
}

impl Default for Window {
fn default() -> Self {
Self::new()
}
}

// Private functions
impl Window {
fn update_cursor(&self) {
self.buffer
.borrow_mut()
.as_mut()
.unwrap()
.use_cursor(self.cursor);
}
}

0 comments on commit 7736ad9

Please sign in to comment.