Skip to content
masukomi (a.k.a. Kay Rhodes) edited this page Oct 26, 2016 · 2 revisions

Text Objects

(Please note that everything in this document is fairly experimental, not everything is necessarily implemented in Iota (and if it has been implemented, there are almost certainly bugs), and everything is very much subject to change without notice. Feedback and criticism is very welcome.)

While editing text it is frequently useful to refer to constructs such as lines or words, or in the case of editing a programming language, expressions or blocks. To facilitate this, Iota provides the textobject module and related constructs.

In Iota, TextObject is a struct composed of three main components: Anchor, Kind, and Offset.

Kind

pub enum Kind {
  Char,
  Line(Anchor),
  Word(Anchor),
}

Text objects in Iota presently fall into one of three Kinds, currently Characters, Lines, and Words. These are all pretty self explanatory, with the possible exception of Word, which is presently defined as a sequence of non-whitespace characters. Eventually there will be a need to allow adjusting that definition to suit different programming languages or user preferences.

In the future, Kind may be extended to programming language specific constructs such as "expression" or "block", as well as more dynamic dynamic concepts like "search result".

Anchor

pub enum Anchor {
  Before,
  Start,
  End,
  After,
  Same
}

You will have noticed the Anchor field in the above definition of Kind. Anchor is used to refer to a specific point on an object. If the user wishes to move forward one word, it's easy enough to find the next word object, but where on that word should the cursor be placed? The first character of the word makes sense, but what if the user wants to delete the next word? In that case, we should remove everything up to and including the last character of the word. Anchor is how we can separate these cases.

Anchor::Before is the very last index before the object that cannot be considered part of the object.

Anchor::Start is the very first index that is considered part of the object.

Anchor::End is the very last index that is considered part of the object.

Anchor::After is the very first index after the object that cannot be considered part of of object.

To give an example in the case of a Word object:

                - - - - - - -
                _ _ _ f o o _
                    B S   E A

(where B=Before, S=Start, E=End, and A=After)

Line objects provide a slightly more subtle case (working with the second line):

                - - - - - - - - - - - - - - - - - -
                l i n e 1 \nl i n e 2 \nl i n e 3 \n
                          B S         E A

Note that "before" a line is the end of the previous line, and "after" a line is the start of the next line.

In some cases, these anchors may "overlap", for example, the index "before" the first line in a buffer is the same as the "start" of the first line. Likewise with "after" and "end" of the last line.

Anchor::Same is the odd one out, and refers to "The same index as within the current object of the same Kind", a slightly imprecise idea used to preserve the current cursor column when moving up or down lines. If the cursor is currently located at the 10th column, moving down one line should put the cursor at either the 10th column of the next line or the end of the next line, whichever is nearest.

Offset

pub enum Offset {
  Absolute(usize),
  Backward(usize, Mark),
  Forward(usize, Mark),
}

Offset is used to indicate which specific instance of a Kind we are referring to.

Absolute(n) is always the nth of a Kind from the start of the buffer; the 10th Line, the 23rd Word, the 332nd Character.

Backward(n, mark) is the nth of a Kind before mark; one Line before the cursor, 10 Characters after point, etc.

Forward(n, mark) is the nth of a Kind after mark; one Word after the cursor, 2 Lines after point, etc.

TextObject

pub struct TextObject {
  pub kind: Kind,
  pub offset: Offset
}

Putting it all together, a TextObject is simply a Kind (what we're dealing with) and an Offset (which one we're talking about).

Now that we have a TextObject, the buffer provides methods to do things like "find the index of an object", or "set a mark to the location of an object", which is how Iota now handles (or will soon handle, see the cmd-builder branch for details) cursor movement.

Presently, much of the logic for locating TextObjects is located in the buffer module. This means that the practical definition of what, for example, Kind::Word means is located in a different module than the one in which Kind::Word is actually defined. Eventually, we intend to refactor things so that all Word related logic is in once place (the textobject module), which should help facilitate future work to allow more customization in this area.

Clone this wiki locally