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