This is a document with information I found most useful as a new contributor to TextMate - basic stuff a new contributor is likely to get in contact with. It's of course incomplete, sparse, can be not well worded and even contain mistakes. But someone had to make the first shot :-)
7.8 KiB
Developer Documentation
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).
Model
oak::basic_tree_t
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): thenitis 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): thenitis 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):itis 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.
ng::detail::storage_t
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).
ng::buffer_t
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
ng::indexed_map_t
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_boundandupper_boundmethods - 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 givenpositionmap.set(position, value): set avalueto 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 thepositionwas beyond the total range currently represented by the map, then the total range is appropriately extended and thevalueis 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 lengthnewLengthand making valid for this whole range the value that was previously valid at positionto.
ng::layout_t
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.
GUI
OakTextView.framework
The OakTextView.framework contains the components you work most with when using TextMate:
OakTextView: the text view itself; it uses ang::buffer_ttogether withng::layout_tto 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,GutterViewandOTVStatusBarand makes them work together