diff options
Diffstat (limited to 'src/plugins/spell')
-rw-r--r-- | src/plugins/spell/CMakeLists.txt | 42 | ||||
-rw-r--r-- | src/plugins/spell/Makefile.am | 44 | ||||
-rw-r--r-- | src/plugins/spell/spell-bar-item.c | 167 | ||||
-rw-r--r-- | src/plugins/spell/spell-bar-item.h | 25 | ||||
-rw-r--r-- | src/plugins/spell/spell-command.c | 507 | ||||
-rw-r--r-- | src/plugins/spell/spell-command.h | 25 | ||||
-rw-r--r-- | src/plugins/spell/spell-completion.c | 139 | ||||
-rw-r--r-- | src/plugins/spell/spell-completion.h | 25 | ||||
-rw-r--r-- | src/plugins/spell/spell-config.c | 676 | ||||
-rw-r--r-- | src/plugins/spell/spell-config.h | 54 | ||||
-rw-r--r-- | src/plugins/spell/spell-info.c | 89 | ||||
-rw-r--r-- | src/plugins/spell/spell-info.h | 25 | ||||
-rw-r--r-- | src/plugins/spell/spell-speller.c | 486 | ||||
-rw-r--r-- | src/plugins/spell/spell-speller.h | 51 | ||||
-rw-r--r-- | src/plugins/spell/spell.c | 1167 | ||||
-rw-r--r-- | src/plugins/spell/spell.h | 52 |
16 files changed, 3574 insertions, 0 deletions
diff --git a/src/plugins/spell/CMakeLists.txt b/src/plugins/spell/CMakeLists.txt new file mode 100644 index 000000000..0a1c51bb1 --- /dev/null +++ b/src/plugins/spell/CMakeLists.txt @@ -0,0 +1,42 @@ +# +# Copyright (C) 2006 Emmanuel Bouthenot <kolter@openics.org> +# Copyright (C) 2006-2019 Sébastien Helleu <flashcode@flashtux.org> +# +# This file is part of WeeChat, the extensible chat client. +# +# WeeChat is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# WeeChat is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with WeeChat. If not, see <https://www.gnu.org/licenses/>. +# + +add_library(spell MODULE +spell.c spell.h +spell-bar-item.c spell-bar-item.h +spell-command.c spell-command.h +spell-completion.c spell-completion.h +spell-config.c spell-config.h +spell-info.c spell-info.h +spell-speller.c spell-speller.h) +set_target_properties(spell PROPERTIES PREFIX "") + +if(ENCHANT_FOUND) + include_directories(${ENCHANT_INCLUDE_DIR}) + target_link_libraries(spell ${ENCHANT_LIBRARIES}) + add_definitions(-DUSE_ENCHANT) +else() + if(ASPELL_FOUND) + include_directories(${ASPELL_INCLUDE_PATH}) + target_link_libraries(spell ${ASPELL_LIBRARY}) + endif() +endif() + +install(TARGETS spell LIBRARY DESTINATION ${LIBDIR}/plugins) diff --git a/src/plugins/spell/Makefile.am b/src/plugins/spell/Makefile.am new file mode 100644 index 000000000..b3313d9f4 --- /dev/null +++ b/src/plugins/spell/Makefile.am @@ -0,0 +1,44 @@ +# +# Copyright (C) 2006 Emmanuel Bouthenot <kolter@openics.org> +# Copyright (C) 2006-2019 Sébastien Helleu <flashcode@flashtux.org> +# +# This file is part of WeeChat, the extensible chat client. +# +# WeeChat is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# WeeChat is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with WeeChat. If not, see <https://www.gnu.org/licenses/>. +# + +AM_CPPFLAGS = -DLOCALEDIR=\"$(datadir)/locale\" $(ASPELL_CFLAGS) $(ENCHANT_CFLAGS) + +libdir = ${weechat_libdir}/plugins + +lib_LTLIBRARIES = spell.la + +spell_la_SOURCES = spell.c \ + spell.h \ + spell-bar-item.c \ + spell-bar-item.h \ + spell-command.c \ + spell-command.h \ + spell-completion.c \ + spell-completion.h \ + spell-config.c \ + spell-config.h \ + spell-info.c \ + spell-info.h \ + spell-speller.c \ + spell-speller.h +spell_la_LDFLAGS = -module -no-undefined +spell_la_LIBADD = $(ASPELL_LFLAGS) $(ENCHANT_LIBS) + +EXTRA_DIST = CMakeLists.txt diff --git a/src/plugins/spell/spell-bar-item.c b/src/plugins/spell/spell-bar-item.c new file mode 100644 index 000000000..0b9ef5453 --- /dev/null +++ b/src/plugins/spell/spell-bar-item.c @@ -0,0 +1,167 @@ +/* + * spell-bar-item.c - bar items for spell checker plugin + * + * Copyright (C) 2012 Nils Görs <weechatter@arcor.de> + * Copyright (C) 2012-2019 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WeeChat. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "../weechat-plugin.h" +#include "spell.h" +#include "spell-config.h" + + +/* + * Returns content of bar item "spell_dict": spell dictionary used on current + * buffer. + */ + +char * +spell_bar_item_dict (const void *pointer, void *data, + struct t_gui_bar_item *item, + struct t_gui_window *window, + struct t_gui_buffer *buffer, + struct t_hashtable *extra_info) +{ + const char *dict_list; + + /* make C compiler happy */ + (void) pointer; + (void) data; + (void) item; + (void) window; + (void) extra_info; + + if (!buffer) + return NULL; + + dict_list = spell_get_dict (buffer); + + return (dict_list) ? strdup (dict_list) : NULL; +} + +/* + * Returns content of bar item "spell_suggest": spell checker suggestions. + */ + +char * +spell_bar_item_suggest (const void *pointer, void *data, + struct t_gui_bar_item *item, + struct t_gui_window *window, + struct t_gui_buffer *buffer, + struct t_hashtable *extra_info) +{ + const char *ptr_suggestions, *pos; + char **suggestions, **suggestions2, **str_suggest; + int i, j, num_suggestions, num_suggestions2; + + /* make C compiler happy */ + (void) pointer; + (void) data; + (void) item; + (void) window; + (void) extra_info; + + if (!spell_enabled) + return NULL; + + if (!buffer) + return NULL; + + ptr_suggestions = weechat_buffer_get_string (buffer, + "localvar_spell_suggest"); + if (!ptr_suggestions) + return NULL; + + pos = strchr (ptr_suggestions, ':'); + if (pos) + pos++; + else + pos = ptr_suggestions; + + str_suggest = weechat_string_dyn_alloc (256); + if (!str_suggest) + return NULL; + + suggestions = weechat_string_split (pos, "/", 0, 0, &num_suggestions); + if (!suggestions) + goto end; + + for (i = 0; i < num_suggestions; i++) + { + if (i > 0) + { + weechat_string_dyn_concat ( + str_suggest, + weechat_color ( + weechat_config_string ( + spell_config_color_suggestion_delimiter_dict))); + weechat_string_dyn_concat ( + str_suggest, + weechat_config_string ( + spell_config_look_suggestion_delimiter_dict)); + } + suggestions2 = weechat_string_split (suggestions[i], ",", 0, 0, + &num_suggestions2); + if (suggestions2) + { + for (j = 0; j < num_suggestions2; j++) + { + if (j > 0) + { + weechat_string_dyn_concat ( + str_suggest, + weechat_color ( + weechat_config_string ( + spell_config_color_suggestion_delimiter_word))); + weechat_string_dyn_concat ( + str_suggest, + weechat_config_string ( + spell_config_look_suggestion_delimiter_word)); + } + weechat_string_dyn_concat ( + str_suggest, + weechat_color ( + weechat_config_string ( + spell_config_color_suggestion))); + weechat_string_dyn_concat (str_suggest, suggestions2[j]); + } + weechat_string_free_split (suggestions2); + } + } + weechat_string_free_split (suggestions); + +end: + return weechat_string_dyn_free (str_suggest, 0); +} + +/* + * Initializes spell bar items. + */ + +void +spell_bar_item_init () +{ + weechat_bar_item_new ("spell_dict", + &spell_bar_item_dict, NULL, NULL); + weechat_bar_item_new ("spell_suggest", + &spell_bar_item_suggest, NULL, NULL); +} diff --git a/src/plugins/spell/spell-bar-item.h b/src/plugins/spell/spell-bar-item.h new file mode 100644 index 000000000..84f65681e --- /dev/null +++ b/src/plugins/spell/spell-bar-item.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2012 Nils Görs <weechatter@arcor.de> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WeeChat. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef WEECHAT_PLUGIN_SPELL_BAR_ITEM_H +#define WEECHAT_PLUGIN_SPELL_BAR_ITEM_H + +extern void spell_bar_item_init (); + +#endif /* WEECHAT_PLUGIN_SPELL_BAR_ITEM_H */ diff --git a/src/plugins/spell/spell-command.c b/src/plugins/spell/spell-command.c new file mode 100644 index 000000000..5d27aa9d7 --- /dev/null +++ b/src/plugins/spell/spell-command.c @@ -0,0 +1,507 @@ +/* + * spell-command.c - spell checker commands + * + * Copyright (C) 2013-2019 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WeeChat. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "../weechat-plugin.h" +#include "spell.h" +#include "spell-config.h" +#include "spell-speller.h" + + +/* + * Converts an ISO lang code in its English full name. + * + * Note: result must be freed after use. + */ + +char * +spell_command_iso_to_lang (const char *code) +{ + int i; + + for (i = 0; spell_langs[i].code; i++) + { + if (strcmp (spell_langs[i].code, code) == 0) + return strdup (spell_langs[i].name); + } + + /* lang code not found */ + return strdup ("Unknown"); +} + +/* + * Converts an ISO country code in its English full name. + * + * Note: result must be freed after use. + */ + +char * +spell_command_iso_to_country (const char *code) +{ + int i; + + for (i = 0; spell_countries[i].code; i++) + { + if (strcmp (spell_countries[i].code, code) == 0) + return strdup (spell_countries[i].name); + } + + /* country code not found */ + return strdup ("Unknown"); +} + +/* + * Displays one dictionary when using enchant. + */ + +#ifdef USE_ENCHANT +void +spell_enchant_dict_describe_cb (const char *lang_tag, + const char *provider_name, + const char *provider_desc, + const char *provider_file, + void *user_data) +{ + char *country, *lang, *pos, *iso; + char str_dict[256]; + + /* make C compiler happy */ + (void) provider_name; + (void) provider_desc; + (void) provider_file; + (void) user_data; + + lang = NULL; + country = NULL; + + pos = strchr (lang_tag, '_'); + + if (pos) + { + iso = weechat_strndup (lang_tag, pos - lang_tag); + if (iso) + { + lang = spell_command_iso_to_lang (iso); + country = spell_command_iso_to_country (pos + 1); + free (iso); + } + } + else + lang = spell_command_iso_to_lang ((char *)lang_tag); + + if (lang) + { + if (country) + { + snprintf (str_dict, sizeof (str_dict), "%-22s %s (%s)", + lang_tag, lang, country); + } + else + { + snprintf (str_dict, sizeof (str_dict), "%-22s %s", + lang_tag, lang); + } + weechat_printf (NULL, " %s", str_dict); + } + + if (lang) + free (lang); + if (country) + free (country); +} +#endif /* USE_ENCHANT */ + +/* + * Displays list of dictionaries installed on system. + */ + +void +spell_command_speller_list_dicts () +{ +#ifndef USE_ENCHANT + char *country, *lang, *pos, *iso; + char str_dict[256], str_country[128]; + struct AspellConfig *config; + AspellDictInfoList *list; + AspellDictInfoEnumeration *elements; + const AspellDictInfo *dict; +#endif /* USE_ENCHANT */ + + weechat_printf (NULL, ""); + weechat_printf (NULL, + /* TRANSLATORS: "%s" is "spell" (name of plugin) */ + _( "%s dictionaries list:"), + SPELL_PLUGIN_NAME); + +#ifdef USE_ENCHANT + enchant_broker_list_dicts (broker, spell_enchant_dict_describe_cb, + NULL); +#else + config = new_aspell_config (); + list = get_aspell_dict_info_list (config); + elements = aspell_dict_info_list_elements (list); + + while ((dict = aspell_dict_info_enumeration_next (elements)) != NULL) + { + lang = NULL; + country = NULL; + pos = strchr (dict->code, '_'); + + if (pos) + { + iso = weechat_strndup (dict->code, pos - dict->code); + if (iso) + { + lang = spell_command_iso_to_lang (iso); + country = spell_command_iso_to_country (pos + 1); + free (iso); + } + } + else + lang = spell_command_iso_to_lang ((char*)dict->code); + + str_country[0] = '\0'; + if (country || dict->jargon[0]) + { + snprintf (str_country, sizeof (str_country), " (%s%s%s)", + (country) ? country : dict->jargon, + (country && dict->jargon[0]) ? " - " : "", + (country && dict->jargon[0]) ? ((dict->jargon[0]) ? dict->jargon : country) : ""); + } + + snprintf (str_dict, sizeof (str_dict), "%-22s %s%s", + dict->name, + (lang) ? lang : "?", + str_country); + + weechat_printf (NULL, " %s", str_dict); + + if (lang) + free (lang); + if (country) + free (country); + } + + delete_aspell_dict_info_enumeration (elements); + delete_aspell_config (config); +#endif /* USE_ENCHANT */ +} + +/* + * Sets a list of dictionaries for a buffer. + */ + +void +spell_command_set_dict (struct t_gui_buffer *buffer, const char *value) +{ + char *name; + + name = spell_build_option_name (buffer); + if (!name) + return; + + if (spell_config_set_dict (name, value) > 0) + { + if (value && value[0]) + weechat_printf (NULL, "%s: \"%s\" => %s", + SPELL_PLUGIN_NAME, name, value); + else + weechat_printf (NULL, _("%s: \"%s\" removed"), + SPELL_PLUGIN_NAME, name); + } + + free (name); +} + +/* + * Adds a word in personal dictionary. + */ + +void +spell_command_add_word (struct t_gui_buffer *buffer, const char *dict, + const char *word) +{ + struct t_spell_speller_buffer *ptr_speller_buffer; +#ifdef USE_ENCHANT + EnchantDict *new_speller, *ptr_speller; +#else + AspellSpeller *new_speller, *ptr_speller; +#endif /* USE_ENCHANT */ + + new_speller = NULL; + + if (dict) + { + ptr_speller = weechat_hashtable_get (spell_spellers, dict); + if (!ptr_speller) + { + if (!spell_speller_dict_supported (dict)) + { + weechat_printf (NULL, + _("%s: error: dictionary \"%s\" is not " + "available on your system"), + SPELL_PLUGIN_NAME, dict); + return; + } + new_speller = spell_speller_new (dict); + if (!new_speller) + return; + ptr_speller = new_speller; + } + } + else + { + ptr_speller_buffer = weechat_hashtable_get (spell_speller_buffer, + buffer); + if (!ptr_speller_buffer) + ptr_speller_buffer = spell_speller_buffer_new (buffer); + if (!ptr_speller_buffer) + goto error; + if (!ptr_speller_buffer->spellers || !ptr_speller_buffer->spellers[0]) + { + weechat_printf (NULL, + _("%s%s: no dictionary on this buffer for " + "adding word"), + weechat_prefix ("error"), + SPELL_PLUGIN_NAME); + return; + } + else if (ptr_speller_buffer->spellers[1]) + { + weechat_printf (NULL, + _("%s%s: many dictionaries are defined for " + "this buffer, please specify dictionary"), + weechat_prefix ("error"), + SPELL_PLUGIN_NAME); + return; + } + ptr_speller = ptr_speller_buffer->spellers[0]; + } + +#ifdef USE_ENCHANT + enchant_dict_add (ptr_speller, word, strlen (word)); +#else + if (aspell_speller_add_to_personal (ptr_speller, + word, + strlen (word)) == 1) + { + weechat_printf (NULL, + _("%s: word \"%s\" added to personal dictionary"), + SPELL_PLUGIN_NAME, word); + } + else + goto error; +#endif /* USE_ENCHANT */ + + goto end; + +error: + weechat_printf (NULL, + _("%s%s: failed to add word to personal " + "dictionary"), + weechat_prefix ("error"), SPELL_PLUGIN_NAME); + +end: + if (new_speller) + weechat_hashtable_remove (spell_spellers, dict); +} + +/* + * Callback for command "/spell". + */ + +int +spell_command_cb (const void *pointer, void *data, + struct t_gui_buffer *buffer, + int argc, char **argv, char **argv_eol) +{ + char *dicts; + const char *default_dict; + struct t_infolist *infolist; + int number; + + /* make C compiler happy */ + (void) pointer; + (void) data; + + if (argc == 1) + { + /* display spell status */ + weechat_printf (NULL, ""); + weechat_printf (NULL, + /* TRANSLATORS: second "%s" is "aspell" or "enchant" */ + _("%s (using %s)"), + (spell_enabled) ? _("Spell checking is enabled") : _("Spell checking is disabled"), +#ifdef USE_ENCHANT + "enchant" +#else + "aspell" +#endif /* USE_ENCHANT */ + ); + default_dict = weechat_config_string (spell_config_check_default_dict); + weechat_printf (NULL, + _("Default dictionary: %s"), + (default_dict && default_dict[0]) ? + default_dict : _("(not set)")); + number = 0; + infolist = weechat_infolist_get ("option", NULL, "spell.dict.*"); + if (infolist) + { + while (weechat_infolist_next (infolist)) + { + if (number == 0) + weechat_printf (NULL, _("Specific dictionaries on buffers:")); + number++; + weechat_printf (NULL, " %s: %s", + weechat_infolist_string (infolist, "option_name"), + weechat_infolist_string (infolist, "value")); + } + weechat_infolist_free (infolist); + } + return WEECHAT_RC_OK; + } + + /* enable spell */ + if (weechat_strcasecmp (argv[1], "enable") == 0) + { + weechat_config_option_set (spell_config_check_enabled, "1", 1); + weechat_printf (NULL, _("Spell checker enabled")); + return WEECHAT_RC_OK; + } + + /* disable spell */ + if (weechat_strcasecmp (argv[1], "disable") == 0) + { + weechat_config_option_set (spell_config_check_enabled, "0", 1); + weechat_printf (NULL, _("Spell checker disabled")); + return WEECHAT_RC_OK; + } + + /* toggle spell */ + if (weechat_strcasecmp (argv[1], "toggle") == 0) + { + if (spell_enabled) + { + weechat_config_option_set (spell_config_check_enabled, "0", 1); + weechat_printf (NULL, _("Spell checker disabled")); + } + else + { + weechat_config_option_set (spell_config_check_enabled, "1", 1); + weechat_printf (NULL, _("Spell checker enabled")); + } + return WEECHAT_RC_OK; + } + + /* list of dictionaries */ + if (weechat_strcasecmp (argv[1], "listdict") == 0) + { + spell_command_speller_list_dicts (); + return WEECHAT_RC_OK; + } + + /* set dictionary for current buffer */ + if (weechat_strcasecmp (argv[1], "setdict") == 0) + { + WEECHAT_COMMAND_MIN_ARGS(3, "setdict"); + dicts = weechat_string_replace (argv_eol[2], " ", ""); + spell_command_set_dict (buffer, + (dicts) ? dicts : argv[2]); + if (dicts) + free (dicts); + return WEECHAT_RC_OK; + } + + /* delete dictionary used on current buffer */ + if (weechat_strcasecmp (argv[1], "deldict") == 0) + { + spell_command_set_dict (buffer, NULL); + return WEECHAT_RC_OK; + } + + /* add word to personal dictionary */ + if (weechat_strcasecmp (argv[1], "addword") == 0) + { + WEECHAT_COMMAND_MIN_ARGS(3, "addword"); + if (argc > 3) + { + /* use a given dict */ + spell_command_add_word (buffer, argv[2], argv_eol[3]); + } + else + { + /* use default dict */ + spell_command_add_word (buffer, NULL, argv_eol[2]); + } + return WEECHAT_RC_OK; + } + + WEECHAT_COMMAND_ERROR; +} + +/* + * Hooks spell command. + */ + +void +spell_command_init () +{ + weechat_hook_command ( + "spell", + N_("spell plugin configuration"), + N_("enable|disable|toggle" + " || listdict" + " || setdict <dict>[,<dict>...]" + " || deldict" + " || addword [<dict>] <word>"), + N_(" enable: enable spell checker\n" + " disable: disable spell checker\n" + " toggle: toggle spell checker\n" + "listdict: show installed dictionaries\n" + " setdict: set dictionary for current buffer (multiple dictionaries " + "can be separated by a comma)\n" + " deldict: delete dictionary used on current buffer\n" + " addword: add a word in personal dictionary\n" + "\n" + "Input line beginning with a '/' is not checked, except for some " + "commands (see /set spell.check.commands).\n" + "\n" + "To enable spell checker on all buffers, use option \"default_dict\", " + "then enable spell checker, for example:\n" + " /set spell.check.default_dict \"en\"\n" + " /spell enable\n" + "\n" + "To display a list of suggestions in a bar, use item " + "\"spell_suggest\".\n" + "\n" + "Default key to toggle spell checker is alt-s."), + "enable" + " || disable" + " || toggle" + " || listdict" + " || setdict %(spell_dicts)" + " || deldict" + " || addword", + &spell_command_cb, NULL, NULL); +} diff --git a/src/plugins/spell/spell-command.h b/src/plugins/spell/spell-command.h new file mode 100644 index 000000000..822955c0c --- /dev/null +++ b/src/plugins/spell/spell-command.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2013-2019 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WeeChat. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef WEECHAT_PLUGIN_SPELL_COMMAND_H +#define WEECHAT_PLUGIN_SPELL_COMMAND_H + +extern void spell_command_init (); + +#endif /* WEECHAT_PLUGIN_SPELL_COMMAND_H */ diff --git a/src/plugins/spell/spell-completion.c b/src/plugins/spell/spell-completion.c new file mode 100644 index 000000000..f80030add --- /dev/null +++ b/src/plugins/spell/spell-completion.c @@ -0,0 +1,139 @@ +/* + * spell-completion.c - completion for spell checker commands + * + * Copyright (C) 2013-2019 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WeeChat. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "../weechat-plugin.h" +#include "spell.h" + + +/* + * Adds spell langs (all langs, even for dictionaries not installed) to + * completion list. + */ + +int +spell_completion_langs_cb (const void *pointer, void *data, + const char *completion_item, + struct t_gui_buffer *buffer, + struct t_gui_completion *completion) +{ + int i; + + /* make C compiler happy */ + (void) pointer; + (void) data; + (void) completion_item; + (void) buffer; + + for (i = 0; spell_langs[i].code; i++) + { + weechat_hook_completion_list_add (completion, + spell_langs[i].code, + 0, WEECHAT_LIST_POS_SORT); + } + + return WEECHAT_RC_OK; +} + +/* + * Adds a dictionary to completion when using enchant. + */ + +#ifdef USE_ENCHANT +void +spell_completion_enchant_add_dict_cb (const char *lang_tag, + const char *provider_name, + const char *provider_desc, + const char *provider_file, + void *user_data) +{ + /* make C compiler happy */ + (void) provider_name; + (void) provider_desc; + (void) provider_file; + + weechat_hook_completion_list_add ((struct t_gui_completion *)user_data, + lang_tag, 0, WEECHAT_LIST_POS_SORT); +} +#endif /* USE_ENCHANT */ + +/* + * Adds spell dictionaries (only installed dictionaries) to completion list. + */ + +int +spell_completion_dicts_cb (const void *pointer, void *data, + const char *completion_item, + struct t_gui_buffer *buffer, + struct t_gui_completion *completion) +{ +#ifndef USE_ENCHANT + struct AspellConfig *config; + AspellDictInfoList *list; + AspellDictInfoEnumeration *elements; + const AspellDictInfo *dict; +#endif /* USE_ENCHANT */ + + /* make C compiler happy */ + (void) pointer; + (void) data; + (void) completion_item; + (void) buffer; + +#ifdef USE_ENCHANT + enchant_broker_list_dicts (broker, + spell_completion_enchant_add_dict_cb, + completion); +#else + config = new_aspell_config (); + list = get_aspell_dict_info_list (config); + elements = aspell_dict_info_list_elements (list); + + while ((dict = aspell_dict_info_enumeration_next (elements)) != NULL) + { + weechat_hook_completion_list_add (completion, dict->name, + 0, WEECHAT_LIST_POS_SORT); + } + + delete_aspell_dict_info_enumeration (elements); + delete_aspell_config (config); +#endif /* USE_ENCHANT */ + + return WEECHAT_RC_OK; +} + +/* + * Hooks completions. + */ + +void +spell_completion_init () +{ + weechat_hook_completion ("spell_langs", + N_("list of all languages supported"), + &spell_completion_langs_cb, NULL, NULL); + weechat_hook_completion ("spell_dicts", + N_("list of installed dictionaries"), + &spell_completion_dicts_cb, NULL, NULL); +} diff --git a/src/plugins/spell/spell-completion.h b/src/plugins/spell/spell-completion.h new file mode 100644 index 000000000..4c3ad6097 --- /dev/null +++ b/src/plugins/spell/spell-completion.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2013-2019 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WeeChat. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef WEECHAT_PLUGIN_SPELL_COMPLETION_H +#define WEECHAT_PLUGIN_SPELL_COMPLETION_H + +extern void spell_completion_init (); + +#endif /* WEECHAT_PLUGIN_SPELL_COMPLETION_H */ diff --git a/src/plugins/spell/spell-config.c b/src/plugins/spell/spell-config.c new file mode 100644 index 000000000..cc24efc35 --- /dev/null +++ b/src/plugins/spell/spell-config.c @@ -0,0 +1,676 @@ +/* + * spell-config.c - spell checker configuration options (file spell.conf) + * + * Copyright (C) 2006 Emmanuel Bouthenot <kolter@openics.org> + * Copyright (C) 2006-2019 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WeeChat. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <string.h> +#include <limits.h> + +#include "../weechat-plugin.h" +#include "spell.h" +#include "spell-config.h" +#include "spell-speller.h" + + +struct t_config_file *spell_config_file = NULL; +struct t_config_section *spell_config_section_dict = NULL; + +int spell_config_loading = 0; + +/* spell config, color section */ + +struct t_config_option *spell_config_color_misspelled; +struct t_config_option *spell_config_color_suggestion; +struct t_config_option *spell_config_color_suggestion_delimiter_dict; +struct t_config_option *spell_config_color_suggestion_delimiter_word; + +/* spell config, check section */ + +struct t_config_option *spell_config_check_commands; +struct t_config_option *spell_config_check_default_dict; +struct t_config_option *spell_config_check_during_search; +struct t_config_option *spell_config_check_enabled; +struct t_config_option *spell_config_check_real_time; +struct t_config_option *spell_config_check_suggestions; +struct t_config_option *spell_config_check_word_min_length; + +/* spell config, look section */ + +struct t_config_option *spell_config_look_suggestion_delimiter_dict; +struct t_config_option *spell_config_look_suggestion_delimiter_word; + + +char **spell_commands_to_check = NULL; +int spell_count_commands_to_check = 0; +int *spell_length_commands_to_check = NULL; + + +/* + * Callback for changes on option "spell.check.commands". + */ + +void +spell_config_change_commands (const void *pointer, void *data, + struct t_config_option *option) +{ + const char *value; + int i; + + /* make C compiler happy */ + (void) pointer; + (void) data; + + if (spell_commands_to_check) + { + weechat_string_free_split (spell_commands_to_check); + spell_commands_to_check = NULL; + spell_count_commands_to_check = 0; + } + + if (spell_length_commands_to_check) + { + free (spell_length_commands_to_check); + spell_length_commands_to_check = NULL; + } + + value = weechat_config_string (option); + if (value && value[0]) + { + spell_commands_to_check = weechat_string_split (value, + ",", 0, 0, + &spell_count_commands_to_check); + if (spell_count_commands_to_check > 0) + { + spell_length_commands_to_check = malloc (spell_count_commands_to_check * + sizeof (int)); + for (i = 0; i < spell_count_commands_to_check; i++) + { + spell_length_commands_to_check[i] = strlen (spell_commands_to_check[i]); + } + } + } +} + +/* + * Callback for changes on option "spell.check.default_dict". + */ + +void +spell_config_change_default_dict (const void *pointer, void *data, + struct t_config_option *option) +{ + /* make C compiler happy */ + (void) pointer; + (void) data; + (void) option; + + weechat_hashtable_remove_all (spell_speller_buffer); + if (!spell_config_loading) + spell_speller_remove_unused (); +} + +/* + * Callback for changes on option "spell.check.enabled". + */ + +void +spell_config_change_enabled (const void *pointer, void *data, + struct t_config_option *option) +{ + /* make C compiler happy */ + (void) pointer; + (void) data; + + spell_enabled = weechat_config_boolean (option); + + /* refresh input and spell suggestions */ + weechat_bar_item_update ("input_text"); + weechat_bar_item_update ("spell_suggest"); +} + +/* + * Callback for changes on option "spell.check.suggestions". + */ + +void +spell_config_change_suggestions (const void *pointer, void *data, + struct t_config_option *option) +{ + /* make C compiler happy */ + (void) pointer; + (void) data; + (void) option; + + weechat_bar_item_update ("spell_suggest"); +} + +/* + * Callback for changes on a dictionary. + */ + +void +spell_config_dict_change (const void *pointer, void *data, + struct t_config_option *option) +{ + /* make C compiler happy */ + (void) pointer; + (void) data; + (void) option; + + weechat_hashtable_remove_all (spell_speller_buffer); + if (!spell_config_loading) + spell_speller_remove_unused (); +} + +/* + * Callback called when an option is deleted in section "dict". + */ + +int +spell_config_dict_delete_option (const void *pointer, void *data, + struct t_config_file *config_file, + struct t_config_section *section, + struct t_config_option *option) +{ + /* make C compiler happy */ + (void) pointer; + (void) data; + (void) config_file; + (void) section; + + weechat_config_option_free (option); + + weechat_hashtable_remove_all (spell_speller_buffer); + if (!spell_config_loading) + spell_speller_remove_unused (); + + return WEECHAT_CONFIG_OPTION_UNSET_OK_REMOVED; +} + +/* + * Creates an option in section "dict". + */ + +int +spell_config_dict_create_option (const void *pointer, void *data, + struct t_config_file *config_file, + struct t_config_section *section, + const char *option_name, + const char *value) +{ + struct t_config_option *ptr_option; + int rc; + + /* make C compiler happy */ + (void) pointer; + (void) data; + + rc = WEECHAT_CONFIG_OPTION_SET_ERROR; + + if (value && value[0]) + spell_speller_check_dictionaries (value); + + if (option_name) + { + ptr_option = weechat_config_search_option (config_file, section, + option_name); + if (ptr_option) + { + if (value && value[0]) + rc = weechat_config_option_set (ptr_option, value, 0); + else + { + weechat_config_option_free (ptr_option); + rc = WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE; + } + } + else + { + if (value && value[0]) + { + ptr_option = weechat_config_new_option ( + config_file, section, + option_name, "string", + _("comma separated list of dictionaries to use on this buffer"), + NULL, 0, 0, "", value, 0, + NULL, NULL, NULL, + &spell_config_dict_change, NULL, NULL, + NULL, NULL, NULL); + rc = (ptr_option) ? + WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE : WEECHAT_CONFIG_OPTION_SET_ERROR; + } + else + rc = WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE; + } + } + + if (rc == WEECHAT_CONFIG_OPTION_SET_ERROR) + { + weechat_printf (NULL, + _("%s%s: error creating spell dictionary \"%s\" => \"%s\""), + weechat_prefix ("error"), SPELL_PLUGIN_NAME, + option_name, value); + } + else + { + weechat_hashtable_remove_all (spell_speller_buffer); + if (!spell_config_loading) + spell_speller_remove_unused (); + } + + return rc; +} + +/* + * Callback for changes on an spell option. + */ + +void +spell_config_option_change (const void *pointer, void *data, + struct t_config_option *option) +{ + /* make C compiler happy */ + (void) pointer; + (void) data; + (void) option; + + weechat_hashtable_remove_all (spell_speller_buffer); + if (!spell_config_loading) + spell_speller_remove_unused (); +} + +/* + * Callback called when an option is deleted in section "option". + */ + +int +spell_config_option_delete_option (const void *pointer, void *data, + struct t_config_file *config_file, + struct t_config_section *section, + struct t_config_option *option) +{ + /* make C compiler happy */ + (void) pointer; + (void) data; + (void) config_file; + (void) section; + + weechat_config_option_free (option); + + weechat_hashtable_remove_all (spell_speller_buffer); + if (!spell_config_loading) + spell_speller_remove_unused (); + + return WEECHAT_CONFIG_OPTION_UNSET_OK_REMOVED; +} + +/* + * Callback called when an option is created in section "option". + */ + +int +spell_config_option_create_option (const void *pointer, void *data, + struct t_config_file *config_file, + struct t_config_section *section, + const char *option_name, + const char *value) +{ + struct t_config_option *ptr_option; + int rc; + + /* make C compiler happy */ + (void) pointer; + (void) data; + + rc = WEECHAT_CONFIG_OPTION_SET_ERROR; + + if (option_name) + { + ptr_option = weechat_config_search_option (config_file, section, + option_name); + if (ptr_option) + { + if (value && value[0]) + rc = weechat_config_option_set (ptr_option, value, 1); + else + { + weechat_config_option_free (ptr_option); + rc = WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE; + } + } + else + { + if (value && value[0]) + { + ptr_option = weechat_config_new_option ( + config_file, section, + option_name, "string", + _("option for aspell (for list of available options and " + "format, run command \"aspell config\" in a shell)"), + NULL, 0, 0, "", value, 0, + NULL, NULL, NULL, + &spell_config_option_change, NULL, NULL, + NULL, NULL, NULL); + rc = (ptr_option) ? + WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE : WEECHAT_CONFIG_OPTION_SET_ERROR; + } + else + rc = WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE; + } + } + + if (rc == WEECHAT_CONFIG_OPTION_SET_ERROR) + { + weechat_printf (NULL, + _("%s%s: error creating spell option \"%s\" => \"%s\""), + weechat_prefix ("error"), SPELL_PLUGIN_NAME, + option_name, value); + } + else + { + weechat_hashtable_remove_all (spell_speller_buffer); + if (!spell_config_loading) + spell_speller_remove_unused (); + } + + return rc; +} + +/* + * Gets a list of dictionaries for a buffer. + */ + +struct t_config_option * +spell_config_get_dict (const char *name) +{ + return weechat_config_search_option (spell_config_file, + spell_config_section_dict, + name); +} + +/* + * Sets a list of dictionaries for a buffer. + */ + +int +spell_config_set_dict (const char *name, const char *value) +{ + return spell_config_dict_create_option (NULL, NULL, + spell_config_file, + spell_config_section_dict, + name, + value); +} + +/* + * Initializes spell configuration file. + * + * Returns: + * 1: OK + * 0: error + */ + +int +spell_config_init () +{ + struct t_config_section *ptr_section; + + spell_config_file = weechat_config_new (SPELL_CONFIG_NAME, + NULL, NULL, NULL); + if (!spell_config_file) + return 0; + + /* color */ + ptr_section = weechat_config_new_section ( + spell_config_file, "color", + 0, 0, + NULL, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL); + if (!ptr_section) + { + weechat_config_free (spell_config_file); + spell_config_file = NULL; + return 0; + } + + spell_config_color_misspelled = weechat_config_new_option ( + spell_config_file, ptr_section, + "misspelled", "color", + N_("text color for misspelled words (input bar)"), + NULL, 0, 0, "lightred", NULL, 0, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + spell_config_color_suggestion = weechat_config_new_option ( + spell_config_file, ptr_section, + "suggestion", "color", + N_("text color for suggestion on a misspelled word in bar item " + "\"spell_suggest\""), + NULL, 0, 0, "default", NULL, 0, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + spell_config_color_suggestion_delimiter_dict = weechat_config_new_option ( + spell_config_file, ptr_section, + "suggestion_delimiter_dict", "color", + N_("text color for delimiters displayed between two dictionaries " + "in bar item \"spell_suggest\""), + NULL, 0, 0, "cyan", NULL, 0, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + spell_config_color_suggestion_delimiter_word = weechat_config_new_option ( + spell_config_file, ptr_section, + "suggestion_delimiter_word", "color", + N_("text color for delimiters displayed between two words in bar item " + "\"spell_suggest\""), + NULL, 0, 0, "cyan", NULL, 0, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + + /* check */ + ptr_section = weechat_config_new_section ( + spell_config_file, "check", + 0, 0, + NULL, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL); + if (!ptr_section) + { + weechat_config_free (spell_config_file); + spell_config_file = NULL; + return 0; + } + + spell_config_check_commands = weechat_config_new_option ( + spell_config_file, ptr_section, + "commands", "string", + N_("comma separated list of commands for which spell checking is " + "enabled (spell checking is disabled for all other commands)"), + NULL, 0, 0, + "ame,amsg,away,command,cycle,kick,kickban,me,msg,notice,part,query," + "quit,topic", NULL, 0, + NULL, NULL, NULL, + &spell_config_change_commands, NULL, NULL, + NULL, NULL, NULL); + spell_config_check_default_dict = weechat_config_new_option ( + spell_config_file, ptr_section, + "default_dict", "string", + N_("default dictionary (or comma separated list of dictionaries) to " + "use when buffer has no dictionary defined (leave blank to disable " + "spell checker on buffers for which you didn't explicitly " + "enabled it)"), + NULL, 0, 0, "", NULL, 0, + NULL, NULL, NULL, + &spell_config_change_default_dict, NULL, NULL, + NULL, NULL, NULL); + spell_config_check_during_search = weechat_config_new_option ( + spell_config_file, ptr_section, + "during_search", "boolean", + N_("check words during text search in buffer"), + NULL, 0, 0, "off", NULL, 0, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + spell_config_check_enabled = weechat_config_new_option ( + spell_config_file, ptr_section, + "enabled", "boolean", + N_("enable spell checker for command line"), + NULL, 0, 0, "off", NULL, 0, + NULL, NULL, NULL, + &spell_config_change_enabled, NULL, NULL, + NULL, NULL, NULL); + spell_config_check_real_time = weechat_config_new_option ( + spell_config_file, ptr_section, + "real_time", "boolean", + N_("real-time spell checking of words (slower, disabled by default: " + "words are checked only if there's delimiter after)"), + NULL, 0, 0, "off", NULL, 0, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + spell_config_check_suggestions = weechat_config_new_option ( + spell_config_file, ptr_section, + "suggestions", "integer", + N_("number of suggestions to display in bar item \"spell_suggest\" " + "for each dictionary set in buffer (-1 = disable suggestions, " + "0 = display all possible suggestions in all languages)"), + NULL, -1, INT_MAX, "-1", NULL, 0, + NULL, NULL, NULL, + &spell_config_change_suggestions, NULL, NULL, + NULL, NULL, NULL); + spell_config_check_word_min_length = weechat_config_new_option ( + spell_config_file, ptr_section, + "word_min_length", "integer", + N_("minimum length for a word to be spell checked (use 0 to check all " + "words)"), + NULL, 0, INT_MAX, "2", NULL, 0, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + + /* dict */ + ptr_section = weechat_config_new_section ( + spell_config_file, "dict", + 1, 1, + NULL, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL, + &spell_config_dict_create_option, NULL, NULL, + &spell_config_dict_delete_option, NULL, NULL); + if (!ptr_section) + { + weechat_config_free (spell_config_file); + spell_config_file = NULL; + return 0; + } + + spell_config_section_dict = ptr_section; + + /* look */ + ptr_section = weechat_config_new_section ( + spell_config_file, "look", + 0, 0, + NULL, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL); + if (!ptr_section) + { + weechat_config_free (spell_config_file); + spell_config_file = NULL; + return 0; + } + + spell_config_look_suggestion_delimiter_dict = weechat_config_new_option ( + spell_config_file, ptr_section, + "suggestion_delimiter_dict", "string", + N_("delimiter displayed between two dictionaries in bar item " + "\"spell_suggest\""), + NULL, 0, 0, " / ", NULL, 0, + NULL, NULL, NULL, + &spell_config_change_suggestions, NULL, NULL, + NULL, NULL, NULL); + spell_config_look_suggestion_delimiter_word = weechat_config_new_option ( + spell_config_file, ptr_section, + "suggestion_delimiter_word", "string", + N_("delimiter displayed between two words in bar item " + "\"spell_suggest\""), + NULL, 0, 0, ",", NULL, 0, + NULL, NULL, NULL, + &spell_config_change_suggestions, NULL, NULL, + NULL, NULL, NULL); + + /* option */ + ptr_section = weechat_config_new_section ( + spell_config_file, "option", + 1, 1, + NULL, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL, + &spell_config_option_create_option, NULL, NULL, + &spell_config_option_delete_option, NULL, NULL); + if (!ptr_section) + { + weechat_config_free (spell_config_file); + spell_config_file = NULL; + return 0; + } + + return 1; +} + +/* + * Reads spell configuration file. + */ + +int +spell_config_read () +{ + int rc; + + spell_config_loading = 1; + rc = weechat_config_read (spell_config_file); + spell_config_loading = 0; + if (rc == WEECHAT_CONFIG_READ_OK) + spell_config_change_commands (NULL, NULL, spell_config_check_commands); + spell_speller_remove_unused (); + + return rc; +} + +/* + * Writes spell configuration file. + */ + +int +spell_config_write () +{ + return weechat_config_write (spell_config_file); +} + +/* + * Frees spell configuration. + */ + +void +spell_config_free () +{ + weechat_config_free (spell_config_file); + + if (spell_commands_to_check) + weechat_string_free_split (spell_commands_to_check); + if (spell_length_commands_to_check) + free (spell_length_commands_to_check); +} diff --git a/src/plugins/spell/spell-config.h b/src/plugins/spell/spell-config.h new file mode 100644 index 000000000..2e036e0a2 --- /dev/null +++ b/src/plugins/spell/spell-config.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2006 Emmanuel Bouthenot <kolter@openics.org> + * Copyright (C) 2006-2019 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WeeChat. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef WEECHAT_PLUGIN_SPELL_CONFIG_H +#define WEECHAT_PLUGIN_SPELL_CONFIG_H + +#define SPELL_CONFIG_NAME "spell" + + +extern struct t_config_option *spell_config_color_misspelled; +extern struct t_config_option *spell_config_color_suggestion; +extern struct t_config_option *spell_config_color_suggestion_delimiter_dict; +extern struct t_config_option *spell_config_color_suggestion_delimiter_word; + +extern struct t_config_option *spell_config_check_commands; +extern struct t_config_option *spell_config_check_default_dict; +extern struct t_config_option *spell_config_check_during_search; +extern struct t_config_option *spell_config_check_enabled; +extern struct t_config_option *spell_config_check_real_time; +extern struct t_config_option *spell_config_check_suggestions; +extern struct t_config_option *spell_config_check_word_min_length; + +extern struct t_config_option *spell_config_look_suggestion_delimiter_dict; +extern struct t_config_option *spell_config_look_suggestion_delimiter_word; + +extern char **spell_commands_to_check; +extern int spell_count_commands_to_check; +extern int *spell_length_commands_to_check; + +extern struct t_config_option *spell_config_get_dict (const char *name); +extern int spell_config_set_dict (const char *name, const char *value); +extern int spell_config_init (); +extern int spell_config_read (); +extern int spell_config_write (); +extern void spell_config_free (); + +#endif /* WEECHAT_PLUGIN_SPELL_CONFIG_H */ diff --git a/src/plugins/spell/spell-info.c b/src/plugins/spell/spell-info.c new file mode 100644 index 000000000..27024c09a --- /dev/null +++ b/src/plugins/spell/spell-info.c @@ -0,0 +1,89 @@ +/* + * spell-info.c - info for spell checker plugin + * + * Copyright (C) 2013-2019 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WeeChat. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "../weechat-plugin.h" +#include "spell.h" + + +/* + * Returns spell info "spell_dict". + */ + +const char * +spell_info_info_spell_dict_cb (const void *pointer, void *data, + const char *info_name, + const char *arguments) +{ + int rc; + unsigned long value; + struct t_gui_buffer *buffer; + const char *buffer_full_name; + + /* make C compiler happy */ + (void) pointer; + (void) data; + (void) info_name; + + if (!arguments) + return NULL; + + buffer_full_name = NULL; + if (strncmp (arguments, "0x", 2) == 0) + { + rc = sscanf (arguments, "%lx", &value); + if ((rc != EOF) && (rc != 0)) + { + buffer = (struct t_gui_buffer *)value; + if (buffer) + { + buffer_full_name = weechat_buffer_get_string (buffer, + "full_name"); + } + } + } + else + buffer_full_name = arguments; + + if (buffer_full_name) + return spell_get_dict_with_buffer_name (buffer_full_name); + + return NULL; +} + +/* + * Hooks info for spell plugin. + */ + +void +spell_info_init () +{ + /* info hooks */ + weechat_hook_info ( + "spell_dict", + N_("comma-separated list of dictionaries used in buffer"), + N_("buffer pointer (\"0x12345678\") or buffer full name " + "(\"irc.freenode.#weechat\")"), + &spell_info_info_spell_dict_cb, NULL, NULL); +} diff --git a/src/plugins/spell/spell-info.h b/src/plugins/spell/spell-info.h new file mode 100644 index 000000000..e529236be --- /dev/null +++ b/src/plugins/spell/spell-info.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2013-2019 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WeeChat. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef WEECHAT_PLUGIN_SPELL_INFO_H +#define WEECHAT_PLUGIN_SPELL_INFO_H + +extern void spell_info_init (); + +#endif /* WEECHAT_PLUGIN_SPELL_INFO_H */ diff --git a/src/plugins/spell/spell-speller.c b/src/plugins/spell/spell-speller.c new file mode 100644 index 000000000..0b0ac5492 --- /dev/null +++ b/src/plugins/spell/spell-speller.c @@ -0,0 +1,486 @@ +/* + * spell-speller.c - speller management for spell checker plugin + * + * Copyright (C) 2006 Emmanuel Bouthenot <kolter@openics.org> + * Copyright (C) 2006-2019 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WeeChat. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <string.h> + +#include "../weechat-plugin.h" +#include "spell.h" +#include "spell-speller.h" +#include "spell-config.h" + + +/* + * spellers: one by dictionary (key is name of dictionary (eg: "fr"), value is + * pointer on AspellSpeller) + */ +struct t_hashtable *spell_spellers = NULL; + +/* + * spellers by buffer (key is buffer pointer, value is pointer on + * struct t_spell_speller_buffer) + */ +struct t_hashtable *spell_speller_buffer = NULL; + + +/* + * Checks if a spelling dictionary is supported (installed on system). + * + * Returns: + * 1: spell dict is supported + * 0: spell dict is NOT supported + */ + +int +spell_speller_dict_supported (const char *lang) +{ +#ifdef USE_ENCHANT + return enchant_broker_dict_exists (broker, lang); +#else + struct AspellConfig *config; + AspellDictInfoList *list; + AspellDictInfoEnumeration *elements; + const AspellDictInfo *dict; + int rc; + + rc = 0; + + config = new_aspell_config (); + list = get_aspell_dict_info_list (config); + elements = aspell_dict_info_list_elements (list); + + while ((dict = aspell_dict_info_enumeration_next (elements)) != NULL) + { + if (strcmp (dict->name, lang) == 0) + { + rc = 1; + break; + } + } + + delete_aspell_dict_info_enumeration (elements); + delete_aspell_config (config); + + return rc; +#endif /* USE_ENCHANT */ +} + +/* + * Checks if dictionaries are valid (called when user creates/changes + * dictionaries for a buffer). + * + * An error is displayed for each invalid dictionary found. + */ + +void +spell_speller_check_dictionaries (const char *dict_list) +{ + char **argv; + int argc, i; + + if (dict_list) + { + argv = weechat_string_split (dict_list, ",", 0, 0, &argc); + if (argv) + { + for (i = 0; i < argc; i++) + { + if (!spell_speller_dict_supported (argv[i])) + { + weechat_printf (NULL, + _("%s: warning: dictionary \"%s\" is not " + "available on your system"), + SPELL_PLUGIN_NAME, argv[i]); + } + } + weechat_string_free_split (argv); + } + } +} + +/* + * Creates and adds a new speller instance in the hashtable. + * + * Returns pointer to new speller, NULL if error. + */ + +#ifdef USE_ENCHANT +EnchantDict * +#else +AspellSpeller * +#endif /* USE_ENCHANT */ +spell_speller_new (const char *lang) +{ +#ifdef USE_ENCHANT + EnchantDict *new_speller; +#else + AspellConfig *config; + AspellCanHaveError *ret; + AspellSpeller *new_speller; +#endif /* USE_ENCHANT */ + struct t_infolist *infolist; + + if (!lang) + return NULL; + + if (weechat_spell_plugin->debug) + { + weechat_printf (NULL, + "%s: creating new speller for lang \"%s\"", + SPELL_PLUGIN_NAME, lang); + } + +#ifdef USE_ENCHANT + new_speller = enchant_broker_request_dict (broker, lang); + if (!new_speller) + { + weechat_printf (NULL, + _("%s%s: error: unable to create speller for lang \"%s\""), + weechat_prefix ("error"), SPELL_PLUGIN_NAME, + lang); + return NULL; + } +#else + /* create a speller instance for the newly created cell */ + config = new_aspell_config (); + aspell_config_replace (config, "lang", lang); +#endif /* USE_ENCHANT */ + + /* apply all options */ + infolist = weechat_infolist_get ("option", NULL, "spell.option.*"); + if (infolist) + { + while (weechat_infolist_next (infolist)) + { +#ifdef USE_ENCHANT + /* TODO: set option with enchant */ +#else + aspell_config_replace (config, + weechat_infolist_string (infolist, "option_name"), + weechat_infolist_string (infolist, "value")); +#endif /* USE_ENCHANT */ + } + weechat_infolist_free (infolist); + } + +#ifndef USE_ENCHANT + ret = new_aspell_speller (config); + + if (aspell_error (ret) != 0) + { + weechat_printf (NULL, + "%s%s: error: %s", + weechat_prefix ("error"), SPELL_PLUGIN_NAME, + aspell_error_message (ret)); + delete_aspell_config (config); + delete_aspell_can_have_error (ret); + return NULL; + } + + new_speller = to_aspell_speller (ret); +#endif /* USE_ENCHANT */ + + weechat_hashtable_set (spell_spellers, lang, new_speller); + +#ifndef USE_ENCHANT + /* free configuration */ + delete_aspell_config (config); +#endif /* USE_ENCHANT */ + + return new_speller; +} + +/* + * Creates hashtable entries with a string containing a list of dicts. + */ + +void +spell_speller_add_dicts_to_hash (struct t_hashtable *hashtable, + const char *dict) +{ + char **dicts; + int num_dicts, i; + + if (!dict || !dict[0]) + return; + + dicts = weechat_string_split (dict, ",", 0, 0, &num_dicts); + if (dicts) + { + for (i = 0; i < num_dicts; i++) + { + weechat_hashtable_set (hashtable, dicts[i], NULL); + } + weechat_string_free_split (dicts); + } +} + +/* + * Removes a speller if it is NOT in hashtable "used_spellers". + */ + +void +spell_speller_remove_unused_cb (void *data, + struct t_hashtable *hashtable, + const void *key, const void *value) +{ + struct t_hashtable *used_spellers; + + /* make C compiler happy */ + (void) value; + + used_spellers = (struct t_hashtable *)data; + + /* if speller is not in "used_spellers", remove it (not used any more) */ + if (!weechat_hashtable_has_key (used_spellers, key)) + weechat_hashtable_remove (hashtable, key); +} + +/* + * Removes unused spellers from hashtable "spell_spellers". + */ + +void +spell_speller_remove_unused () +{ + struct t_hashtable *used_spellers; + struct t_infolist *infolist; + + if (weechat_spell_plugin->debug) + { + weechat_printf (NULL, + "%s: removing unused spellers", + SPELL_PLUGIN_NAME); + } + + /* create a hashtable that will contain all used spellers */ + used_spellers = weechat_hashtable_new (32, + WEECHAT_HASHTABLE_STRING, + WEECHAT_HASHTABLE_STRING, + NULL, NULL); + if (!used_spellers) + return; + + /* collect used spellers and store them in hashtable "used_spellers" */ + spell_speller_add_dicts_to_hash (used_spellers, + weechat_config_string (spell_config_check_default_dict)); + infolist = weechat_infolist_get ("option", NULL, "spell.dict.*"); + if (infolist) + { + while (weechat_infolist_next (infolist)) + { + spell_speller_add_dicts_to_hash (used_spellers, + weechat_infolist_string (infolist, "value")); + } + weechat_infolist_free (infolist); + } + + /* + * look at current spellers, and remove spellers that are not in hashtable + * "used_spellers" + */ + weechat_hashtable_map (spell_spellers, + &spell_speller_remove_unused_cb, + used_spellers); + + weechat_hashtable_free (used_spellers); +} + +/* + * Callback called when a key is removed in hashtable "spell_spellers". + */ + +void +spell_speller_free_value_cb (struct t_hashtable *hashtable, + const void *key, void *value) +{ +#ifdef USE_ENCHANT + EnchantDict *ptr_speller; +#else + AspellSpeller *ptr_speller; +#endif /* USE_ENCHANT */ + + /* make C compiler happy */ + (void) hashtable; + + if (weechat_spell_plugin->debug) + { + weechat_printf (NULL, + "%s: removing speller for lang \"%s\"", + SPELL_PLUGIN_NAME, (const char *)key); + } + + /* free speller */ +#ifdef USE_ENCHANT + ptr_speller = (EnchantDict *)value; + enchant_broker_free_dict (broker, ptr_speller); +#else + ptr_speller = (AspellSpeller *)value; + aspell_speller_save_all_word_lists (ptr_speller); + delete_aspell_speller (ptr_speller); +#endif /* USE_ENCHANT */ +} + +/* + * Creates a structure for buffer speller info in hashtable + * "spell_buffer_spellers". + */ + +struct t_spell_speller_buffer * +spell_speller_buffer_new (struct t_gui_buffer *buffer) +{ + const char *buffer_dicts; + char **dicts; + int num_dicts, i; + struct t_spell_speller_buffer *new_speller_buffer; +#ifdef USE_ENCHANT + EnchantDict *ptr_speller; +#else + AspellSpeller *ptr_speller; +#endif /* USE_ENCHANT */ + + if (!buffer) + return NULL; + + weechat_hashtable_remove (spell_speller_buffer, buffer); + + new_speller_buffer = malloc (sizeof (*new_speller_buffer)); + if (!new_speller_buffer) + return NULL; + + new_speller_buffer->spellers = NULL; + new_speller_buffer->modifier_string = NULL; + new_speller_buffer->input_pos = -1; + new_speller_buffer->modifier_result = NULL; + + buffer_dicts = spell_get_dict (buffer); + if (buffer_dicts) + { + dicts = weechat_string_split (buffer_dicts, ",", 0, 0, &num_dicts); + if (dicts && (num_dicts > 0)) + { + new_speller_buffer->spellers = +#ifdef USE_ENCHANT + malloc ((num_dicts + 1) * sizeof (EnchantDict *)); +#else + malloc ((num_dicts + 1) * sizeof (AspellSpeller *)); +#endif /* USE_ENCHANT */ + if (new_speller_buffer->spellers) + { + for (i = 0; i < num_dicts; i++) + { + ptr_speller = weechat_hashtable_get (spell_spellers, + dicts[i]); + if (!ptr_speller) + ptr_speller = spell_speller_new (dicts[i]); + new_speller_buffer->spellers[i] = ptr_speller; + } + new_speller_buffer->spellers[num_dicts] = NULL; + } + } + if (dicts) + weechat_string_free_split (dicts); + } + + weechat_hashtable_set (spell_speller_buffer, + buffer, + new_speller_buffer); + + weechat_bar_item_update ("spell_dict"); + + return new_speller_buffer; +} + +/* + * Callback called when a key is removed in hashtable + * "spell_speller_buffer". + */ + +void +spell_speller_buffer_free_value_cb (struct t_hashtable *hashtable, + const void *key, void *value) +{ + struct t_spell_speller_buffer *ptr_speller_buffer; + + /* make C compiler happy */ + (void) hashtable; + (void) key; + + ptr_speller_buffer = (struct t_spell_speller_buffer *)value; + + if (ptr_speller_buffer->spellers) + free (ptr_speller_buffer->spellers); + if (ptr_speller_buffer->modifier_string) + free (ptr_speller_buffer->modifier_string); + if (ptr_speller_buffer->modifier_result) + free (ptr_speller_buffer->modifier_result); + + free (ptr_speller_buffer); +} + +/* + * Initializes spellers (creates hashtables). + * + * Returns: + * 1: OK (hashtables created) + * 0: error (not enough memory) + */ + +int +spell_speller_init () +{ + spell_spellers = weechat_hashtable_new (32, + WEECHAT_HASHTABLE_STRING, + WEECHAT_HASHTABLE_POINTER, + NULL, NULL); + if (!spell_spellers) + return 0; + weechat_hashtable_set_pointer (spell_spellers, + "callback_free_value", + &spell_speller_free_value_cb); + + spell_speller_buffer = weechat_hashtable_new (32, + WEECHAT_HASHTABLE_POINTER, + WEECHAT_HASHTABLE_POINTER, + NULL, NULL); + if (!spell_speller_buffer) + { + weechat_hashtable_free (spell_spellers); + return 0; + } + weechat_hashtable_set_pointer (spell_speller_buffer, + "callback_free_value", + &spell_speller_buffer_free_value_cb); + + return 1; +} + +/* + * Ends spellers (removes hashtables). + */ + +void +spell_speller_end () +{ + weechat_hashtable_free (spell_spellers); + weechat_hashtable_free (spell_speller_buffer); +} diff --git a/src/plugins/spell/spell-speller.h b/src/plugins/spell/spell-speller.h new file mode 100644 index 000000000..2d47d7e24 --- /dev/null +++ b/src/plugins/spell/spell-speller.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2006 Emmanuel Bouthenot <kolter@openics.org> + * Copyright (C) 2006-2019 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WeeChat. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef WEECHAT_PLUGIN_SPELL_SPELLER_H +#define WEECHAT_PLUGIN_SPELL_SPELLER_H + +struct t_spell_speller_buffer +{ +#ifdef USE_ENCHANT + EnchantDict **spellers; /* enchant spellers for buffer */ +#else + AspellSpeller **spellers; /* aspell spellers for buffer */ +#endif /* USE_ENCHANT */ + char *modifier_string; /* last modifier string */ + int input_pos; /* position of cursor in input */ + char *modifier_result; /* last modifier result */ +}; + +extern struct t_hashtable *spell_spellers; +extern struct t_hashtable *spell_speller_buffer; + +extern int spell_speller_dict_supported (const char *lang); +extern void spell_speller_check_dictionaries (const char *dict_list); +#ifdef USE_ENCHANT +extern EnchantDict *spell_speller_new (const char *lang); +#else +extern AspellSpeller *spell_speller_new (const char *lang); +#endif /* USE_ENCHANT */ +extern void spell_speller_remove_unused (); +extern struct t_spell_speller_buffer *spell_speller_buffer_new (struct t_gui_buffer *buffer); +extern int spell_speller_init (); +extern void spell_speller_end (); + +#endif /* WEECHAT_PLUGIN_SPELL_SPELLER_H */ diff --git a/src/plugins/spell/spell.c b/src/plugins/spell/spell.c new file mode 100644 index 000000000..b39e5ce54 --- /dev/null +++ b/src/plugins/spell/spell.c @@ -0,0 +1,1167 @@ +/* + * spell.c - spell checker plugin for WeeChat + * + * Copyright (C) 2006 Emmanuel Bouthenot <kolter@openics.org> + * Copyright (C) 2006-2019 Sébastien Helleu <flashcode@flashtux.org> + * Copyright (C) 2012 Nils Görs <weechatter@arcor.de> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WeeChat. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/time.h> +#include <wctype.h> + +#include "../weechat-plugin.h" +#include "spell.h" +#include "spell-bar-item.h" +#include "spell-command.h" +#include "spell-completion.h" +#include "spell-config.h" +#include "spell-info.h" +#include "spell-speller.h" + + +WEECHAT_PLUGIN_NAME(SPELL_PLUGIN_NAME); +WEECHAT_PLUGIN_DESCRIPTION(N_("Spell checker for input")); +WEECHAT_PLUGIN_AUTHOR("Sébastien Helleu <flashcode@flashtux.org>"); +WEECHAT_PLUGIN_VERSION(WEECHAT_VERSION); +WEECHAT_PLUGIN_LICENSE(WEECHAT_LICENSE); +WEECHAT_PLUGIN_PRIORITY(11000); + +struct t_weechat_plugin *weechat_spell_plugin = NULL; + +int spell_enabled = 0; + +char *spell_nick_completer = NULL; +int spell_len_nick_completer = 0; + +#ifdef USE_ENCHANT +EnchantBroker *broker = NULL; +#endif /* USE_ENCHANT */ + +/* + * aspell supported languages, updated on 2012-07-05 + * URL: ftp://ftp.gnu.org/gnu/aspell/dict/0index.html + */ + +struct t_spell_code spell_langs[] = +{ + { "af", "Afrikaans" }, + { "am", "Amharic" }, + { "ar", "Arabic" }, + { "ast", "Asturian" }, + { "az", "Azerbaijani" }, + { "be", "Belarusian" }, + { "bg", "Bulgarian" }, + { "bn", "Bengali" }, + { "br", "Breton" }, + { "ca", "Catalan" }, + { "cs", "Czech" }, + { "csb", "Kashubian" }, + { "cy", "Welsh" }, + { "da", "Danish" }, + { "de", "German" }, + { "de-alt", "German - Old Spelling" }, + { "el", "Greek" }, + { "en", "English" }, + { "eo", "Esperanto" }, + { "es", "Spanish" }, + { "et", "Estonian" }, + { "fa", "Persian" }, + { "fi", "Finnish" }, + { "fo", "Faroese" }, + { "fr", "French" }, + { "fy", "Frisian" }, + { "ga", "Irish" }, + { "gd", "Scottish Gaelic" }, + { "gl", "Galician" }, + { "grc", "Ancient Greek" }, + { "gu", "Gujarati" }, + { "gv", "Manx Gaelic" }, + { "he", "Hebrew" }, + { "hi", "Hindi" }, + { "hil", "Hiligaynon" }, + { "hr", "Croatian" }, + { "hsb", "Upper Sorbian" }, + { "hu", "Hungarian" }, + { "hus", "Huastec" }, + { "hy", "Armenian" }, + { "ia", "Interlingua" }, + { "id", "Indonesian" }, + { "is", "Icelandic" }, + { "it", "Italian" }, + { "kn", "Kannada" }, + { "ku", "Kurdi" }, + { "ky", "Kirghiz" }, + { "la", "Latin" }, + { "lt", "Lithuanian" }, + { "lv", "Latvian" }, + { "mg", "Malagasy" }, + { "mi", "Maori" }, + { "mk", "Macedonian" }, + { "ml", "Malayalam" }, + { "mn", "Mongolian" }, + { "mr", "Marathi" }, + { "ms", "Malay" }, + { "mt", "Maltese" }, + { "nb", "Norwegian Bokmal" }, + { "nds", "Low Saxon" }, + { "nl", "Dutch" }, + { "nn", "Norwegian Nynorsk" }, + { "ny", "Chichewa" }, + { "or", "Oriya" }, + { "pa", "Punjabi" }, + { "pl", "Polish" }, + { "pt_BR", "Brazilian Portuguese" }, + { "pt_PT", "Portuguese" }, + { "qu", "Quechua" }, + { "ro", "Romanian" }, + { "ru", "Russian" }, + { "rw", "Kinyarwanda" }, + { "sc", "Sardinian" }, + { "sk", "Slovak" }, + { "sl", "Slovenian" }, + { "sr", "Serbian" }, + { "sv", "Swedish" }, + { "sw", "Swahili" }, + { "ta", "Tamil" }, + { "te", "Telugu" }, + { "tet", "Tetum" }, + { "tk", "Turkmen" }, + { "tl", "Tagalog" }, + { "tn", "Setswana" }, + { "tr", "Turkish" }, + { "uk", "Ukrainian" }, + { "uz", "Uzbek" }, + { "vi", "Vietnamese" }, + { "wa", "Walloon" }, + { "yi", "Yiddish" }, + { "zu", "Zulu" }, + { NULL, NULL} +}; + +struct t_spell_code spell_countries[] = +{ + { "AT", "Austria" }, + { "BR", "Brazil" }, + { "CA", "Canada" }, + { "CH", "Switzerland" }, + { "DE", "Germany" }, + { "FR", "France" }, + { "GB", "Great Britain" }, + { "PT", "Portugal" }, + { "SK", "Slovakia" }, + { "US", "United States of America" }, + { NULL, NULL} +}; + +char *spell_url_prefix[] = +{ "http:", "https:", "ftp:", "tftp:", "ftps:", "ssh:", "fish:", "dict:", + "ldap:", "file:", "telnet:", "gopher:", "irc:", "ircs:", "irc6:", "irc6s:", + "cvs:", "svn:", "svn+ssh:", "git:", NULL }; + + +/* + * Builds full name of buffer. + * + * Note: result must be freed after use. + */ + +char * +spell_build_option_name (struct t_gui_buffer *buffer) +{ + const char *plugin_name, *name; + char *option_name; + int length; + + if (!buffer) + return NULL; + + plugin_name = weechat_buffer_get_string (buffer, "plugin"); + name = weechat_buffer_get_string (buffer, "name"); + + length = strlen (plugin_name) + 1 + strlen (name) + 1; + option_name = malloc (length); + if (!option_name) + return NULL; + + snprintf (option_name, length, "%s.%s", plugin_name, name); + + return option_name; +} + +/* + * Gets dictionary list for a name of buffer. + * + * First tries with all arguments, then removes one by one to find dict (from + * specific to general dict). + */ + +const char * +spell_get_dict_with_buffer_name (const char *name) +{ + char *option_name, *ptr_end; + struct t_config_option *ptr_option; + + if (!name) + return NULL; + + option_name = strdup (name); + if (option_name) + { + ptr_end = option_name + strlen (option_name); + while (ptr_end >= option_name) + { + ptr_option = spell_config_get_dict (option_name); + if (ptr_option) + { + free (option_name); + return weechat_config_string (ptr_option); + } + ptr_end--; + while ((ptr_end >= option_name) && (ptr_end[0] != '.')) + { + ptr_end--; + } + if ((ptr_end >= option_name) && (ptr_end[0] == '.')) + ptr_end[0] = '\0'; + } + ptr_option = spell_config_get_dict (option_name); + + free (option_name); + + if (ptr_option) + return weechat_config_string (ptr_option); + } + + /* nothing found => return default dictionary (if set) */ + if (weechat_config_string (spell_config_check_default_dict) + && weechat_config_string (spell_config_check_default_dict)[0]) + { + return weechat_config_string (spell_config_check_default_dict); + } + + /* no default dictionary set */ + return NULL; +} + +/* + * Gets dictionary list for a buffer. + * + * First tries with all arguments, then removes one by one to find dict (from + * specific to general dict). + */ + +const char * +spell_get_dict (struct t_gui_buffer *buffer) +{ + char *name; + const char *dict; + + name = spell_build_option_name (buffer); + if (!name) + return NULL; + + dict = spell_get_dict_with_buffer_name (name); + + free (name); + + return dict; +} + +/* + * Checks if command is authorized for spell checking. + * + * Returns: + * 1: command authorized + * 0: command not authorized + */ + +int +spell_command_authorized (const char *command) +{ + int length_command, i; + + if (!command) + return 1; + + length_command = strlen (command); + + for (i = 0; i < spell_count_commands_to_check; i++) + { + if ((spell_length_commands_to_check[i] == length_command) + && (weechat_strcasecmp (command, + spell_commands_to_check[i]) == 0)) + { + /* command is authorized */ + return 1; + } + } + + /* command is not authorized */ + return 0; +} + +/* + * Checks if a word is an URL. + * + * Returns: + * 1: word is an URL + * 0: word is not an URL + */ + +int +spell_string_is_url (const char *word) +{ + int i; + + for (i = 0; spell_url_prefix[i]; i++) + { + if (weechat_strncasecmp (word, spell_url_prefix[i], + strlen (spell_url_prefix[i])) == 0) + return 1; + } + + /* word is not an URL */ + return 0; +} + +/* + * Checks if a word is a nick of nicklist. + * + * Returns: + * 1: word is a nick of nicklist + * 0: word is not a nick of nicklist + */ + +int +spell_string_is_nick (struct t_gui_buffer *buffer, const char *word) +{ + char *pos, *pos_nick_completer, *pos_space, saved_char; + const char *buffer_type, *buffer_nick, *buffer_channel; + int rc; + + pos_nick_completer = (spell_nick_completer) ? + strstr (word, spell_nick_completer) : NULL; + pos_space = strchr (word, ' '); + + pos = NULL; + if (pos_nick_completer && pos_space) + { + if ((pos_nick_completer < pos_space) + && (pos_nick_completer + spell_len_nick_completer == pos_space)) + { + pos = pos_nick_completer; + } + else + pos = pos_space; + } + else + { + pos = (pos_nick_completer + && !pos_nick_completer[spell_len_nick_completer]) ? + pos_nick_completer : pos_space; + } + + if (pos) + { + saved_char = pos[0]; + pos[0] = '\0'; + } + + rc = (weechat_nicklist_search_nick (buffer, NULL, word)) ? 1 : 0; + + if (!rc) + { + /* for "private" buffers, check if word is self or remote nick */ + buffer_type = weechat_buffer_get_string (buffer, "localvar_type"); + if (buffer_type && (strcmp (buffer_type, "private") == 0)) + { + /* check self nick */ + buffer_nick = weechat_buffer_get_string (buffer, "localvar_nick"); + if (buffer_nick && (weechat_strcasecmp (buffer_nick, word) == 0)) + { + rc = 1; + } + else + { + /* check remote nick */ + buffer_channel = weechat_buffer_get_string (buffer, + "localvar_channel"); + if (buffer_channel + && (weechat_strcasecmp (buffer_channel, word) == 0)) + { + rc = 1; + } + } + } + } + + if (pos) + pos[0] = saved_char; + + return rc; +} + +/* + * Checks if a word is made of digits and punctuation. + * + * Returns: + * 1: word has only digits and punctuation + * 0: word has some other chars (not digits neither punctuation) + */ + +int +spell_string_is_simili_number (const char *word) +{ + int code_point; + + if (!word || !word[0]) + return 0; + + while (word && word[0]) + { + code_point = weechat_utf8_char_int (word); + if (!iswpunct (code_point) && !iswdigit (code_point)) + return 0; + word = weechat_utf8_next_char (word); + } + + /* there are only digits or punctuation */ + return 1; +} + +/* + * Spell checks a word. + * + * Returns: + * 1: word is OK + * 0: word is misspelled + */ + +int +spell_check_word (struct t_spell_speller_buffer *speller_buffer, + const char *word) +{ + int i; + + /* word too small? then do not check word */ + if ((weechat_config_integer (spell_config_check_word_min_length) > 0) + && ((int)strlen (word) < weechat_config_integer (spell_config_check_word_min_length))) + return 1; + + /* word is a number? then do not check word */ + if (spell_string_is_simili_number (word)) + return 1; + + /* check word with all spellers (order is important) */ + if (speller_buffer->spellers) + { + for (i = 0; speller_buffer->spellers[i]; i++) + { +#ifdef USE_ENCHANT + if (enchant_dict_check (speller_buffer->spellers[i], word, strlen (word)) == 0) +#else + if (aspell_speller_check (speller_buffer->spellers[i], word, -1) == 1) +#endif /* USE_ENCHANT */ + return 1; + } + } + + /* misspelled word! */ + return 0; +} + +/* + * Gets suggestions for a word. + * + * Returns a string with format: "suggest1,suggest2,suggest3". + * + * Note: result must be freed after use (if not NULL). + */ + +char * +spell_get_suggestions (struct t_spell_speller_buffer *speller_buffer, + const char *word) +{ + int i, size, max_suggestions, num_suggestions; + char *suggestions, *suggestions2; + const char *ptr_word; +#ifdef USE_ENCHANT + char **elements; + size_t num_elements; +#else + const AspellWordList *list; + AspellStringEnumeration *elements; +#endif /* USE_ENCHANT */ + + max_suggestions = weechat_config_integer (spell_config_check_suggestions); + if (max_suggestions < 0) + return NULL; + + size = 1; + suggestions = malloc (size); + if (!suggestions) + return NULL; + + suggestions[0] = '\0'; + if (speller_buffer->spellers) + { + for (i = 0; speller_buffer->spellers[i]; i++) + { +#ifdef USE_ENCHANT + elements = enchant_dict_suggest (speller_buffer->spellers[i], word, + -1, &num_elements); + if (elements) + { + if (num_elements > 0) + { + num_suggestions = 0; + while ((ptr_word = elements[num_suggestions]) != NULL) + { + size += strlen (ptr_word) + ((suggestions[0]) ? 1 : 0); + suggestions2 = realloc (suggestions, size); + if (!suggestions2) + { + free (suggestions); + enchant_dict_free_string_list (speller_buffer->spellers[i], + elements); + return NULL; + } + suggestions = suggestions2; + if (suggestions[0]) + strcat (suggestions, (num_suggestions == 0) ? "/" : ","); + strcat (suggestions, ptr_word); + num_suggestions++; + if (num_suggestions == max_suggestions) + break; + } + } + enchant_dict_free_string_list (speller_buffer->spellers[i], elements); + } +#else + list = aspell_speller_suggest (speller_buffer->spellers[i], word, -1); + if (list) + { + elements = aspell_word_list_elements (list); + num_suggestions = 0; + while ((ptr_word = aspell_string_enumeration_next (elements)) != NULL) + { + size += strlen (ptr_word) + ((suggestions[0]) ? 1 : 0); + suggestions2 = realloc (suggestions, size); + if (!suggestions2) + { + free (suggestions); + delete_aspell_string_enumeration (elements); + return NULL; + } + suggestions = suggestions2; + if (suggestions[0]) + strcat (suggestions, (num_suggestions == 0) ? "/" : ","); + strcat (suggestions, ptr_word); + num_suggestions++; + if (num_suggestions == max_suggestions) + break; + } + delete_aspell_string_enumeration (elements); + } +#endif /* USE_ENCHANT */ + } + } + + /* no suggestions found */ + if (!suggestions[0]) + { + free (suggestions); + return NULL; + } + + return suggestions; +} + +/* + * Updates input text by adding color for misspelled words. + */ + +char * +spell_modifier_cb (const void *pointer, void *data, + const char *modifier, + const char *modifier_data, const char *string) +{ + unsigned long value; + struct t_gui_buffer *buffer; + struct t_spell_speller_buffer *ptr_speller_buffer; + char *result, *ptr_string, *ptr_string_orig, *pos_space; + char *ptr_end, *ptr_end_valid, save_end; + char *misspelled_word, *old_misspelled_word, *old_suggestions, *suggestions; + char *word_and_suggestions; + const char *color_normal, *color_error, *ptr_suggestions, *pos_colon; + int code_point, char_size; + int length, index_result, length_word, word_ok; + int length_color_normal, length_color_error, rc; + int input_pos, current_pos, word_start_pos, word_end_pos, word_end_pos_valid; + + /* make C compiler happy */ + (void) pointer; + (void) data; + (void) modifier; + + if (!spell_enabled) + return NULL; + + if (!string) + return NULL; + + rc = sscanf (modifier_data, "%lx", &value); + if ((rc == EOF) || (rc == 0)) + return NULL; + + buffer = (struct t_gui_buffer *)value; + + /* check text during search only if option is enabled */ + if (weechat_buffer_get_integer (buffer, "text_search") + && !weechat_config_boolean (spell_config_check_during_search)) + return NULL; + + /* get structure with speller info for buffer */ + ptr_speller_buffer = weechat_hashtable_get (spell_speller_buffer, + buffer); + if (!ptr_speller_buffer) + { + ptr_speller_buffer = spell_speller_buffer_new (buffer); + if (!ptr_speller_buffer) + return NULL; + } + if (!ptr_speller_buffer->spellers) + return NULL; + + /* + * for performance: return last string built if input string is the + * same (and cursor position is the same, if suggestions are enabled) + */ + input_pos = weechat_buffer_get_integer (buffer, "input_pos"); + if (ptr_speller_buffer->modifier_string + && (strcmp (string, ptr_speller_buffer->modifier_string) == 0) + && ((weechat_config_integer (spell_config_check_suggestions) < 0) + || (input_pos == ptr_speller_buffer->input_pos))) + { + return (ptr_speller_buffer->modifier_result) ? + strdup (ptr_speller_buffer->modifier_result) : NULL; + } + + /* free last modifier string and result */ + if (ptr_speller_buffer->modifier_string) + { + free (ptr_speller_buffer->modifier_string); + ptr_speller_buffer->modifier_string = NULL; + } + if (ptr_speller_buffer->modifier_result) + { + free (ptr_speller_buffer->modifier_result); + ptr_speller_buffer->modifier_result = NULL; + } + + misspelled_word = NULL; + + /* save last modifier string received */ + ptr_speller_buffer->modifier_string = strdup (string); + ptr_speller_buffer->input_pos = input_pos; + + color_normal = weechat_color ("bar_fg"); + length_color_normal = strlen (color_normal); + color_error = weechat_color (weechat_config_string (spell_config_color_misspelled)); + length_color_error = strlen (color_error); + + length = strlen (string); + result = malloc (length + (length * length_color_error) + 1); + + if (result) + { + result[0] = '\0'; + + ptr_string = ptr_speller_buffer->modifier_string; + index_result = 0; + + /* check if string is a command */ + if (!weechat_string_input_for_buffer (ptr_string)) + { + char_size = weechat_utf8_char_size (ptr_string); + ptr_string += char_size; + pos_space = ptr_string; + while (pos_space && pos_space[0] && (pos_space[0] != ' ')) + { + pos_space = (char *)weechat_utf8_next_char (pos_space); + } + if (!pos_space || !pos_space[0]) + { + free (result); + return NULL; + } + + pos_space[0] = '\0'; + + /* exit if command is not authorized for spell checking */ + if (!spell_command_authorized (ptr_string)) + { + free (result); + return NULL; + } + memcpy (result + index_result, + ptr_speller_buffer->modifier_string, + char_size); + index_result += char_size; + strcpy (result + index_result, ptr_string); + index_result += strlen (ptr_string); + + pos_space[0] = ' '; + ptr_string = pos_space; + } + + current_pos = 0; + while (ptr_string[0]) + { + ptr_string_orig = NULL; + + /* find start of word: it must start with an alphanumeric char */ + code_point = weechat_utf8_char_int (ptr_string); + while ((!iswalnum (code_point)) || iswspace (code_point)) + { + if (!ptr_string_orig && !iswspace (code_point)) + ptr_string_orig = ptr_string; + char_size = weechat_utf8_char_size (ptr_string); + memcpy (result + index_result, ptr_string, char_size); + index_result += char_size; + ptr_string += char_size; + current_pos++; + if (!ptr_string[0]) + break; + code_point = weechat_utf8_char_int (ptr_string); + } + if (!ptr_string[0]) + break; + if (!ptr_string_orig) + ptr_string_orig = ptr_string; + + word_start_pos = current_pos; + word_end_pos = current_pos; + word_end_pos_valid = current_pos; + + /* find end of word: ' and - allowed in word, but not at the end */ + ptr_end_valid = ptr_string; + ptr_end = (char *)weechat_utf8_next_char (ptr_string); + code_point = weechat_utf8_char_int (ptr_end); + while (iswalnum (code_point) || (code_point == '\'') + || (code_point == '-')) + { + word_end_pos++; + if (iswalnum (code_point)) + { + /* pointer to last alphanumeric char in the word */ + ptr_end_valid = ptr_end; + word_end_pos_valid = word_end_pos; + } + ptr_end = (char *)weechat_utf8_next_char (ptr_end); + if (!ptr_end[0]) + break; + code_point = weechat_utf8_char_int (ptr_end); + } + ptr_end = (char *)weechat_utf8_next_char (ptr_end_valid); + word_end_pos = word_end_pos_valid; + word_ok = 0; + if (spell_string_is_url (ptr_string) + || spell_string_is_nick (buffer, ptr_string_orig)) + { + /* + * word is an URL or a nick, then it is OK: search for next + * space (will be end of word) + */ + word_ok = 1; + if (ptr_end[0]) + { + code_point = weechat_utf8_char_int (ptr_end); + while (!iswspace (code_point)) + { + ptr_end = (char *)weechat_utf8_next_char (ptr_end); + if (!ptr_end[0]) + break; + code_point = weechat_utf8_char_int (ptr_end); + } + } + } + save_end = ptr_end[0]; + ptr_end[0] = '\0'; + length_word = ptr_end - ptr_string; + + if (!word_ok) + { + if ((save_end != '\0') + || (weechat_config_integer (spell_config_check_real_time))) + { + word_ok = spell_check_word (ptr_speller_buffer, + ptr_string); + if (!word_ok && (input_pos >= word_start_pos)) + { + /* + * if word is misspelled and that cursor is after + * the beginning of this word, save the word (we will + * look for suggestions after this loop) + */ + if (misspelled_word) + free (misspelled_word); + misspelled_word = strdup (ptr_string); + } + } + else + word_ok = 1; + } + + /* add error color */ + if (!word_ok) + { + strcpy (result + index_result, color_error); + index_result += length_color_error; + } + + /* add word */ + strcpy (result + index_result, ptr_string); + index_result += length_word; + + /* add normal color (after misspelled word) */ + if (!word_ok) + { + strcpy (result + index_result, color_normal); + index_result += length_color_normal; + } + + if (save_end == '\0') + break; + + ptr_end[0] = save_end; + ptr_string = ptr_end; + current_pos = word_end_pos + 1; + } + result[index_result] = '\0'; + } + + /* save old suggestions in buffer */ + ptr_suggestions = weechat_buffer_get_string (buffer, + "localvar_spell_suggest"); + old_suggestions = (ptr_suggestions) ? strdup (ptr_suggestions) : NULL; + + /* if there is a misspelled word, get suggestions and set them in buffer */ + if (misspelled_word) + { + /* + * get the old misspelled word; we'll get suggestions or clear + * local variable "spell_suggest" only if the current misspelled + * word is different + */ + old_misspelled_word = NULL; + if (old_suggestions) + { + pos_colon = strchr (old_suggestions, ':'); + old_misspelled_word = (pos_colon) ? + weechat_strndup (old_suggestions, pos_colon - old_suggestions) : + strdup (old_suggestions); + } + + if (!old_misspelled_word + || (strcmp (old_misspelled_word, misspelled_word) != 0)) + { + suggestions = spell_get_suggestions (ptr_speller_buffer, + misspelled_word); + if (suggestions) + { + length = strlen (misspelled_word) + 1 /* ":" */ + + strlen (suggestions) + 1; + word_and_suggestions = malloc (length); + if (word_and_suggestions) + { + snprintf (word_and_suggestions, length, "%s:%s", + misspelled_word, suggestions); + weechat_buffer_set (buffer, "localvar_set_spell_suggest", + word_and_suggestions); + free (word_and_suggestions); + } + else + { + weechat_buffer_set (buffer, + "localvar_del_spell_suggest", ""); + } + free (suggestions); + } + else + { + /* set a misspelled word in buffer, also without suggestions */ + weechat_buffer_set (buffer, "localvar_set_spell_suggest", + misspelled_word); + } + } + + if (old_misspelled_word) + free (old_misspelled_word); + + free (misspelled_word); + } + else + { + weechat_buffer_set (buffer, "localvar_del_spell_suggest", ""); + } + + /* + * if suggestions have changed, update the bar item + * and send signal "spell_suggest" + */ + ptr_suggestions = weechat_buffer_get_string (buffer, + "localvar_spell_suggest"); + if ((old_suggestions && !ptr_suggestions) + || (!old_suggestions && ptr_suggestions) + || (old_suggestions && ptr_suggestions + && (strcmp (old_suggestions, ptr_suggestions) != 0))) + { + weechat_bar_item_update ("spell_suggest"); + (void) weechat_hook_signal_send ("spell_suggest", + WEECHAT_HOOK_SIGNAL_POINTER, buffer); + } + if (old_suggestions) + free (old_suggestions); + + if (!result) + return NULL; + + ptr_speller_buffer->modifier_result = strdup (result); + + return result; +} + +/* + * Refreshes bar items on signal "buffer_switch". + */ + +int +spell_buffer_switch_cb (const void *pointer, void *data, const char *signal, + const char *type_data, void *signal_data) +{ + /* make C compiler happy */ + (void) pointer; + (void) data; + (void) signal; + (void) type_data; + (void) signal_data; + + /* refresh bar items (for root bars) */ + weechat_bar_item_update ("spell_dict"); + weechat_bar_item_update ("spell_suggest"); + + return WEECHAT_RC_OK; +} + +/* + * Refreshes bar items on signal "window_switch". + */ + +int +spell_window_switch_cb (const void *pointer, void *data, const char *signal, + const char *type_data, void *signal_data) +{ + /* make C compiler happy */ + (void) pointer; + (void) data; + (void) signal; + (void) type_data; + (void) signal_data; + + /* refresh bar items (for root bars) */ + weechat_bar_item_update ("spell_dict"); + weechat_bar_item_update ("spell_suggest"); + + return WEECHAT_RC_OK; +} + +/* + * Removes struct for buffer in hashtable "spell_speller_buffer" on + * signal "buffer_closed". + */ + +int +spell_buffer_closed_cb (const void *pointer, void *data, const char *signal, + const char *type_data, void *signal_data) +{ + /* make C compiler happy */ + (void) pointer; + (void) data; + (void) signal; + (void) type_data; + + weechat_hashtable_remove (spell_speller_buffer, signal_data); + + return WEECHAT_RC_OK; +} + +/* + * Display infos about external libraries used. + */ + +int +spell_debug_libs_cb (const void *pointer, void *data, const char *signal, + const char *type_data, void *signal_data) +{ + /* make C compiler happy */ + (void) pointer; + (void) data; + (void) signal; + (void) type_data; + (void) signal_data; + +#ifdef USE_ENCHANT +#ifdef HAVE_ENCHANT_GET_VERSION + weechat_printf (NULL, " %s: enchant %s", + SPELL_PLUGIN_NAME, enchant_get_version ()); +#else + weechat_printf (NULL, " %s: enchant (?)", SPELL_PLUGIN_NAME); +#endif /* HAVE_ENCHANT_GET_VERSION */ +#else +#ifdef HAVE_ASPELL_VERSION_STRING + weechat_printf (NULL, " %s: aspell %s", + SPELL_PLUGIN_NAME, aspell_version_string ()); +#else + weechat_printf (NULL, " %s: aspell (?)", SPELL_PLUGIN_NAME); +#endif /* HAVE_ASPELL_VERSION_STRING */ +#endif /* USE_ENCHANT */ + + return WEECHAT_RC_OK; +} + +/* + * Callback for changes to option "weechat.completion.nick_completer". + */ + +int +spell_config_change_nick_completer_cb (const void *pointer, void *data, + const char *option, const char *value) +{ + /* make C compiler happy */ + (void) pointer; + (void) data; + (void) option; + + if (spell_nick_completer) + free (spell_nick_completer); + + spell_nick_completer = weechat_string_strip (value, 0, 1, " "); + spell_len_nick_completer = + (spell_nick_completer) ? strlen (spell_nick_completer) : 0; + + return WEECHAT_RC_OK; +} + +/* + * Initializes spell plugin. + */ + +int +weechat_plugin_init (struct t_weechat_plugin *plugin, int argc, char *argv[]) +{ + /* make C compiler happy */ + (void) argc; + (void) argv; + + weechat_plugin = plugin; + +#ifdef USE_ENCHANT + /* acquire enchant broker */ + broker = enchant_broker_init (); + if (!broker) + return WEECHAT_RC_ERROR; +#endif /* USE_ENCHANT */ + + if (!spell_speller_init ()) + return WEECHAT_RC_ERROR; + + if (!spell_config_init ()) + return WEECHAT_RC_ERROR; + + spell_config_read (); + + spell_command_init (); + + spell_completion_init (); + + /* + * callback for spell checking input text + * we use a low priority here, so that other modifiers "input_text_display" + * (from other plugins) will be called before this one + */ + weechat_hook_modifier ("500|input_text_display", + &spell_modifier_cb, NULL, NULL); + + spell_bar_item_init (); + + spell_info_init (); + + weechat_hook_signal ("buffer_switch", + &spell_buffer_switch_cb, NULL, NULL); + weechat_hook_signal ("window_switch", + &spell_window_switch_cb, NULL, NULL); + weechat_hook_signal ("buffer_closed", + &spell_buffer_closed_cb, NULL, NULL); + weechat_hook_signal ("debug_libs", + &spell_debug_libs_cb, NULL, NULL); + + weechat_hook_config ("weechat.completion.nick_completer", + &spell_config_change_nick_completer_cb, + NULL, NULL); + /* manually call callback to initialize */ + spell_config_change_nick_completer_cb ( + NULL, NULL, "weechat.completion.nick_completer", + weechat_config_string ( + weechat_config_get ("weechat.completion.nick_completer"))); + + return WEECHAT_RC_OK; +} + +/* + * Ends spell plugin. + */ + +int +weechat_plugin_end (struct t_weechat_plugin *plugin) +{ + /* make C compiler happy */ + (void) plugin; + + spell_config_write (); + spell_config_free (); + + spell_speller_end (); + +#ifdef USE_ENCHANT + /* release enchant broker */ + enchant_broker_free (broker); +#endif /* USE_ENCHANT */ + + if (spell_nick_completer) + free (spell_nick_completer); + + return WEECHAT_RC_OK; +} diff --git a/src/plugins/spell/spell.h b/src/plugins/spell/spell.h new file mode 100644 index 000000000..8625ea7db --- /dev/null +++ b/src/plugins/spell/spell.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2006 Emmanuel Bouthenot <kolter@openics.org> + * Copyright (C) 2006-2019 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WeeChat. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef WEECHAT_PLUGIN_SPELL_H +#define WEECHAT_PLUGIN_SPELL_H + +#ifdef USE_ENCHANT +#include <enchant.h> +#else +#include <aspell.h> +#endif /* USE_ENCHANT */ + +#define weechat_plugin weechat_spell_plugin +#define SPELL_PLUGIN_NAME "spell" + +struct t_spell_code +{ + char *code; + char *name; +}; + +#ifdef USE_ENCHANT +extern EnchantBroker *broker; +#endif /* USE_ENCHANT */ + +extern struct t_weechat_plugin *weechat_spell_plugin; +extern int spell_enabled; +extern struct t_spell_code spell_langs[]; +extern struct t_spell_code spell_countries[]; + +extern char *spell_build_option_name (struct t_gui_buffer *buffer); +extern const char *spell_get_dict_with_buffer_name (const char *name); +extern const char *spell_get_dict (struct t_gui_buffer *buffer); + +#endif /* WEECHAT_PLUGIN_SPELL_H */ |