diff options
| author | Federico Igne <undyamon@disroot.org> | 2024-01-11 19:31:31 +0100 |
|---|---|---|
| committer | Federico Igne <undyamon@disroot.org> | 2024-01-11 19:31:31 +0100 |
| commit | 055c743c55bde27f4475d3434c26d8383c0c3ea1 (patch) | |
| tree | aabf2173a9995f5795da86d5676181b62fee0e9e /lib/zipper.mli | |
| parent | 416c56656af65d656f637dc8c8fdb62d0ba03e29 (diff) | |
| download | sandy-055c743c55bde27f4475d3434c26d8383c0c3ea1.tar.gz sandy-055c743c55bde27f4475d3434c26d8383c0c3ea1.zip | |
bulk: add PoC of vim-like modular editor
Diffstat (limited to 'lib/zipper.mli')
| -rw-r--r-- | lib/zipper.mli | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/lib/zipper.mli b/lib/zipper.mli new file mode 100644 index 0000000..c3c79a6 --- /dev/null +++ b/lib/zipper.mli | |||
| @@ -0,0 +1,258 @@ | |||
| 1 | open Base | ||
| 2 | |||
| 3 | (** Linear zippers. | ||
| 4 | |||
| 5 | A zipper represents {b a paused traversal} of a certain data | ||
| 6 | structure. A linear zipper of type ['a zipper] represents a zipper | ||
| 7 | over a sequence of ['a] elements. | ||
| 8 | |||
| 9 | One can access the {b focused element} of the zipper with [focus]. | ||
| 10 | The focus of a zipper is also known as the the {b cursor} of the | ||
| 11 | zipper, and can be moved back and forth with [left] and [right]. | ||
| 12 | |||
| 13 | Elements can be added or removed at the cursor's position with | ||
| 14 | [push] and [pop] operations. Variants are available, e.g., to act | ||
| 15 | before the cursor, with suffix [_before], or after the cursor, with | ||
| 16 | suffix [_after]. *) | ||
| 17 | |||
| 18 | type !+'a zipper | ||
| 19 | (** A zipper is represented as a {b pair} of sequences of type ['a]. The | ||
| 20 | focus of the zipper is the element at the head of the second | ||
| 21 | sequence, if any. | ||
| 22 | |||
| 23 | Note that the first sequence is reversed. Given a sequence | ||
| 24 | [[1; 2; 3; 4; {5}; 6; 7]], where we represent the cursor using | ||
| 25 | curly braces [{_}], its representation as a zipper is | ||
| 26 | [([4; 3; 2; 1], [5; 6; 7])]. *) | ||
| 27 | |||
| 28 | type !+'a t = 'a zipper | ||
| 29 | (** An alias for the ['a zipper] type. *) | ||
| 30 | |||
| 31 | val empty : 'a zipper | ||
| 32 | (** Return an empty zipper *) | ||
| 33 | |||
| 34 | val before : 'a zipper -> 'a Sequence.t | ||
| 35 | (** Return the sequence before the cursor *) | ||
| 36 | |||
| 37 | val after : 'a zipper -> 'a Sequence.t | ||
| 38 | (** Return the sequence after the cursor *) | ||
| 39 | |||
| 40 | val focus : 'a zipper -> 'a option | ||
| 41 | (** Return the focus of the zipper, if any. *) | ||
| 42 | |||
| 43 | val focus_or : 'a zipper -> default:'a -> 'a | ||
| 44 | (** Return the focus of the zipper, or a user-provided default, otherwise. *) | ||
| 45 | |||
| 46 | val history : 'a zipper -> 'a Sequence.t | ||
| 47 | (** Returns the sequence of elements [pop]ped so far from the zipper. *) | ||
| 48 | |||
| 49 | val is_far_left : 'a zipper -> bool | ||
| 50 | (** Return whether the cursor is at the beginning of the zipper. *) | ||
| 51 | |||
| 52 | val is_far_right : 'a zipper -> bool | ||
| 53 | (** Return whether the cursor is at the end of the zipper. *) | ||
| 54 | |||
| 55 | val is_empty : 'a zipper -> bool | ||
| 56 | (** Return whether the zipper is empty. *) | ||
| 57 | |||
| 58 | val left_length : 'a zipper -> int | ||
| 59 | (** Return the number of elements before the cursor. *) | ||
| 60 | |||
| 61 | val right_length : 'a zipper -> int | ||
| 62 | (** Return the number of elements after the cursor. *) | ||
| 63 | |||
| 64 | val length : 'a zipper -> int | ||
| 65 | (** Return the length of the zipper. *) | ||
| 66 | |||
| 67 | (** {1 Moving the cursor} *) | ||
| 68 | |||
| 69 | val left : 'a zipper -> 'a zipper | ||
| 70 | (** Move the cursor one step to the left, if possible. | ||
| 71 | Calling [left z], | ||
| 72 | |||
| 73 | - if [z] is [([3; 2; 1], [4; 5])], the result is [([2; 1], [3; 4; 5])], | ||
| 74 | - if [z] is [([], [1; 2; 3])], the result is [([], [1; 2; 3])]. | ||
| 75 | *) | ||
| 76 | |||
| 77 | val left_while : ('a -> bool) -> 'a zipper -> 'a zipper | ||
| 78 | (** [left_while f z] moves the cursor in [z] to the left as long as the | ||
| 79 | predicate [f] is [true] when applied to the focus, or the left end | ||
| 80 | of the zipper is reached. *) | ||
| 81 | |||
| 82 | val far_left : 'a zipper -> 'a zipper | ||
| 83 | (** Move the cursor to the left, as much as possible. *) | ||
| 84 | |||
| 85 | val right : 'a zipper -> 'a zipper | ||
| 86 | (** Move the cursor one step to the right, if possible. | ||
| 87 | Calling [right z], | ||
| 88 | |||
| 89 | - if [z] is [([3; 2; 1], [4; 5])], the result is [([4; 3; 2; 1], [5])], | ||
| 90 | - if [z] is [([1; 2; 3], [])], the result is [([1; 2; 3], [])]. | ||
| 91 | *) | ||
| 92 | |||
| 93 | val right_while : ('a -> bool) -> 'a zipper -> 'a zipper | ||
| 94 | (** [right_while f z] moves the cursor in [z] to the right as long as | ||
| 95 | the predicate [f] is [true] when applied to the focus, or the right | ||
| 96 | end of the zipper is reached. *) | ||
| 97 | |||
| 98 | val far_right : 'a zipper -> 'a zipper | ||
| 99 | (** Move the cursor to the right, as much as possible. *) | ||
| 100 | |||
| 101 | val goto : int -> 'a zipper -> 'a zipper | ||
| 102 | (** Move the cursor to a specific (absolute) position in the zipper. | ||
| 103 | Depending on the current position, it either moves the cursor | ||
| 104 | forward or backwards, without crossing the zipper boundaries. *) | ||
| 105 | |||
| 106 | (** {1 Changes at the cursor} *) | ||
| 107 | |||
| 108 | (** Zippers provide [O(1)] operations performed at the cursor position. | ||
| 109 | This involve [push]ing and [pop]ping elements before and after the | ||
| 110 | cursor. *) | ||
| 111 | |||
| 112 | val pop_after : 'a zipper -> 'a zipper | ||
| 113 | (** Remove the element at the cursor position, if any, and return the | ||
| 114 | modified zipper. Calling [pop_after z], | ||
| 115 | |||
| 116 | - if [z] is [([3; 2; 1], [4; 5])], the result is [([3; 2; 1], [5])], | ||
| 117 | - if [z] is [([1; 2; 3], [])], the result is [([1; 2; 3], [])]. | ||
| 118 | *) | ||
| 119 | |||
| 120 | val pop_before : 'a zipper -> 'a zipper | ||
| 121 | (** Remove the element before the cursor, if any, and return the | ||
| 122 | modified zipper. Calling [pop_before z], | ||
| 123 | |||
| 124 | - if [z] is [([3; 2; 1], [4; 5])], the result is [([2; 1], [4, 5])], | ||
| 125 | - if [z] is [([], [1; 2; 3])], the result is [([], [1; 2; 3])]. | ||
| 126 | *) | ||
| 127 | |||
| 128 | val pop : 'a zipper -> 'a zipper | ||
| 129 | (** [pop] is an alias for [pop_before]. *) | ||
| 130 | |||
| 131 | val push_after : 'a -> 'a zipper -> 'a zipper | ||
| 132 | (** Insert an element after the cursor. | ||
| 133 | Calling [push_after 0 z], if [z] is [([3; 2; 1], [4; 5])], | ||
| 134 | the result is [([3; 2; 1], [0; 4, 5]))], *) | ||
| 135 | |||
| 136 | val push_before : 'a -> 'a zipper -> 'a zipper | ||
| 137 | (** Insert an element before the cursor. Return the modified zipper. | ||
| 138 | Calling [push_before 0 z], if [z] is [([3; 2; 1], [4; 5])], | ||
| 139 | the result is [([0; 3; 2; 1], [4, 5]))]. *) | ||
| 140 | |||
| 141 | val push : 'a -> 'a zipper -> 'a zipper | ||
| 142 | (** [push] is an alias for [push_before]. *) | ||
| 143 | |||
| 144 | val split : 'a zipper -> 'a zipper * 'a zipper | ||
| 145 | (** [split z] splits the zipper in two. [([3; 2; 1], [4; 5])] becomes | ||
| 146 | [([3; 2; 1], []), ([], [4; 5])]. *) | ||
| 147 | |||
| 148 | (** {1 Consuming zippers} *) | ||
| 149 | |||
| 150 | (** Since zippers are based on sequences, iterating over zippers | ||
| 151 | terminates only when both sequences are finite. | ||
| 152 | Unless otherwise stated, these functions will iterate on the | ||
| 153 | elements before the cursor, first. *) | ||
| 154 | |||
| 155 | val iter_before : ('a -> unit) -> 'a zipper -> unit | ||
| 156 | (** [iter_before f z] will call [f x] for all [x], elements before the | ||
| 157 | cursor in [z].*) | ||
| 158 | |||
| 159 | val iter_after : ('a -> unit) -> 'a zipper -> unit | ||
| 160 | (** [iter_after f z] will call [f x] for all [x], elements after the | ||
| 161 | cursor in [z].*) | ||
| 162 | |||
| 163 | val iter : ('a -> unit) -> 'a zipper -> unit | ||
| 164 | (** [iter f z] is equivalent to [iter_before f z; iter_after f z] *) | ||
| 165 | |||
| 166 | val for_all : ('a -> bool) -> 'a zipper -> bool | ||
| 167 | (** [for_all p z] tests whether a predicate [p] is [true] for all | ||
| 168 | elements in a zipper. *) | ||
| 169 | |||
| 170 | val exists : ('a -> bool) -> 'a zipper -> bool | ||
| 171 | (** [exists p z] tests whether at least one element in the zipper is | ||
| 172 | [true] according to the predicate [p]. *) | ||
| 173 | |||
| 174 | val find_before : ('a -> bool) -> 'a zipper -> 'a option | ||
| 175 | (** [find_before p z] will return the first element before the cursor in | ||
| 176 | [z] satisfying the predicate [p], if any. *) | ||
| 177 | |||
| 178 | val find_after : ('a -> bool) -> 'a zipper -> 'a option | ||
| 179 | (** [find_after p z] will return the first element after the cursor in | ||
| 180 | [z] satisfying the predicate [p], if any. *) | ||
| 181 | |||
| 182 | (** {1 Transforming zippers} *) | ||
| 183 | |||
| 184 | (** Since zippers are based on sequences, the functions in this section | ||
| 185 | are lazy; i.e., resulting elements of the zipper are computed only | ||
| 186 | when demanded. *) | ||
| 187 | |||
| 188 | val map_before : ('a -> 'a) -> 'a zipper -> 'a zipper | ||
| 189 | (** Map a function over all elements before the cursor. *) | ||
| 190 | |||
| 191 | val map_after : ('a -> 'a) -> 'a zipper -> 'a zipper | ||
| 192 | (** Map a function over all elements after the cursor. *) | ||
| 193 | |||
| 194 | val map_focus : ('a -> 'a) -> 'a zipper -> 'a zipper | ||
| 195 | (** Map a function over the element focused by the cursor, if any. *) | ||
| 196 | |||
| 197 | val map : ('a -> 'a) -> 'a zipper -> 'a zipper | ||
| 198 | (** Map a function over all elements of a zipper. *) | ||
| 199 | |||
| 200 | val mapi_before : (int -> 'a -> 'a) -> 'a zipper -> 'a zipper | ||
| 201 | (** [mapi_before] is analogous to {!Zipper.map_before}, but the function | ||
| 202 | takes an index and an element. | ||
| 203 | The index indicates the distance of an element from the cursor. *) | ||
| 204 | |||
| 205 | val mapi_after : (int -> 'a -> 'a) -> 'a zipper -> 'a zipper | ||
| 206 | (** [mapi_after] is analogous to {!Zipper.map_after}, but the function | ||
| 207 | takes an index and an element. | ||
| 208 | The index indicates the distance of an element from the cursor. *) | ||
| 209 | |||
| 210 | val mapi : (int -> 'a -> 'a) -> 'a zipper -> 'a zipper | ||
| 211 | (** [mapi] is analogous to {!Zipper.map}, but the function takes an | ||
| 212 | index and an element. | ||
| 213 | The index indicates the distance of an element from the cursor. *) | ||
| 214 | |||
| 215 | val filter_before : ('a -> bool) -> 'a zipper -> 'a zipper | ||
| 216 | (** [filter_before p z] filters the elements before the cursor in a | ||
| 217 | zipper [z] according to a predicate [p], i.e., keeping the elements | ||
| 218 | that satisfy the predicate. *) | ||
| 219 | |||
| 220 | val filter_after : ('a -> bool) -> 'a zipper -> 'a zipper | ||
| 221 | (** [filter_after p z] filters the elements after the cursor in a | ||
| 222 | zipper [z] according to a predicate [p], i.e., keeping the elements | ||
| 223 | that satisfy the predicate. *) | ||
| 224 | |||
| 225 | val filter : ('a -> bool) -> 'a zipper -> 'a zipper | ||
| 226 | (** [filter p z] filters the elements of the zipper [z] according to a | ||
| 227 | predicate [p], i.e., keeping the elements that satisfy the | ||
| 228 | predicate. *) | ||
| 229 | |||
| 230 | val context_before : int -> 'a zipper -> 'a zipper | ||
| 231 | (** [context_before n z] will limit the zipper [z] to [n] elements before | ||
| 232 | the cursor. *) | ||
| 233 | |||
| 234 | val context_after : int -> 'a zipper -> 'a zipper | ||
| 235 | (** [context_after n z] will limit the zipper [z] to [n] elements after | ||
| 236 | the cursor. *) | ||
| 237 | |||
| 238 | val context : b:int -> ?a:int -> 'a zipper -> 'a zipper | ||
| 239 | (** [context ~b ~a z] will limit the zipper [z] to [b] elements before | ||
| 240 | the cursor and [a] elements after the cursor. When [a] is not | ||
| 241 | provided, it defaults to [b]. *) | ||
| 242 | |||
| 243 | val clear_history : 'a zipper -> 'a zipper | ||
| 244 | (** Clear the history of the zipper. See {!Zipper.history}. *) | ||
| 245 | |||
| 246 | (** {1 Zippers and sequences} *) | ||
| 247 | |||
| 248 | val of_seq : 'a Sequence.t -> 'a zipper | ||
| 249 | (** Turn a sequence into a zipper with the cursor at the beginning. *) | ||
| 250 | |||
| 251 | val to_seq : 'a zipper -> 'a Sequence.t | ||
| 252 | (** Return the zipper as a sequence. | ||
| 253 | Calling [to_seq z], with [z] being [([3; 2; 1], [4; 5])], results in | ||
| 254 | [[1; 2; 3; 4; 5]]. *) | ||
| 255 | |||
| 256 | val window : from:int -> len:int -> 'a zipper -> 'a Sequence.t | ||
| 257 | (** [windows from len z] returns a sequence containing [len] elements | ||
| 258 | starting from [from]. *) | ||
