From 498608970f7671696e05074ac1cefb7aa6d6d1fe Mon Sep 17 00:00:00 2001 From: "Michael D. Lowis" Date: Wed, 27 Sep 2017 14:22:17 -0400 Subject: [PATCH] Added preliminary buffer implementation based on a naive rope implementation --- Makefile | 2 + lib/buf.ml | 86 ++++++++++++++++++++++++++++++++++++ lib/rope.ml | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 211 insertions(+) create mode 100644 lib/buf.ml create mode 100644 lib/rope.ml diff --git a/Makefile b/Makefile index e0a96cb..40ab0ee 100644 --- a/Makefile +++ b/Makefile @@ -29,6 +29,8 @@ LIBOBJS = \ lib/x11.$(OBJEXT) \ lib/cfg.$(OBJEXT) \ lib/x11_prims.o \ + lib/rope.$(OBJEXT) \ + lib/buf.$(OBJEXT) \ lib/utf8.o .PHONY: all clean diff --git a/lib/buf.ml b/lib/buf.ml new file mode 100644 index 0000000..421d31a --- /dev/null +++ b/lib/buf.ml @@ -0,0 +1,86 @@ +type encoding = Utf8 | Binary + +type lfstyle = Unix | Dos + +type bufinfo = { + path: string; + modtime: int; + charset: encoding; + crlf: lfstyle; +} + +type bufstate = { + nlines : int; + outpoint : int; + rope : Rope.t +} + +type buf = { + info : bufinfo; + current : bufstate; + lastsave : bufstate; + undo : bufstate list; + redo : bufstate list; +} + +let create = + let state = { nlines = 0; outpoint = 0; rope = Rope.empty } + and info = { path = ""; modtime = 0; charset = Utf8; crlf = Unix } in + { info = info; current = state; lastsave = state; undo = []; redo = [] } + +let load path = + create + +let saveas buf path = + () + +let save buf = + saveas buf buf.info.path + +(* + +/* cursor/selection representation */ +typedef struct { + size_t beg; + size_t end; + size_t col; +} Sel; + +void buf_init(Buf* buf, void ( *errfn )( char* )); +size_t buf_load(Buf* buf, char* path); +void buf_reload(Buf* buf); +void buf_save(Buf* buf); + +size_t buf_insert(Buf* buf, bool indent, size_t off, Rune rune); +size_t buf_delete(Buf* buf, size_t beg, size_t end); + +void buf_undo(Buf* buf, Sel* sel); +void buf_redo(Buf* buf, Sel* sel); + +Rune buf_get(Buf* buf, size_t pos); +size_t buf_end(Buf* buf); + +size_t buf_change(Buf* buf, size_t beg, size_t end); +void buf_chomp(Buf* buf); +void buf_loglock(Buf* buf); +void buf_logclear(Buf* buf); +bool buf_iseol(Buf* buf, size_t pos); +size_t buf_bol(Buf* buf, size_t pos); +size_t buf_eol(Buf* buf, size_t pos); +size_t buf_bow(Buf* buf, size_t pos); +size_t buf_eow(Buf* buf, size_t pos); +size_t buf_lscan(Buf* buf, size_t pos, Rune r); +size_t buf_rscan(Buf* buf, size_t pos, Rune r); +void buf_getword(Buf* buf, bool ( *isword )(Rune), Sel* sel); +void buf_getblock(Buf* buf, Rune beg, Rune end, Sel* sel); +size_t buf_byrune(Buf* buf, size_t pos, int count); +size_t buf_byword(Buf* buf, size_t pos, int count); +size_t buf_byline(Buf* buf, size_t pos, int count); +void buf_findstr(Buf* buf, int dir, char* str, size_t* beg, size_t* end); +void buf_lastins(Buf* buf, size_t* beg, size_t* end); +size_t buf_setln(Buf* buf, size_t line); +size_t buf_getln(Buf* buf, size_t off); +size_t buf_getcol(Buf* buf, size_t pos); +size_t buf_setcol(Buf* buf, size_t pos, size_t col); + +*) diff --git a/lib/rope.ml b/lib/rope.ml new file mode 100644 index 0000000..acb9eda --- /dev/null +++ b/lib/rope.ml @@ -0,0 +1,123 @@ +exception Out_of_bounds of string + +type rope = + | Leaf of string * int * int + | Node of rope * rope * int + +type t = rope + +let empty = Leaf ("", 0, 0) + +let length = function + | Leaf (_,_,l) -> l + | Node (_,_,l) -> l + +let from_string s = + Leaf (s, 0, (String.length s)) + +let check_index rope i = + if i < 0 || i >= (length rope) then + raise (Out_of_bounds "Rope.check_index") + +let join left right = + let left_len = (length left) in + let right_len = (length right) in + if left_len == 0 then right + else if right_len == 0 then left + else Node (left, right, (length left) + (length right)) + +let rec split rope i = + if i < 0 || i > (length rope) then + raise (Out_of_bounds "Rope.split"); + match rope with + | Leaf (s,off,len) -> + (Leaf (s, off, i), Leaf (s, (off + i), len - (i))) + | Node (l,r,len) -> + let left_len = (length l) in + if i < left_len then + let (sl,sr) = (split l i) in + (sl, (join sr r)) + else + let (sl,sr) = (split r i) in + ((join l sl), sr) + +let rec getc rope i = + check_index rope i; + match rope with + | Leaf (s,off,len) -> s.[off + i] + | Node (l,r,len) -> + let left_len = (length l) in + if i < left_len then + getc l i + else + getc r (i - left_len) + +let rec puts rope s i = + let (left,right) = split rope i in + let middle = from_string s in + (join (join left middle) right) + +let del rope i j = + let (l_left,l_right) = split rope i in + let (r_left,r_right) = split l_right (j - i) in + (join l_left r_right) + +module Iter = struct + type t = { + rope: rope; + length: int; + mutable pos: int; + } + + let make rope index = + check_index rope index; + { rope = rope; length = (length rope); pos = index } + + let pos itr = itr.pos + + let incr itr = itr.pos <- (itr.pos + 1) + + let decr itr = itr.pos <- (itr.pos - 1) + + let goto itr pos= itr.pos <- pos + + let move itr off = itr.pos <- (itr.pos + off) + + let get itr = getc itr.rope itr.pos + + let has_next itr = (itr.pos + 1) <= itr.length + + let has_prev itr = (itr.pos - 1) > 0 +end + +let iteri fn rope = + let it = Iter.make rope 0 in + while (Iter.has_next it) do + fn (Iter.pos it) (Iter.get it); + Iter.incr it + done + +let iter fn rope = + iteri (fun i c -> (fn c)) rope + +let map fn rope = + let buf = Bytes.create (length rope) in + iteri (fun i c -> Bytes.set buf i (fn c)) rope; + from_string (Bytes.unsafe_to_string buf) + +let mapi fn rope = + let buf = Bytes.create (length rope) in + iteri (fun i c -> Bytes.set buf i (fn i c)) rope; + from_string (Bytes.unsafe_to_string buf) + +let gets rope i j = + let buf = Bytes.create (j - i) + and it = Iter.make rope 0 in + while (Iter.has_next it) && ((Iter.pos it) <= j) do + Bytes.set buf ((Iter.pos it) - i) (Iter.get it); + Iter.incr it; + done; + Bytes.unsafe_to_string buf + +let to_string rope = + gets rope 0 (length rope) -- 2.52.0