// Copyright (c) 2026 Storm Dragon // SPDX-License-Identifier: GPL-3.0-or-later // Package spellcheck owns offline dictionary work and personal exceptions. package spellcheck /* #cgo LDFLAGS: -ldl #include "native.h" #include */ import "C" import ( "fmt" "unsafe" ) // Dictionary is used only by the service's worker, including Close. type Dictionary interface { Check(string) (bool, error) Suggest(string) ([]string, error) Close() } type EnchantDictionary struct { native *C.SpellDictionary } func open_dictionary(language string) (Dictionary, error) { if language == "" { return nil, fmt.Errorf("the session has no spelling language") } tag := C.CString(language) defer C.free(unsafe.Pointer(tag)) var status C.int native := C.spell_dictionary_open(tag, &status) if native == nil { switch status { case 1: return nil, fmt.Errorf("Enchant 2 could not be loaded") case 2: return nil, fmt.Errorf("Enchant 2 has an incompatible interface") case 4: return nil, fmt.Errorf("dictionary for %s is unavailable", language) default: return nil, fmt.Errorf("could not initialize Enchant") } } return &EnchantDictionary{native: native}, nil } func (d *EnchantDictionary) Check(word string) (bool, error) { value := C.CString(word) defer C.free(unsafe.Pointer(value)) result := C.spell_dictionary_check(d.native, value, C.size_t(len(word))) if result < 0 { return false, fmt.Errorf("dictionary check failed") } return result == 0, nil } func (d *EnchantDictionary) Suggest(word string) ([]string, error) { value := C.CString(word) defer C.free(unsafe.Pointer(value)) var count C.size_t list := C.spell_dictionary_suggest(d.native, value, C.size_t(len(word)), &count) if list == nil { return nil, nil } defer C.spell_dictionary_free_suggestions(d.native, list) // Preserve provider ranking, with a bounded result set for the picker. var suggestions []string seen := make(map[string]bool) for _, item := range unsafe.Slice(list, int(min(count, 128))) { word := C.GoString(item) if word != "" && !seen[word] { seen[word] = true suggestions = append(suggestions, word) } } return suggestions, nil } func (d *EnchantDictionary) Close() { C.spell_dictionary_close(d.native) }