I’m experimenting with Glib::Regex and Gtk::TextView with Gtk::TextBuffer-s, and I’m trying to do syntax highlighting with Gtk::TextTag-s.
My code for updating syntax (it receives iterators at the start and end of the line)
void MainWindow::update_syntax(const Gtk::TextBuffer::iterator& start, const Gtk::TextBuffer::iterator& end) {
std::vector<Glib::ustring> keywords;
keywords.push_back("class");
keywords.push_back("struct");
Glib::MatchInfo info;
auto regex = Glib::Regex::create(R"((\w+))");
auto ok = regex->match(start.get_visible_text(end), info);
std::map<Glib::ustring, std::pair<Glib::RefPtr<Gtk::TextMark>, Glib::RefPtr<Gtk::TextMark>>> marks;
do {
std::cout << "word: " << info.fetch(1) << std::endl;
for (auto kw : keywords) {
if (info.fetch(1) == kw) {
int start_offset, end_offset;
info.fetch_pos(1, start_offset, end_offset);
std::cout << info.fetch(1) << " (at: [" << start_offset << ";" << end_offset << "])" << std::endl;
marks["keyword"] = std::make_pair(
this->m_buffer->create_mark(
this->m_buffer->get_iter_at_offset(start.get_offset() + start_offset)
),
this->m_buffer->create_mark(
this->m_buffer->get_iter_at_offset(start.get_offset() + end_offset)
)
);
}
}
} while(info.next());
for (auto mark : marks) {
this->m_buffer->apply_tag_by_name(mark.first,
mark.second.first->get_iter(), mark.second.second->get_iter());
}
}
So the flow is that I create a simple regexp that should match every word in that line, and then create a map of marks that will later give ranges for which tags will be set. I’m using Gtk::Mark here, because iterators are invalidated with each modification to the buffer.
To illustrate what’s wrong here, I will post some debug output from this function, and a slot on_insert before;
void MainWindow::on_insert(const Gtk::TextBuffer::iterator& pos,
const Glib::ustring& text, int bytes)
{
std::cout << text << " (added at[" << pos.get_offset() <<
"]; with [" << bytes << "]bytes)" << std::endl << std::endl;
So the output of writing class class to the TextView results in first one being highlighted, and the second one not picked up, log:
c (added at[1]; with [1]bytes)
word: c
l (added at[2]; with [1]bytes)
word: cl
a (added at[3]; with [1]bytes)
word: cla
s (added at[4]; with [1]bytes)
word: clas
s (added at[5]; with [1]bytes)
word: class
class (keyword at: [0;5])
(added at[6]; with [1]bytes)
word: class
class (keyword at: [0;5])
word: r
c (added at[7]; with [1]bytes)
word: class
class (keyword at: [0;5])
word: rd
l (added at[8]; with [1]bytes)
word: class
class (keyword at: [0;5])
word: rd
a (added at[9]; with [1]bytes)
word: class
class (keyword at: [0;5])
word: rd
word: a
s (added at[10]; with [1]bytes)
word: class
class (keyword at: [0;5])
word: rd
word: as
s (added at[11]; with [1]bytes)
word: class
class (keyword at: [0;5])
word: rd
word: ass
It’s easy to notice that the last line shows that it’s moved by two offsets. Could it be that the tag is applied. Also, what is not clear here, the: word: rd. I use keyword as the name of the tag. And back when this code was still using iterators, the info.fetch(1) returned "keyword", so could it be that regex is matching tags also?
I hope someone with experience in Glib and Gtk knows the answer, thanks.
I’ve not used this particular API yet1, but I think you have an issue with object lifetime.
iter->get_visible_text()is returning a string object which is destroyed after the call toregex->match(). This is a problem becauseGlib::MatchInfo::next()expects that string to still exist2. This is probably why you get garbage for the second match. I think you’ll be safe doing something like this: