TextMate is written in Objective-C++: the low-level data structures (mostly non-GUI specific code) are written in C++, the GUI part in Objective-C++ (the C++ part here coming from the need to use the low-level C++ data structures).
This is basically a balanced binary indexed tree. I.e. it has 2 specifics:
- it is balanced: this is achieved by using an AA-tree
- it is a binary indexed tree: you have O(1) access to
std::accumulate(tree.begin(), it, key_type(), [](key_type const& key, value_type const& value) -> key_type { return key + value.key })
for anyit
It is a template parameterized by 2 types:
key_type
: this type has to implement the+
and-
operations, and also a default constructor that yields the identity element w.r.t. the operations (i.e. for anykey_type key
, it must hold thatkey + key_type() == key_type() + key == key - key_type() == key_type() - key
).value
: the value stored by each tree node
When iterating over the values in the tree, the iterator's value type (i.e. what you get from *it
) has 3 members:
offset
: result ofstd::accumulate(tree.begin(), it, key_type(), [](key_type const& key, value_type const& value) -> key_type { return key + value.key })
key
: simply a reference to the key user stored in the nodevalue
: simply a reference the value the user stored in the node
Note that for the key
and value
members, a reference to the actual object is stored. While it's not a surprise that you can modify the it->value
, what's really interesting is that you can modify an it->key
and then call tree->update_key(it)
to make the tree recalculate the offset
information for the whole tree (takes O(log(N))).
Unlike the standard associative containers which have a comparison object inherent in their type (as a template parameter), with oak::basic_tree_t
you pass a comparison object directly to the methods working with comparisons. These are lower_bound
, upper_bound
and find
.
Also unlike the standard comparison object which takes 2 parameters (and models a <
relation), here the comparison object takes 3 parameters, all of type key_type
: search
, offset
and key
. The search
parameter is the one passed to one of the 3 comparison methods above. The offset
and key
parameters correspond to an iterator's value_type. The object returns a value in the set {-1, 0, 1}: -1 means iterator's node is "less" than search, 0 means it is "equal" and 1 means it is "more". Analogically to the standard associative containers then, the comparison methods return the following:
it = tree.lower_bound(search, comp)
: thenit
is the first node for whichcomp(search, it->offset, it->key) != 1
(i.e. the first node that is "not less than"search
)it = tree.upper_bound(search, comp)
: thenit
is the first node for whichcomp(search, it->offset, it->key) == -1
(i.e. the first node that is "more than"search
)it = tree.find(search, comp)
:it
is the node for whichcomp(search, it->offset, it->key) == 0
, ortree->end()
if no such node exists (i.e. the first node that is "equal" tosearch
)
oak::basic_tree_t
is a very important data structure in TextMate as it is used in various places and contexts, including text storage, layout, to implement ng::indexed_map_t
(see later), etc.
This is a type used to store a (potentially big) sequence of bytes using chunks of memory stored in oak::basic_tree_t
. Think of it as an efficient std::string :-). More specifically, inserting and deleting a string in a storage representing a string of length N
is better than O(N)
.
This type builds on top of the raw character storage provided by ng::detail::storage_t
and provides some semantical services for the text stored within it:
- lines: it detects newline characters and provides a way to translate between position in text and the line and column number
- spelling: it checks the text for spelling errors and provides a way to retrieve them
- scopes: it parses the text (using one or more bundles) and assigns one or more scopes to some ranges of the text; these usually correspond to various markup or syntax parts of the language the text is written in
- marks: TODO
This data structure is what it is called:
- it's a map: behaves like std::map<ssize_t, ValT> in that it provides the
find
,lower_bound
andupper_bound
methods - it's indexed: you get O(log(N)) access to its n-th element for any
n
It is implemented as an oak::basic_tree_t
which dictates its key_type
and provides some services built around the key_type
members:
number_of_children
: this enables the efficient indexing of nodes, i.e. getting the n-th iterator is O(log(N)) (instead of O(N) in the generaloak::basic_tree_t
)length
: this is used for thestd::map
-like functionality.
This structure basically provides a segment tree where a value is valid for a specific ssize_t
range: ng::indexed_map_t::iterator it
represents a value it->value
that is valid in the semi-open range [it.base()->offset.length, it.base()->offset.length + it.base()->key.length)
, and also that it is the it->offset.number_of_children
-th value in the indexed map (you can get this more nicely and reliably as it->index()
which also works if it == map.end()
).
You can also work with this segment map in the following ways:
map.upper_bound(position)
: find a value valid at a givenposition
map.set(position, value)
: set avalue
to be valid for a range ending atposition
. The value that was valid at this position before the setting is then valid only after theposition
(the end of the range for the previously valid value remains unchanged). If theposition
was beyond the total range currently represented by the map, then the total range is appropriately extended and thevalue
is valid from the end of the previous total range untilposition
.map.remove(position)
: remove a value valid exactly untilposition
, extending the range of the next value to be valid for the range of the removed value. If this is the last value, then the total range represented by the map gets reduced. Note that if you want to remove a value valid atposition
(as opposed to a value valid exactly untilposition
), you have to do it likemap.remove(map.upper_bound(position)->second)
map.replace(from, to, newLength, bindRight)
: this models replacing a range(from, to)
with a new one of lengthnewLength
and making valid for this whole range the value that was previously valid at positionto
.
This data structure holds a ng::buffer_t
and a viewport width and height and provides services for calculating the layout of text, i.e. how the semantical lines (divided by newline character) are divided into visual softlines (induced by wrapping the text at the viewport width and folding), what is the interline spacing, font size etc.. It provides a way to retrieve various geometrical characteristics of ranges of text. Finally, it can use all this information to draw portions of the buffer into a CGContext.
The OakTextView.framework
contains the components you work most with when using TextMate:
OakTextView
: the text view itself; it uses ang::buffer_t
together withng::layout_t
to display text, and among other things, implements input handling consistent with Cocoa's key bindings mechanism.GutterView
: the view left to the text view containing line numbers, folding marks etc.OTVStatusBar
: the bar below the text view containing e.g. current bundle, symbol etc.OakDocumentView
: a view that contains as its subviews anOakTextView
,GutterView
andOTVStatusBar
and makes them work together