-
Notifications
You must be signed in to change notification settings - Fork 81
TextObject
(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
.
pub enum Kind {
Char,
Line(Anchor),
Word(Anchor),
}
Text objects in Iota presently fall into one of three Kind
s,
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".
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.
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.
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 TextObject
s 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.