1package ui
2
3import (
4 "context"
5 "fmt"
6 "log/slog"
7 "sync"
8
9 "github.com/gdamore/tcell/v2"
10 "github.com/rivo/tview"
11 "github.com/urfave/cli/v2"
12
13 "git.gabrielgio.me/dict/db"
14)
15
16var UICommand = &cli.Command{
17 Name: "ui",
18 Usage: "interactive dictionary",
19 Flags: []cli.Flag{
20 &cli.StringFlag{
21 Name: "database",
22 Value: "main.dict",
23 Usage: "Dictionary database location",
24 },
25 },
26 Action: func(cCtx *cli.Context) error {
27 name := cCtx.String("database")
28 return Run(context.Background(), name)
29 },
30}
31
32func Run(ctx context.Context, name string) error {
33 db, err := db.Open(name)
34 if err != nil {
35 return err
36 }
37
38 textView := tview.NewTextView().
39 SetDynamicColors(true).
40 SetRegions(true)
41
42 app := tview.NewApplication()
43
44 search := ""
45 tx := sync.Mutex{}
46
47 input := tview.NewInputField().
48 SetChangedFunc(func(v string) {
49
50 tx.Lock()
51 defer tx.Unlock()
52 search = v
53
54 go func(v string) {
55 words, err := db.SelectDict(ctx, v, 20)
56 if err != nil {
57 return
58 }
59
60 tx.Lock()
61 if search != v {
62 tx.Unlock()
63 return
64 }
65 tx.Unlock()
66
67 textView.Clear()
68 lastWord := ""
69 for _, w := range words {
70 w.Word = tview.Escape(w.Word)
71 w.Line = tview.Escape(w.Line)
72
73 if lastWord == w.Word {
74 fmt.Fprintf(textView, "%s\n", w.Line)
75 } else if lastWord == "" {
76 fmt.Fprintf(textView, "[::bu]%s[-:-:-]\n", w.Word)
77 fmt.Fprintf(textView, "%s\n", w.Line)
78 } else {
79 fmt.Fprintf(textView, "\n[::bu]%s[-:-:-]\n", w.Word)
80 fmt.Fprintf(textView, "%s\n", w.Line)
81 }
82
83 lastWord = w.Word
84 }
85
86 app.Draw()
87 }(v)
88 }).
89 SetAutocompleteFunc(func(v string) []string {
90 if len(v) == 0 {
91 return []string{}
92 }
93
94 vs, err := db.SelectSpell(ctx, v)
95 if err != nil {
96 slog.Error("Error select spelling", "error", err)
97 return []string{}
98 }
99
100 return vs
101 })
102
103 input.SetDoneFunc(func(key tcell.Key) {
104 if key == tcell.KeyEscape {
105 textView.Clear()
106 input.SetText("")
107 }
108 })
109
110 grid := tview.NewGrid().
111 SetRows(1, 0, 3).
112 AddItem(input, 0, 0, 1, 3, 0, 0, false).
113 AddItem(textView, 1, 0, 1, 3, 0, 0, false)
114
115 return app.
116 SetRoot(grid, true).
117 SetFocus(input).
118 Run()
119}