2 * Copyright Neil Brown ©2020-2023 <neil@brown.name>
3 * May be distributed under terms of GPLv2 - see file:COPYING
5 * aspell: edlib interface for aspell library.
11 #define PANE_DATA_TYPE struct aspell_data
15 AspellSpeller *speller safe;
18 #include "core-pane.h"
20 static AspellConfig *spell_config;
22 static int trim(const char *safe *wordp safe)
24 const char *wp = *wordp;
30 while (*wp && (ch = get_utf8(&wp, NULL)) < WERR &&
36 /* start is the first alphabetic, wp points beyond it */
37 while (*wp && (ch = get_utf8(&wp, NULL)) < WERR) {
45 static struct map *aspell_map safe;
46 DEF_LOOKUP_CMD(aspell_handle, aspell_map);
48 DEF_CMD(aspell_attach_helper)
50 struct aspell_data *as;
51 AspellCanHaveError *ret;
54 ret = new_aspell_speller(spell_config);
55 if (aspell_error_number(ret)) {
56 LOG("Cannot create speller: %s", aspell_error_message(ret));
59 p = pane_register(ci->focus, 0, &aspell_handle.c);
63 as->speller = safe_cast to_aspell_speller(ret);
65 call("doc:request:aspell:check", p);
66 call("doc:request:aspell:suggest", p);
67 call("doc:request:aspell:set-dict", p);
68 call("doc:request:aspell:add-word", p);
69 call("doc:request:aspell:save", p);
74 DEF_CMD_CLOSED(aspell_close)
76 struct aspell_data *as = ci->home->data;
79 aspell_speller_save_all_word_lists(as->speller);
80 delete_aspell_speller(as->speller);
86 struct aspell_data *as = ci->home->data;
88 const char *word = ci->str;
97 correct = aspell_speller_check(as->speller, word, len);
98 return correct ? 1 : Efalse;
103 int rv = call("doc:notify:aspell:check", ci->focus, 0, NULL, ci->str);
105 if (rv != Efallthrough)
107 call_comm("doc:get-doc", ci->focus, &aspell_attach_helper);
108 return call("doc:notify:aspell:check", ci->focus, 0, NULL, ci->str);
111 DEF_CMD(aspell_suggest)
113 struct aspell_data *as = ci->home->data;
114 const AspellWordList *l;
115 AspellStringEnumeration *el;
117 const char *word = ci->str;
126 l = aspell_speller_suggest(as->speller, word, len);
127 el = aspell_word_list_elements(l);
128 while ((w = aspell_string_enumeration_next(el)) != NULL)
129 comm_call(ci->comm2, "suggest", ci->focus, 0, NULL, w);
130 delete_aspell_string_enumeration(el);
134 DEF_CMD(spell_suggest)
136 int rv = call_comm("doc:notify:aspell:suggest", ci->focus, ci->comm2,
139 if (rv != Efallthrough)
141 call_comm("doc:get-doc", ci->focus, &aspell_attach_helper);
142 return call_comm("doc:notify:aspell:suggest", ci->focus, ci->comm2,
148 struct aspell_data *as = ci->home->data;
150 as->need_save = False;
151 aspell_speller_save_all_word_lists(as->speller);
156 DEF_CMD(aspell_do_save)
158 aspell_save_func(ci);
164 return call_comm("doc:notify:aspell:save", ci->focus, ci->comm2,
165 ci->num, NULL, ci->str);
170 struct aspell_data *as = ci->home->data;
171 const char *word = ci->str;
181 aspell_speller_add_to_personal(as->speller, word, len);
183 call_comm("event:free", ci->home, &aspell_save);
184 as->need_save = True;
185 call_comm("event:timer", ci->home, &aspell_save, 30*1000);
187 aspell_speller_add_to_session(as->speller, word, len);
188 call("doc:notify:spell:dict-changed", ci->home);
194 int rv = call_comm("doc:notify:aspell:add-word", ci->focus, ci->comm2,
195 ci->num, NULL, ci->str);
197 if (rv != Efallthrough)
199 call_comm("doc:get-doc", ci->focus, &aspell_attach_helper);
200 return call_comm("doc:notify:aspell:add-word", ci->focus, ci->comm2,
201 ci->num, NULL, ci->str);
204 DEF_CMD(aspell_set_dict)
206 struct aspell_data *as = ci->home->data;
207 const char *lang = ci->str;
209 AspellCanHaveError *ret;
213 conf2 = aspell_config_clone(spell_config);
214 aspell_config_replace(conf2, "lang", lang);
215 ret = new_aspell_speller(conf2);
216 if (!aspell_error_number(ret)) {
217 delete_aspell_speller(as->speller);
218 as->speller = safe_cast to_aspell_speller(ret);
219 call("doc:notify:spell:dict-changed", ci->focus);
221 delete_aspell_config(conf2);
228 ret = call("doc:notify:aspell:set-dict", ci->focus, 0, NULL,
229 ksuffix(ci, "interactive-cmd-dict-"));
230 if (ret != Efallthrough)
232 call_comm("doc:get-doc", ci->focus, &aspell_attach_helper);
233 return call("doc:notify:aspell:set-dict", ci->focus, 0, NULL,
234 ksuffix(ci, "interactive-cmd-dict-"));
237 static inline bool is_word_body(wint_t ch)
239 /* alphabetics, appostrophies */
240 return iswalpha(ch) || ch == '\'';
243 static inline bool is_word_initial(wint_t ch)
245 /* Word must start with an alphabetic */
249 static inline bool is_word_final(wint_t ch)
251 /* Word must end with an alphabetic */
257 /* Find a word "here" to spell. It must include the first
258 * permitted character after '->mark'.
259 * '->mark' is moved to the end and if ->mark2 is available, it
260 * is moved to the start.
261 * If ->comm2 is available, the word is returned as a string.
262 * This command should ignore any view-specific or doc-specific rules
263 * about where words are allowed, but should honour any rules about
264 * what characters can constitute a word.
271 while ((ch = doc_next(ci->focus, ci->mark)) != WEOF &&
272 !is_word_initial(ch))
278 mark_to_mark(m2, ci->mark);
280 m2 = mark_dup(ci->mark);
281 while ((ch = doc_following(ci->focus, ci->mark)) != WEOF &&
283 doc_next(ci->focus, ci->mark);
284 while ((ch = doc_prior(ci->focus, ci->mark)) != WEOF &&
286 doc_prev(ci->focus, ci->mark);
288 while ((ch = doc_prior(ci->focus, m2)) != WEOF &&
290 doc_prev(ci->focus, m2);
291 while ((ch = doc_following(ci->focus, m2)) != WEOF &&
292 !is_word_initial(ch))
293 doc_next(ci->focus, m2);
295 call_comm("doc:get-str", ci->focus, ci->comm2,
296 0, m2, NULL, 0, ci->mark);
304 /* Find the next word-start after ->mark.
305 * A view or doc might over-ride this to skip over
306 * content that shouldn't be spell-checked.
312 while ((ch = doc_next(ci->focus, ci->mark)) != WEOF &&
313 !is_word_initial(ch))
317 doc_prev(ci->focus, ci->mark);
321 void edlib_init(struct pane *ed safe)
323 spell_config = new_aspell_config();
325 aspell_config_replace(spell_config, "lang", "en_AU");
327 call_comm("global-set-command", ed, &spell_check,
328 0, NULL, "Spell:Check");
329 call_comm("global-set-command", ed, &spell_suggest,
330 0, NULL, "Spell:Suggest");
331 call_comm("global-set-command", ed, &spell_this,
332 0, NULL, "Spell:ThisWord");
333 call_comm("global-set-command", ed, &spell_next,
334 0, NULL, "Spell:NextWord");
335 call_comm("global-set-command", ed, &spell_add,
336 0, NULL, "Spell:AddWord");
337 call_comm("global-set-command", ed, &spell_save,
338 0, NULL, "Spell:Save");
340 call_comm("global-set-command-prefix", ed, &spell_dict,
341 0, NULL, "interactive-cmd-dict-");
343 aspell_map = key_alloc();
344 key_add(aspell_map, "Close", &aspell_close);
345 key_add(aspell_map, "aspell:check", &aspell_check);
346 key_add(aspell_map, "aspell:suggest", &aspell_suggest);
347 key_add(aspell_map, "aspell:set-dict", &aspell_set_dict);
348 key_add(aspell_map, "aspell:add-word", &aspell_add);
349 key_add(aspell_map, "aspell:save", &aspell_do_save);