โ– humdrum codex / glint v1.0.2
license AGPL-3.0
1.7 KB raw
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package spell

import (
	"bufio"
	"os"
	"path/filepath"
	"strings"
)

// SetPersonalPath records where the hand-editable personal dictionary lives
// (conventionally ~/.config/glint/dict.txt). It does not touch the file.
func (d *Dict) SetPersonalPath(path string) { d.personalPath = path }

// LoadPersonal reads the personal dictionary into memory, one word per line.
// Blank lines and lines beginning with '#' are ignored. A missing file is not an
// error โ€” the personal dictionary simply starts empty.
func (d *Dict) LoadPersonal() error {
	if d.personalPath == "" {
		return nil
	}
	f, err := os.Open(d.personalPath)
	if err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return err
	}
	defer func() { _ = f.Close() }()
	sc := bufio.NewScanner(f)
	for sc.Scan() {
		w := strings.TrimSpace(sc.Text())
		if w == "" || strings.HasPrefix(w, "#") {
			continue
		}
		d.personal[strings.ToLower(w)] = struct{}{}
	}
	return sc.Err()
}

// Add inserts word into the in-memory personal set and appends it to dict.txt,
// creating the file (and any missing parent directories) if needed. The word is
// stored lowercased so membership stays case-insensitive.
func (d *Dict) Add(word string) error {
	w := strings.ToLower(strings.TrimSpace(word))
	if w == "" {
		return nil
	}
	if _, ok := d.personal[w]; !ok {
		d.personal[w] = struct{}{}
	}
	if d.personalPath == "" {
		return nil
	}
	if err := os.MkdirAll(filepath.Dir(d.personalPath), 0o755); err != nil {
		return err
	}
	f, err := os.OpenFile(d.personalPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
	if err != nil {
		return err
	}
	if _, err := f.WriteString(w + "\n"); err != nil {
		_ = f.Close()
		return err
	}
	return f.Close()
}