Infinite loop in gtk_source_view_extend_selection in presence of invisible text (3.24)
When invisible text is present in the buffer, gtk_source_view_extend_selection
(and some other functions, like move_cursor) can go into an infinite loop involving _gtk_source_iter_forward_visible_word_end
_gtk_source_iter_backward_extra_natural_word_start
.
Here's a minimal example to trigger the behaviour (try ctrl-right/left to move across the word, or just doubleclicking anything in the buffer; using gtkmm because it has less boilerplate to set up an application), tested against 3.24 only:
#include <gtkmm.h>
#include <gtksourceview/gtksource.h>
int main(int argc, char *argv[])
{
auto app =
Gtk::Application::create(argc, argv,
"org.gtkmm.examples.base");
Gtk::Window window;
window.set_default_size(200, 200);
GtkWidget *w = gtk_source_view_new();
GtkTextBuffer *b = gtk_text_view_get_buffer(GTK_TEXT_VIEW(w));
gtk_text_buffer_set_text(b,"abc",3);
GtkTextTag *invis = gtk_text_buffer_create_tag(b,"invis","invisible",true,NULL);
GtkTextIter zero, one;
gtk_text_buffer_get_iter_at_offset(b,&zero,0);
gtk_text_buffer_get_iter_at_offset(b,&one,1);
gtk_text_buffer_apply_tag(b,invis,&zero,&one);
gtk_container_add(GTK_CONTAINER(window.gobj()), w);
window.show_all();
return app->run(window);
}
While variants of this example seem to require the invisible text to be at the beginning of the buffer, I am pretty certain I have observed the same infinite loop in more complex circumstances when this was not the case (in this). The issue does not occur with plain GtkTextView, and I managed to sidestep all crashes in my code by overwriting the extend_selection and move_cursor function pointers in the class with GtkTextView's.
Typical stack trace fragment:
#0 0x00007ffff690aefc in _gtk_text_btree_char_is_invisible (iter=iter@entry=0x7fffffffbcd0) at ../../../../gtk/gtktextbtree.c:2522
#1 0x00007ffff6927ea8 in find_visible_by_log_attrs (iter=iter@entry=0x7fffffffbfc0, func=func@entry=0x7ffff6922e60 <find_word_start_func>, forward=forward@entry=0)
at ../../../../gtk/gtktextiter.c:3215
#2 0x00007ffff6927fde in gtk_text_iter_backward_visible_word_start (iter=iter@entry=0x7fffffffbfc0) at ../../../../gtk/gtktextiter.c:3372
#3 0x00007ffff55d5c0c in _gtk_source_iter_backward_extra_natural_word_start (iter=iter@entry=0x7fffffffbfc0) at gtksourceiter.c:245
#4 0x00007ffff55d604d in _gtk_source_iter_forward_visible_word_end (iter=iter@entry=0x7fffffffc090) at gtksourceiter.c:379
#5 0x00007ffff55d66ca in _gtk_source_iter_inside_word (iter=0x7fffffffc860) at gtksourceiter.c:569
#6 0x00007ffff55d66ca in _gtk_source_iter_inside_word (iter=0x7fffffffc860) at gtksourceiter.c:552
#7 0x00007ffff55d677e in _gtk_source_iter_extend_selection_word (location=location@entry=0x7fffffffc810, start=start@entry=0x7fffffffc860, end=end@entry=0x7fffffffc8b0) at gtksourceiter.c:587
#8 0x00007ffff55c252e in gtk_source_view_extend_selection (text_view=<optimized out>, granularity=<optimized out>, location=0x7fffffffc810, start=0x7fffffffc860, end=0x7fffffffc8b0) at gtksourceview.c:2279