GNOME Builder: Improving word completion GSoC final report - I- 3 mins
So, finally I got a chance to hook things into GNOME-Builder tree and change the CompletionProvider namespace from GtkSourceVimWordsCompletion to IdeWordCompletion*
If you want to just try out the feature; fetch wip/uajain/word-completion from Builder git repository and build it. Next step is to ensure that you are in “Vim - Insert Mode” in Builder(Preferences->Keyboard->Vim) and try Ctrl-n / Ctrl-p to word-complete.
Other key bindings (for Emacs/Gedit mode) will be added in future as we need to decide what those key bindings should be.
So, how things are structured ?
IdeWordCompletionProvider - The word completion provider responsible for creating word completion proposals and provide the proposals to GtkSourceCompletionContext
IdeWordCompletionItem - A single proposal; takes in offset (see gtk_text_iter_get_offset) as a parameter so that we can appropriately sort in completion window.
IdeWordCompletionResults - The result set containing all the potential proposals for the given match. IdeWordCompletionResults is reponsible for sorting the proposals based on direction; Ctrl-n(Forward Search), Ctrl-p(Backward Search). It is also reponsible for saving some extra buffer scans by replaying the result set, if possible. We will see this particular section in next post. For this post, I want to keep things simple.
By wiring the key bindings, we can set “direction” property of IdeWordCompletionProvider, which determines whether to sort the proposal in backward or forward order of matches. This property is carried forward to IdeWordCompletionResults in case we are able to replay the results without scanning again.
Wrapping head around GtkTextIter and GtkTextMark :
To extract a match while scanning the buffer, gtk_text_iter_get_text (GtkTextIter *match_start, GtkTextIter *match_end) is used. match_start and match_end comes from gtk_source_search_context_forward_finish2 or _backward_finish2
The buffer scans are asynchronous and GtkTextIter tend to go invalid on buffer changes. So, it’s quite possible that the buffer is changed (user types) while the async operating is still on. Therefore, match_start and match_end will be invalid and will provide a hefty warning on the terminal.
Therefore, in order to extract the match-text, GtkTextMark comes to rescue. Therefore, GtkTextMark preserve the location and can provide us with refreshed GtkTextIters which are not (yet) invalid. GtkTextIters at marks can be obtained by gtk_text_buffer_get_iter_at_mark.
Here is a short snippet of refresh_iters in IdeWordCompletionProvider.
Meanwhile getting the matched texts, IdeWordCompletionProvider also checks if we are not adding any duplicate proposals that probably have been added already. We weed out the duplicate proposal by keeping a hash table around.
Next, I would like to dig into how exactly IdeWordCompletionProvider scans and how we replay the result set to save some extra buffers scans and optimize.
A very short video of the implementation: