From 4eb85d90c6903fb37b3f6bcd69a5b504fce2a504 Mon Sep 17 00:00:00 2001 From: eidheim Date: Fri, 28 Aug 2015 11:35:50 +0200 Subject: [PATCH] Added spellcheck using aspell to comments and strings. Please install (lib)aspell(-dev) and rm ~/.juci to test. --- docs/install.md | 6 ++-- src/CMakeLists.txt | 6 +++- src/config.cc | 2 ++ src/files.h | 1 + src/source.cc | 76 +++++++++++++++++++++++++++++++++++++++------- src/source.h | 6 ++++ 6 files changed, 82 insertions(+), 15 deletions(-) diff --git a/docs/install.md b/docs/install.md index 2e8837b..12062d5 100644 --- a/docs/install.md +++ b/docs/install.md @@ -4,7 +4,7 @@ Before installation, please install libclangmm, see [installation guide](http:// ## Debian/Ubuntu ```sh -sudo apt-get install pkg-config libboost-system-dev libboost-thread-dev libboost-filesystem-dev libboost-log-dev libgtkmm-3.0-dev libgtksourceviewmm-3.0-dev +sudo apt-get install pkg-config libboost-system-dev libboost-thread-dev libboost-filesystem-dev libboost-log-dev libgtkmm-3.0-dev libgtksourceviewmm-3.0-dev libaspell-dev ``` ```sh git clone http://github.com/cppit/jucipp.git @@ -16,7 +16,7 @@ sudo make install ## OS X with Homebrew (http://brew.sh/) ```sh -brew install pkg-config boost gtkmm3 gtksourceviewmm3 +brew install pkg-config boost gtkmm3 gtksourceviewmm3 aspell ``` ```sh @@ -36,7 +36,7 @@ Please wait until the following TODOs are resolved: Install dependencies(replace [arch] with i686 or x86_64 depending on your MSYS2 install): ```sh -pacman -S patch autoconf automake-wrapper mingw-w64-[arch]-gtkmm3 mingw-w64-[arch]-boost +pacman -S patch autoconf automake-wrapper mingw-w64-[arch]-gtkmm3 mingw-w64-[arch]-boost mingw-w64-[arch]-aspell ``` Get juCi++ source: diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d15e8e1..3dcada4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -56,6 +56,8 @@ validate(${GTKMM_FOUND} "gtkmm" "libgtkmm-dev" "gtkmm") pkg_check_modules(GTKSVMM gtksourceviewmm-3.0) validate(${GTKSVMM_FOUND} "gtksvmm" "libgtksvmm-dev" "gtkmmsv") +find_package(ASPELL REQUIRED) + set(source_files juci.h juci.cc menu.h @@ -105,7 +107,8 @@ if(${validation}) ${GTKMM_INCLUDE_DIRS} ${GTKSVMM_INCLUDE_DIRS} ${LCL_INCLUDE_DIRS} - ${LIBCLANG_INCLUDE_DIRS}) + ${LIBCLANG_INCLUDE_DIRS} + ${ASPELL_INCLUDE_DIR}) link_directories( ${GTKMM_LIBRARY_DIRS} @@ -128,6 +131,7 @@ if(${validation}) ${GTKMM_LIBRARIES} ${GTKSVMM_LIBRARIES} ${Boost_LIBRARIES} + ${ASPELL_LIBRARIES} # ${PYTHON_LIBRARIES} ) diff --git a/src/config.cc b/src/config.cc index 0de059c..7d6da88 100644 --- a/src/config.cc +++ b/src/config.cc @@ -46,6 +46,8 @@ void MainConfig::GenerateSource() { auto source_cfg = Singleton::Config::source(); auto source_json = cfg.get_child("source"); + source_cfg->spellcheck_language = source_json.get("spellcheck_language"); + source_cfg->default_tab_char = source_json.get("default_tab_char"); source_cfg->default_tab_size = source_json.get("default_tab_size"); source_cfg->auto_tab_char_and_size = source_json.get("auto_tab_char_and_size"); diff --git a/src/files.h b/src/files.h index 7b390a1..516cf05 100644 --- a/src/files.h +++ b/src/files.h @@ -17,6 +17,7 @@ const std::string configjson = #endif #endif "//Use \"\" for default font, and for instance \"Monospace 12\" to also set size.\n" +" \"spellcheck_language\": \"en_US\", \n" " \"clang_types\": {\n" " \"8\": \"def:function\",\n" " \"21\": \"def:function\",\n" diff --git a/src/source.cc b/src/source.cc index de8146d..82168b0 100644 --- a/src/source.cc +++ b/src/source.cc @@ -7,14 +7,16 @@ #include "singletons.h" #include #include +#include -#include //TODO: remove using namespace std; //TODO: remove namespace sigc { SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE } +AspellConfig* spellcheck_config=NULL; + Glib::RefPtr Source::guess_language(const boost::filesystem::path &file_path) { auto language_manager=Gsv::LanguageManager::get_default(); bool result_uncertain = false; @@ -98,18 +100,43 @@ Source::View::View(const boost::filesystem::path &file_path): file_path(file_pat tabs_regex=std::regex(std::string("^(")+tab_char+"*)(.*)$"); - get_buffer()->signal_changed().connect([this](){ - auto iter=get_buffer()->get_insert()->get_iter(); - if(iter.backward_char()) { - auto context_iter=iter; - if(context_iter.backward_char()) { - if(get_source_buffer()->iter_has_context_class(context_iter, "comment")) { - //TODO: get word, and spellcheck - //cout << "comment: " << (char)*iter << endl; + if(spellcheck_config==NULL) { + spellcheck_config=new_aspell_config(); + aspell_config_replace(spellcheck_config, "lang", Singleton::Config::source()->spellcheck_language.c_str()); + } + + spellcheck_possible_err=new_aspell_speller(spellcheck_config); + spellcheck_checker=NULL; + if (aspell_error_number(spellcheck_possible_err) != 0) + std::cerr << "Spell check error: " << aspell_error_message(spellcheck_possible_err) << std::endl; + else { + spellcheck_checker = to_aspell_speller(spellcheck_possible_err); + + auto tag=get_buffer()->create_tag("spellcheck_error"); + tag->property_underline()=Pango::Underline::UNDERLINE_ERROR; + + get_buffer()->signal_changed().connect([this](){ + auto iter=get_buffer()->get_insert()->get_iter(); + if(iter.backward_char()) { + auto context_iter=iter; + if(context_iter.backward_char()) { + if(get_source_buffer()->iter_has_context_class(context_iter, "comment") || get_source_buffer()->iter_has_context_class(context_iter, "string")) { + if(*iter==32) { //Might have used space to split two words + auto first=iter; + auto second=iter; + if(first.backward_char() && second.forward_char()) { + get_buffer()->remove_tag_by_name("spellcheck_error", first, second); + spellcheck(first); + spellcheck(second); + } + } + else + spellcheck(iter); + } } } - } - }); + }); + } } void Source::View::search_occurrences_updated(GtkWidget* widget, GParamSpec* property, gpointer data) { @@ -121,6 +148,8 @@ void Source::View::search_occurrences_updated(GtkWidget* widget, GParamSpec* pro Source::View::~View() { g_clear_object(&search_context); g_clear_object(&search_settings); + + delete_aspell_speller(spellcheck_checker); } void Source::View::search_highlight(const std::string &text, bool case_sensitive, bool regex) { @@ -477,6 +506,31 @@ std::pair Source::View::find_tab_char_and_size() { return {found_tab_char, found_tab_size}; } +void Source::View::spellcheck(Gtk::TextIter iter) { + auto start=iter; + auto end=iter; + + while((*iter>=48 && *iter<=57) || (*iter>=65 && *iter<=90) || (*iter>=97 && *iter<=122) || *iter==95 || *iter>=128) { + start=iter; + if(!iter.backward_char()) + break; + } + while((*end>=48 && *end<=57) || (*end>=65 && *end<=90) || (*end>=97 && *end<=122) || *end==95 || *end>=128) { + if(!end.forward_char()) + break; + } + auto word=get_buffer()->get_text(start, end); + std::vector words; + if(word.size()>0) { + auto correct = aspell_speller_check(spellcheck_checker, word.data(), word.bytes()); + if(correct==0) + get_buffer()->apply_tag_by_name("spellcheck_error", start, end); + else + get_buffer()->remove_tag_by_name("spellcheck_error", start, end); + } +} + + ///////////////////// //// GenericView //// ///////////////////// diff --git a/src/source.h b/src/source.h index 20185f9..9a8dd15 100644 --- a/src/source.h +++ b/src/source.h @@ -15,6 +15,7 @@ #include "selectiondialog.h" #include #include +#include namespace Source { Glib::RefPtr guess_language(const boost::filesystem::path &file_path); @@ -23,6 +24,7 @@ namespace Source { public: std::string style; std::string font; + std::string spellcheck_language; bool auto_tab_char_and_size; char default_tab_char; unsigned default_tab_size; @@ -91,6 +93,10 @@ namespace Source { GtkSourceSearchContext *search_context; GtkSourceSearchSettings *search_settings; static void search_occurrences_updated(GtkWidget* widget, GParamSpec* property, gpointer data); + + AspellCanHaveError *spellcheck_possible_err; + AspellSpeller *spellcheck_checker; + void spellcheck(Gtk::TextIter iter); }; // class View class GenericView : public View {