// textAreaSpan represents a range of text in a text area. The text area widget
// roughly follows the concept of Piece Chains outline in
// roughly follows the concept of Piece Chains outlined in
// http://www.catch22.net/tuts/neatpad/piece-chains with some modifications.
// This type represents a "span" (or "piece") and thus refers to a subset of the
// text in the editor as part of a doubly-linked list.
@ -53,23 +53,28 @@ var NewLine = "\n"
// three-element int array. The first element is the index of the referenced
// span in the piece chain. The second element is the offset into the span's
// referenced text (relative to the span's start), its value is always >= 0 and
// < span.length. The third elements is the corresponding text parser's state.
// < span.length. The third element is the state of the text parser at that
// position.
//
// A range of text is represented by a span range which is a starting position
// (int array) and an ending position (int array). The starting position
// (3-int array) and an ending position (3-int array). The starting position
// references the first character of the range, the ending position references
// the position after the last character of the range. The end of the text is
// therefore always [3]int{1, 0, 0}, position 0 of the ending sentinel.
//
// Sentinel spans are dummy spans not referring to any text. There are always
// two sentinel spans: the starting span at index 0 of the [TextArea.spans]
// slice and the ending span at index 1.
typetextAreaSpanstruct{
// Links to the previous and next textAreaSpan objects as indices into the
// TextArea.spans slice. The sentinel spans (index 0 and 1) have -1 as their
// previous or next links.
// [TextArea.spans] slice. The sentinel spans (index 0 and 1) have -1 as
// their previous or next links, respectively.
previous,nextint
// The start index and the length of the text segment this span represents.
// If "length" is negative, the span represents a substring of
// TextArea.initialText and the actual length must be its absolute value. If
// it is positive, the span represents a substring of TextArea.editText. For
// [TextArea.initialText] and the actual length is its absolute value. If it
// is positive, the span represents a substring of [TextArea.editText]. For
// the sentinel spans (index 0 and 1), both values will be 0. Others will
// never have a zero length.
offset,lengthint
@ -81,13 +86,17 @@ type textAreaUndoItem struct {
before,afterint// The index of the copied "before" and "after" spans into the "spans" slice.
originalBefore,originalAfterint// The original indices of the "before" and "after" spans.
pos[3]int// The cursor position to be assumed after applying an undo.
continuationbool// If true, this item is a continuation of the previous undo item.
lengthint// The total text length at the time the undo item was created.
continuationbool// If true, this item is a continuation of the previous undo item. It is handled together with all other undo items in the same continuation sequence.
}
// TextArea implements a simple text editor for multi-line text. Multi-color
// text is not supported. Word-wrapping is enabled by default but can be turned
// off or be changed to character-wrapping.
//
// At this point, a text area cannot be added to a [Form]. This will be added in
// the future.
//
// # Navigation and Editing
//
// A text area is always in editing mode and no other mode exists. The following
@ -108,14 +117,15 @@ type textAreaUndoItem struct {
// position. Ignored if wrapping is enabled.
// - Alt-Right arrow: Scroll the page to the right, leaving the cursor in its
// position. Ignored if wrapping is enabled.
// - Alt-B, Ctrl-Left arrow: Jump to the beginning of the current or previous word.
// - Alt-B, Ctrl-Left arrow: Jump to the beginning of the current or previous
// word.
// - Alt-F, Ctrl-Right arrow: Jump to the end of the current or next word.
//
// Words are defined according to Unicode Standard Annex #29. We skip any words
// that contain only spaces or punctuation.
// Words are defined according to [Unicode Standard Annex #29]. We skip any
// words that contain only spaces or punctuation.
//
// Entering a character will insert it at the current cursor location.
// Subsequent characters are moved accordingly. If the cursor is outside the
// Subsequent characters are shifted accordingly. If the cursor is outside the
// visible area, any changes to the text will move it into the visible area. The
// following keys can also be used to modify the text:
//
@ -153,7 +163,9 @@ type textAreaUndoItem struct {
// function, you may use [Box.SetInputCapture] to override the Ctrl-Q key to
// implement copying to the clipboard. Note that using your terminal's /
// operating system's key bindings for copy+paste functionality may not have the
// expected effect as tview will not be able to handle these keys.
// expected effect as tview will not be able to handle these keys. Pasting text
// using your operating system's or terminal's own methods may be very slow as
// each character will be pasted individually.
//
// The default clipboard is an internal text buffer, i.e. the operating system's
// clipboard is not used. If you want to implement your own clipboard (or make
@ -161,9 +173,6 @@ type textAreaUndoItem struct {
// [TextArea.SetClipboard] which provides all the functionality needed to
// implement your own clipboard.
//
// Note that pasting text using your operating system's or terminal's own
// methods will be very slow as each character will be pasted individually.
//
// The text area also supports Undo:
//
// - Ctrl-Z: Undo the last change.
@ -178,6 +187,8 @@ type textAreaUndoItem struct {
// - Left double-click: Select the word under the cursor.
// - Left click while holding the Shift key: Select text.
// - Scroll wheel: Scroll the text.
//
// [Unicode Standard Annex #29]: https://unicode.org/reports/tr29/
typeTextAreastruct{
*Box
@ -215,7 +226,7 @@ type TextArea struct {
// The piece chain. The first two spans are sentinel spans which don't
// reference anything and always remain in the same place. Spans are never
// deleted.
// deleted from this slice.
spans[]textAreaSpan
// Display, navigation, and cursor related fields:
@ -239,13 +250,12 @@ type TextArea struct {
lastHeight,lastWidthint
// The width of the currently known widest line, as determined by
// [extendLines].
// [TextArea.extendLines].
widestLineint
// Text positions and states of the start of lines. Each element is a span
// position (see textAreaSpan) and a state as returned by uniseg.Step(). Not
// all lines of the text may be contained at any time, extend as needed with
// the TextArea.extendLines() function.
// position (see [textAreaSpan]). Not all lines of the text may be contained
// at any time, extend as needed with the [TextArea.extendLines] function.
lineStarts[][3]int
// The cursor always points to the next position where a new character would
@ -300,6 +310,10 @@ type TextArea struct {
// An optional function which is called when the input has changed.
changedfunc()
// An optional function which is called when the position of the cursor or
// the selection has changed.
movedfunc()
}
// NewTextArea returns a new text area. Use [TextArea.SetText] to set the