Browse Source

Spellcheck cleanup and improvements

merge-requests/365/head
eidheim 9 years ago
parent
commit
25544a1be5
  1. 131
      src/source_spellcheck.cc
  2. 6
      src/source_spellcheck.h

131
src/source_spellcheck.cc

@ -22,8 +22,8 @@ Source::SpellCheckView::SpellCheckView() : Gsv::View() {
if(spellcheck_config==nullptr) if(spellcheck_config==nullptr)
spellcheck_config=new_aspell_config(); spellcheck_config=new_aspell_config();
spellcheck_checker=nullptr; spellcheck_checker=nullptr;
auto tag=get_buffer()->create_tag("spellcheck_error"); spellcheck_error_tag=get_buffer()->create_tag("spellcheck_error");
tag->property_underline()=Pango::Underline::UNDERLINE_ERROR; spellcheck_error_tag->property_underline()=Pango::Underline::UNDERLINE_ERROR;
signal_key_press_event().connect([this](GdkEventKey *event) { signal_key_press_event().connect([this](GdkEventKey *event) {
if(spellcheck_suggestions_dialog && spellcheck_suggestions_dialog->shown) { if(spellcheck_suggestions_dialog && spellcheck_suggestions_dialog->shown) {
@ -47,43 +47,40 @@ Source::SpellCheckView::SpellCheckView() : Gsv::View() {
delayed_spellcheck_suggestions_connection.disconnect(); delayed_spellcheck_suggestions_connection.disconnect();
auto iter=get_buffer()->get_insert()->get_iter(); auto iter=get_buffer()->get_insert()->get_iter();
bool ends_line=iter.ends_line(); if(!is_word_iter(iter) && !iter.starts_line())
if(iter.backward_char()) { iter.backward_char();
auto context_iter=iter; if(!is_code_iter(iter)) {
bool backward_success=true;
if(ends_line || *iter=='/' || *iter=='*') //iter_has_context_class is sadly bugged
backward_success=context_iter.backward_char();
if(backward_success) {
if(last_keyval==GDK_KEY_BackSpace && !is_word_iter(iter) && iter.forward_char()) {} //backspace fix
if((spellcheck_all && !get_source_buffer()->iter_has_context_class(context_iter, "no-spell-check")) || !is_code_iter(context_iter)) {
if(!is_word_iter(iter) || (last_keyval==GDK_KEY_Return || last_keyval==GDK_KEY_KP_Enter)) { //Might have used space or - to split two words
auto first=iter;
auto second=iter;
if(last_keyval==GDK_KEY_Return || last_keyval==GDK_KEY_KP_Enter) { if(last_keyval==GDK_KEY_Return || last_keyval==GDK_KEY_KP_Enter) {
while(first && !first.ends_line() && first.backward_char()) {} auto previous_line_iter=iter;
if(first.backward_char() && second.forward_char()) { while(previous_line_iter.backward_char() && !previous_line_iter.ends_line()) {}
get_buffer()->remove_tag_by_name("spellcheck_error", first, second); if(previous_line_iter.backward_char()) {
auto word=spellcheck_get_word(first); get_buffer()->remove_tag(spellcheck_error_tag, previous_line_iter, iter);
auto word=get_word(previous_line_iter);
spellcheck_word(word.first, word.second); spellcheck_word(word.first, word.second);
word=spellcheck_get_word(second); word=get_word(iter);
spellcheck_word(word.first, word.second); spellcheck_word(word.first, word.second);
} }
} }
else if(first.backward_char() && second.forward_char() && !second.starts_line()) { else {
get_buffer()->remove_tag_by_name("spellcheck_error", first, second); auto previous_iter=iter;
auto word=spellcheck_get_word(first); //When for instance using space to split two words
if(previous_iter.backward_char() && !is_word_iter(previous_iter)) {
auto first=previous_iter;
auto second=iter;
if(first.backward_char()) {
get_buffer()->remove_tag(spellcheck_error_tag, first, second);
auto word=get_word(first);
spellcheck_word(word.first, word.second); spellcheck_word(word.first, word.second);
word=spellcheck_get_word(second); word=get_word(second);
spellcheck_word(word.first, word.second); spellcheck_word(word.first, word.second);
} }
} }
else { else {
auto word=spellcheck_get_word(iter); auto word=get_word(iter);
spellcheck_word(word.first, word.second); spellcheck_word(word.first, word.second);
} }
} }
} }
}
delayed_spellcheck_error_clear.disconnect(); delayed_spellcheck_error_clear.disconnect();
delayed_spellcheck_error_clear=Glib::signal_timeout().connect([this]() { delayed_spellcheck_error_clear=Glib::signal_timeout().connect([this]() {
auto iter=get_buffer()->begin(); auto iter=get_buffer()->begin();
@ -100,7 +97,7 @@ Source::SpellCheckView::SpellCheckView() : Gsv::View() {
if(!spell_check) if(!spell_check)
begin_no_spellcheck_iter=iter; begin_no_spellcheck_iter=iter;
else else
get_buffer()->remove_tag_by_name("spellcheck_error", begin_no_spellcheck_iter, iter); get_buffer()->remove_tag(spellcheck_error_tag, begin_no_spellcheck_iter, iter);
} }
return false; return false;
} }
@ -124,12 +121,29 @@ Source::SpellCheckView::SpellCheckView() : Gsv::View() {
if(!spell_check) if(!spell_check)
begin_no_spellcheck_iter=iter; begin_no_spellcheck_iter=iter;
else else
get_buffer()->remove_tag_by_name("spellcheck_error", begin_no_spellcheck_iter, iter); get_buffer()->remove_tag(spellcheck_error_tag, begin_no_spellcheck_iter, iter);
} }
return false; return false;
}, 1000); }, 1000);
}); });
// In case of for instance text paste or undo/redo
get_buffer()->signal_insert().connect([this](const Gtk::TextIter &end_iter, const Glib::ustring &inserted_string, int) {
if(spellcheck_checker==nullptr)
return;
if(inserted_string.size()<=1)
return;
auto start_iter=end_iter;
start_iter.backward_chars(inserted_string.size());
if(!is_word_iter(start_iter) && !start_iter.starts_line())
start_iter.backward_char();
auto word=get_word(start_iter);
start_iter=word.first;
word=get_word(end_iter);
get_buffer()->remove_tag(spellcheck_error_tag, start_iter, word.second);
});
get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator& iter, const Glib::RefPtr<Gtk::TextBuffer::Mark>& mark) { get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator& iter, const Glib::RefPtr<Gtk::TextBuffer::Mark>& mark) {
if(spellcheck_checker==nullptr) if(spellcheck_checker==nullptr)
return; return;
@ -139,18 +153,10 @@ Source::SpellCheckView::SpellCheckView() : Gsv::View() {
spellcheck_suggestions_dialog->hide(); spellcheck_suggestions_dialog->hide();
delayed_spellcheck_suggestions_connection.disconnect(); delayed_spellcheck_suggestions_connection.disconnect();
delayed_spellcheck_suggestions_connection=Glib::signal_timeout().connect([this]() { delayed_spellcheck_suggestions_connection=Glib::signal_timeout().connect([this]() {
auto tags=get_buffer()->get_insert()->get_iter().get_tags(); if(get_buffer()->get_insert()->get_iter().has_tag(spellcheck_error_tag)) {
bool need_suggestions=false;
for(auto &tag: tags) {
if(tag->property_name()=="spellcheck_error") {
need_suggestions=true;
break;
}
}
if(need_suggestions) {
spellcheck_suggestions_dialog=std::make_unique<SelectionDialog>(*this, get_buffer()->create_mark(get_buffer()->get_insert()->get_iter()), false); spellcheck_suggestions_dialog=std::make_unique<SelectionDialog>(*this, get_buffer()->create_mark(get_buffer()->get_insert()->get_iter()), false);
auto word=spellcheck_get_word(get_buffer()->get_insert()->get_iter()); auto word=get_word(get_buffer()->get_insert()->get_iter());
auto suggestions=spellcheck_get_suggestions(word.first, word.second); auto suggestions=get_spellcheck_suggestions(word.first, word.second);
if(suggestions.size()==0) if(suggestions.size()==0)
return false; return false;
for(auto &suggestion: suggestions) for(auto &suggestion: suggestions)
@ -228,7 +234,7 @@ void Source::SpellCheckView::configure() {
std::cerr << "Spell check error: " << aspell_error_message(spellcheck_possible_err) << std::endl; std::cerr << "Spell check error: " << aspell_error_message(spellcheck_possible_err) << std::endl;
else else
spellcheck_checker = to_aspell_speller(spellcheck_possible_err); spellcheck_checker = to_aspell_speller(spellcheck_possible_err);
get_buffer()->remove_tag_by_name("spellcheck_error", get_buffer()->begin(), get_buffer()->end()); get_buffer()->remove_tag(spellcheck_error_tag, get_buffer()->begin(), get_buffer()->end());
} }
void Source::SpellCheckView::hide_dialogs() { void Source::SpellCheckView::hide_dialogs() {
@ -243,7 +249,7 @@ void Source::SpellCheckView::spellcheck(const Gtk::TextIter& start, const Gtk::T
auto iter=start; auto iter=start;
while(iter && iter<end) { while(iter && iter<end) {
if(is_word_iter(iter)) { if(is_word_iter(iter)) {
auto word=spellcheck_get_word(iter); auto word=get_word(iter);
spellcheck_word(word.first, word.second); spellcheck_word(word.first, word.second);
iter=word.second; iter=word.second;
} }
@ -295,7 +301,7 @@ void Source::SpellCheckView::spellcheck() {
} }
void Source::SpellCheckView::remove_spellcheck_errors() { void Source::SpellCheckView::remove_spellcheck_errors() {
get_buffer()->remove_tag_by_name("spellcheck_error", get_buffer()->begin(), get_buffer()->end()); get_buffer()->remove_tag(spellcheck_error_tag, get_buffer()->begin(), get_buffer()->end());
} }
void Source::SpellCheckView::goto_next_spellcheck_error() { void Source::SpellCheckView::goto_next_spellcheck_error() {
@ -306,7 +312,7 @@ void Source::SpellCheckView::goto_next_spellcheck_error() {
while(!wrapped || iter<insert_iter) { while(!wrapped || iter<insert_iter) {
auto toggled_tags=iter.get_toggled_tags(); auto toggled_tags=iter.get_toggled_tags();
for(auto &toggled_tag: toggled_tags) { for(auto &toggled_tag: toggled_tags) {
if(toggled_tag->property_name()=="spellcheck_error") { if(toggled_tag==spellcheck_error_tag) {
get_buffer()->place_cursor(iter); get_buffer()->place_cursor(iter);
scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5); scroll_to(get_buffer()->get_insert(), 0.0, 1.0, 0.5);
return; return;
@ -333,8 +339,31 @@ bool Source::SpellCheckView::is_code_iter(const Gtk::TextIter &iter) {
} }
if(comment_tag && ((iter.has_tag(comment_tag) && !iter.begins_tag(comment_tag)) || iter.ends_tag(comment_tag))) if(comment_tag && ((iter.has_tag(comment_tag) && !iter.begins_tag(comment_tag)) || iter.ends_tag(comment_tag)))
return false; return false;
if(string_tag && iter.has_tag(string_tag) && !iter.begins_tag(string_tag)) if(string_tag) {
if(iter.has_tag(string_tag)) {
if(!iter.begins_tag(string_tag))
return false;
}
// If iter is at the end of string_tag, with exception of after " and '
else if(iter.ends_tag(string_tag)) {
auto previous_iter=iter;
if(!iter.starts_line() && previous_iter.backward_char()) {
if((*previous_iter=='"' || *previous_iter=='\'')) {
long backslash_count=0;
auto it=previous_iter;
while(it.backward_char() && *it=='\\')
++backslash_count;
if(backslash_count%2==0) {
auto it=previous_iter;
while(!it.begins_tag(string_tag) && it.backward_to_tag_toggle(string_tag)) {}
if(it.begins_tag(string_tag) && *previous_iter==*it && previous_iter!=it)
return true;
}
}
return false; return false;
}
}
}
return true; return true;
} }
@ -342,7 +371,7 @@ bool Source::SpellCheckView::is_word_iter(const Gtk::TextIter& iter) {
return ((*iter>='A' && *iter<='Z') || (*iter>='a' && *iter<='z') || *iter=='\'' || *iter>=128); return ((*iter>='A' && *iter<='Z') || (*iter>='a' && *iter<='z') || *iter=='\'' || *iter>=128);
} }
std::pair<Gtk::TextIter, Gtk::TextIter> Source::SpellCheckView::spellcheck_get_word(Gtk::TextIter iter) { std::pair<Gtk::TextIter, Gtk::TextIter> Source::SpellCheckView::get_word(Gtk::TextIter iter) {
auto start=iter; auto start=iter;
auto end=iter; auto end=iter;
@ -363,27 +392,31 @@ void Source::SpellCheckView::spellcheck_word(const Gtk::TextIter& start, const G
if((end.get_offset()-start.get_offset())==2) { if((end.get_offset()-start.get_offset())==2) {
auto before_end=end; auto before_end=end;
before_end.backward_char(); before_end.backward_char();
if(*before_end=='\'' || *start=='\'') if(*before_end=='\'' || *start=='\'') {
get_buffer()->remove_tag(spellcheck_error_tag, start, end);
return; return;
} }
}
else if((end.get_offset()-start.get_offset())==3) { else if((end.get_offset()-start.get_offset())==3) {
auto before_end=end; auto before_end=end;
before_end.backward_char(); before_end.backward_char();
if(*before_end=='\'' && *start=='\'') if(*before_end=='\'' && *start=='\'') {
get_buffer()->remove_tag(spellcheck_error_tag, start, end);
return; return;
} }
}
auto word=get_buffer()->get_text(start, end); auto word=get_buffer()->get_text(start, end);
if(word.size()>0) { if(word.size()>0) {
auto correct = aspell_speller_check(spellcheck_checker, word.data(), word.bytes()); auto correct = aspell_speller_check(spellcheck_checker, word.data(), word.bytes());
if(correct==0) if(correct==0)
get_buffer()->apply_tag_by_name("spellcheck_error", start, end); get_buffer()->apply_tag(spellcheck_error_tag, start, end);
else else
get_buffer()->remove_tag_by_name("spellcheck_error", start, end); get_buffer()->remove_tag(spellcheck_error_tag, start, end);
} }
} }
std::vector<std::string> Source::SpellCheckView::spellcheck_get_suggestions(const Gtk::TextIter& start, const Gtk::TextIter& end) { std::vector<std::string> Source::SpellCheckView::get_spellcheck_suggestions(const Gtk::TextIter& start, const Gtk::TextIter& end) {
auto word_with_error=get_buffer()->get_text(start, end); auto word_with_error=get_buffer()->get_text(start, end);
const AspellWordList *suggestions = aspell_speller_suggest(spellcheck_checker, word_with_error.data(), word_with_error.bytes()); const AspellWordList *suggestions = aspell_speller_suggest(spellcheck_checker, word_with_error.data(), word_with_error.bytes());

6
src/source_spellcheck.h

@ -26,6 +26,8 @@ namespace Source {
private: private:
std::unique_ptr<SelectionDialog> spellcheck_suggestions_dialog; std::unique_ptr<SelectionDialog> spellcheck_suggestions_dialog;
Glib::RefPtr<Gtk::TextTag> spellcheck_error_tag;
Glib::RefPtr<Gtk::TextTag> comment_tag; Glib::RefPtr<Gtk::TextTag> comment_tag;
Glib::RefPtr<Gtk::TextTag> string_tag; Glib::RefPtr<Gtk::TextTag> string_tag;
Glib::RefPtr<Gtk::TextTag> no_spell_check_tag; Glib::RefPtr<Gtk::TextTag> no_spell_check_tag;
@ -36,9 +38,9 @@ namespace Source {
AspellCanHaveError *spellcheck_possible_err; AspellCanHaveError *spellcheck_possible_err;
AspellSpeller *spellcheck_checker; AspellSpeller *spellcheck_checker;
bool is_word_iter(const Gtk::TextIter& iter); bool is_word_iter(const Gtk::TextIter& iter);
std::pair<Gtk::TextIter, Gtk::TextIter> spellcheck_get_word(Gtk::TextIter iter); std::pair<Gtk::TextIter, Gtk::TextIter> get_word(Gtk::TextIter iter);
void spellcheck_word(const Gtk::TextIter& start, const Gtk::TextIter& end); void spellcheck_word(const Gtk::TextIter& start, const Gtk::TextIter& end);
std::vector<std::string> spellcheck_get_suggestions(const Gtk::TextIter& start, const Gtk::TextIter& end); std::vector<std::string> get_spellcheck_suggestions(const Gtk::TextIter& start, const Gtk::TextIter& end);
sigc::connection delayed_spellcheck_suggestions_connection; sigc::connection delayed_spellcheck_suggestions_connection;
sigc::connection delayed_spellcheck_error_clear; sigc::connection delayed_spellcheck_error_clear;

Loading…
Cancel
Save