the-sett / elm-gap-buffer / GapBuffer

Implements an efficient buffer for text editing.

Make a Buffer


type alias GapBuffer a b =
{ head : Array a
, zip : Maybe { val : b
, at : Basics.Int
, tail : Array a }
, length : Basics.Int
, toFocus : a -> b
, fromFocus : Maybe a -> b -> a 
}

A GapBuffer model.

empty : (a -> b) -> (Maybe a -> b -> a) -> GapBuffer a b

Creates an empty GapBuffer.

fromArray : (a -> b) -> (Maybe a -> b -> a) -> Array a -> GapBuffer a b

Creates a GapBuffer from an Array.

fromList : (a -> b) -> (Maybe a -> b -> a) -> List a -> GapBuffer a b

Creates a GapBuffer from a List.

Query

get : Basics.Int -> GapBuffer a b -> Maybe a

Extracts the element at the specified index in the GapBuffer. If the GapBuffer does not hold data for this index, Nothing is returned.

isEmpty : GapBuffer a b -> Basics.Bool

Checks if a GapBuffer is empty.

length : GapBuffer a b -> Basics.Int

Gets the number of element in the GapBuffer.

slice : Basics.Int -> Basics.Int -> GapBuffer a b -> Array a

Extracts a slice of data from the buffer, between the from and to indices specified.

If these indicies go outside the range of the GapBuffer, data from the actual available range will be returned.

If you are iterating over the contents of the buffer, to render a UI for example, there is no need to copy the contents into an intermediate Array. You can iterate directly over a region of the buffer using the foldlSlice function instead.

currentFocus : GapBuffer a b -> Maybe ( Basics.Int, b )

If the buffer has a current focus point, its position and the data element at it are returned.

Manipulate

getFocus : Basics.Int -> GapBuffer a b -> ( GapBuffer a b, Maybe b )

Gets the value at the specified focus of the GapBuffer. If the GapBuffer was already focussed at a different index, that index will be de-focussed, and the focus shifted to the specified index.

Note that de-focussing and re-focussing the GapBuffer will use the toFocus and fromFocus functions that were specified when creating the buffer.

setFocus : Basics.Int -> b -> GapBuffer a b -> GapBuffer a b

Sets the value as the focus of the GapBuffer. If the GapBuffer was already focussed at a different index, that index will be de-focussed, and the focus shifted to the specified index.

Note that de-focussing and re-focussing the GapBuffer will use the toFocus and fromFocus functions that were specified when creating the buffer.

insertAtFocus : Basics.Int -> b -> GapBuffer a b -> GapBuffer a b

Inserts an entry at the specified index into the buffer.If the GapBuffer was already focussed at a different index, that index will be de-focussed, and the focus shifted to the specified index. Entries at higher indexes will now have an index one higher than before.

Note that de-focussing and re-focussing the GapBuffer will use the toFocus and fromFocus functions that were specified when creating the buffer.

If the index is out of range for the buffer this operation will do nothing.

updateFocus : Basics.Int -> (b -> b) -> GapBuffer a b -> GapBuffer a b

Update the value at the specified focus of the GapBuffer. If the GapBuffer was already focussed at a different index, that index will be de-focussed, and the focus shifted to the specified index.

Note that de-focussing and re-focussing the GapBuffer will use the toFocus and fromFocus functions that were specified when creating the buffer.

focusAt : Basics.Int -> GapBuffer a b -> GapBuffer a b

Focusses the buffer at the specified index. If the GapBuffer was already focussed at a different index, that index will be de-focussed, and the focus shifted to the specified index.

Note that de-focussing and re-focussing the GapBuffer will use the toFocus and fromFocus functions that were specified when creating the buffer.

If the index is out of range for the buffer this operation clamp the index to the available range. Negative values will set the focus to zero. Values greater than the buffer length will be off the end of the buffer, so no focus will be set.

delete : Basics.Int -> GapBuffer a b -> GapBuffer a b

Deletes the specified index from the buffer. If the GapBuffer was already focussed at a different index, that index will be de-focussed, and the focus shifted to the specified index. Entries at higher indexes will now have an index one less than before.

Note that de-focussing and re-focussing the GapBuffer will use the toFocus and fromFocus functions that were specified when creating the buffer.

If the index is out of range for the buffer this operation will do nothing.

advanceFocus : (b -> Maybe b) -> GapBuffer a b -> Maybe (GapBuffer a b)

Advances the focus by 1. If there is no focus becuase it fell off the end this returns nothing.

A function is supplied that can optionally map the entry at the current focus into a new entry. If this mapping returns Nothing, then the focus is advanced. If this mapping returns a value, the focus is not advanced and the new entry replaces the one at the current focus. This feature can be used to stack advanceFocus functions together over GapBuffers of GapBuffers of ...

Rippling


type RippleOutcome
    = Done
    | StoppedAt Basics.Int

The possible outcomes of ripple operations.

A ripple is an operation which can complete all the way to the end of the buffer, or can be stopped when it reaches a certain point from which it can be continued.

ripple : Basics.Int -> Basics.Int -> (a -> a -> Basics.Bool) -> GapBuffer a b -> ( GapBuffer a b, RippleOutcome )

Rippling runs the buffer focus between a 'from' index and a 'to' index. Each entry encountered is extracted from the buffer and re-merged into the buffer by passing it through its 'toFocus' and 'fromFocus' functions.

This can be used to apply an operation such as formatting the text correctly, but only within a window of the buffer. For example, in an editor working on 1 million lines, a change on an earlier line my change the formatting on later lines, but we only want to apply the formatting on lines that the user can currently see, or the operation will be too slow.

Since we know where a ripply operation ended, it can be re-run from that point. Or ripple operations can be cancelled if they are overtaking by other ripple operations.

Iterate

foldlSlice : (Basics.Int -> a -> acc -> acc) -> acc -> Basics.Int -> Basics.Int -> GapBuffer a b -> acc

Iterates forward over a region of the buffer.

This is the most efficient way to extract and map data from the buffer. For example, you would use this when rendering the visible contents of a GapBuffer to Html. The implementation does not create intermediate data structures to hold the extracted elements, and it only iterates over the range you specify.

foldrSlice : (Basics.Int -> a -> acc -> acc) -> acc -> Basics.Int -> Basics.Int -> GapBuffer a b -> acc

Iterates backward over a region of the buffer.

This is the most efficient way to extract and map data from the buffer. For example, you would use this when rendering the visible contents of a GapBuffer to Html. The implementation does not create intermediate data structures to hold the extracted elements, and it only iterates over the range you specify.

indexedFoldl : (Basics.Int -> a -> acc -> acc) -> acc -> GapBuffer a b -> acc

Iterates forward over the whole buffer.

This is the most efficient way to extract and map data from the buffer. For example, you would use this when rendering the visible contents of a GapBuffer to Html. The implementation does not create intermediate data structures to hold the extracted elements, and it only iterates over the range you specify.

indexedFoldr : (Basics.Int -> a -> acc -> acc) -> acc -> GapBuffer a b -> acc

Iterates backward over the whole buffer.

This is the most efficient way to extract and map data from the buffer. For example, you would use this when rendering the visible contents of a GapBuffer to Html. The implementation does not create intermediate data structures to hold the extracted elements, and it only iterates over the range you specify.