Skip to content

Commit 0805330

Browse files
committed
Finish port of themes kitten to Go
1 parent 0c20a4d commit 0805330

File tree

5 files changed

+116
-53
lines changed

5 files changed

+116
-53
lines changed

tools/cmd/themes/list.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ import (
66
"fmt"
77

88
"kitty/tools/themes"
9+
"kitty/tools/tty"
910
"kitty/tools/utils"
1011
"kitty/tools/wcswidth"
1112
)
1213

1314
var _ = fmt.Print
15+
var DebugPrintln = tty.DebugPrintln
1416

1517
type ThemesList struct {
1618
themes, all_themes *themes.Themes

tools/cmd/themes/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ func main(_ *cli.Command, opts *Options, args []string) (rc int, err error) {
7979
return nil
8080
}
8181
lp.OnKeyEvent = h.on_key_event
82+
lp.OnText = h.on_text
8283
err = lp.Run()
8384
if err != nil {
8485
return 1, err

tools/cmd/themes/ui.go

Lines changed: 66 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@ func (self *handler) fetch_themes() {
8484
}
8585

8686
func (self *handler) on_fetching_key_event(ev *loop.KeyEvent) error {
87-
if ev.MatchesRelease("esc") {
88-
self.lp.Quit(0)
87+
if ev.MatchesPressOrRepeat("esc") {
88+
self.quit_on_next_key_release = 0
8989
ev.Handled = true
9090
}
9191
return nil
@@ -120,7 +120,7 @@ func (self *handler) finalize() {
120120
func (self *handler) initialize() {
121121
self.quit_on_next_key_release = -1
122122
self.tabs = strings.Split("all dark light recent user", " ")
123-
self.rl = readline.New(self.lp, readline.RlInit{DontMarkPrompts: true, Prompt: "/ "})
123+
self.rl = readline.New(self.lp, readline.RlInit{DontMarkPrompts: true, Prompt: "/"})
124124
self.themes_list = &ThemesList{}
125125
self.fetch_result = make(chan fetch_data)
126126
self.category_filters = make(map[string]func(*themes.Theme) bool, len(category_filters)+1)
@@ -238,13 +238,13 @@ func (self *handler) next(delta int, allow_wrapping bool) {
238238
}
239239

240240
func (self *handler) on_browsing_key_event(ev *loop.KeyEvent) error {
241-
if ev.MatchesRelease("esc") || ev.MatchesRelease("q") {
242-
self.lp.Quit(0)
241+
if ev.MatchesPressOrRepeat("esc") || ev.MatchesPressOrRepeat("q") {
242+
self.quit_on_next_key_release = 0
243243
ev.Handled = true
244244
return nil
245245
}
246246
for _, cat := range self.tabs {
247-
if ev.MatchesRelease(cat[0:1]) || ev.MatchesRelease("alt+"+cat[0:1]) {
247+
if ev.MatchesPressOrRepeat(cat[0:1]) || ev.MatchesPressOrRepeat("alt+"+cat[0:1]) {
248248
ev.Handled = true
249249
if cat != self.current_category() {
250250
self.set_current_category(cat)
@@ -253,47 +253,56 @@ func (self *handler) on_browsing_key_event(ev *loop.KeyEvent) error {
253253
}
254254
}
255255
}
256-
if ev.MatchesRelease("left") || ev.MatchesRelease("shift+tab") {
256+
if ev.MatchesPressOrRepeat("left") || ev.MatchesPressOrRepeat("shift+tab") {
257257
self.next_category(-1)
258258
ev.Handled = true
259259
return nil
260260
}
261-
if ev.MatchesRelease("right") || ev.MatchesRelease("tab") {
261+
if ev.MatchesPressOrRepeat("right") || ev.MatchesPressOrRepeat("tab") {
262262
self.next_category(1)
263263
ev.Handled = true
264264
return nil
265265
}
266-
if ev.MatchesRelease("j") || ev.MatchesRelease("down") {
266+
if ev.MatchesPressOrRepeat("j") || ev.MatchesPressOrRepeat("down") {
267267
self.next(1, true)
268268
ev.Handled = true
269269
return nil
270270
}
271-
if ev.MatchesRelease("k") || ev.MatchesRelease("up") {
271+
if ev.MatchesPressOrRepeat("k") || ev.MatchesPressOrRepeat("up") {
272272
self.next(-1, true)
273273
ev.Handled = true
274274
return nil
275275
}
276-
if ev.MatchesRelease("page_down") {
276+
if ev.MatchesPressOrRepeat("page_down") {
277277
ev.Handled = true
278278
sz, err := self.lp.ScreenSize()
279279
if err == nil {
280280
self.next(int(sz.HeightCells)-3, false)
281281
}
282282
return nil
283283
}
284-
if ev.MatchesRelease("page_up") {
284+
if ev.MatchesPressOrRepeat("page_up") {
285285
ev.Handled = true
286286
sz, err := self.lp.ScreenSize()
287287
if err == nil {
288288
self.next(3-int(sz.HeightCells), false)
289289
}
290290
return nil
291291
}
292-
if ev.MatchesRelease("s") || ev.MatchesRelease("/") {
292+
if ev.MatchesPressOrRepeat("s") || ev.MatchesPressOrRepeat("/") {
293293
ev.Handled = true
294294
self.start_search()
295295
return nil
296296
}
297+
if ev.MatchesPressOrRepeat("c") || ev.MatchesPressOrRepeat("enter") {
298+
ev.Handled = true
299+
if self.themes_list == nil || self.themes_list.Len() == 0 {
300+
self.lp.Beep()
301+
} else {
302+
self.state = ACCEPTING
303+
self.draw_screen()
304+
}
305+
}
297306
return nil
298307
}
299308

@@ -311,21 +320,19 @@ func (self *handler) draw_browsing_screen() {
311320
}
312321
num_rows := int(sz.HeightCells) - 2
313322
mw := self.themes_list.max_width + 1
323+
green_fg, _, _ := strings.Cut(self.lp.SprintStyled("fg=green", "|"), "|")
314324
for _, l := range self.themes_list.Lines(num_rows) {
315-
num_rows--
316325
line := l.text
317326
if l.is_current {
318-
line = strings.ReplaceAll(line, themes.MARK_BEFORE, self.lp.SprintStyled("fg=green"))
319-
if l.is_current {
320-
self.lp.PrintStyled("fg=green", ">")
321-
self.lp.PrintStyled("fg=green bold", line)
322-
} else {
323-
self.lp.PrintStyled("fg=green", " ")
324-
self.lp.QueueWriteString(line)
325-
}
326-
self.lp.MoveCursorHorizontally(mw - l.width)
327-
self.lp.Println(SEPARATOR)
327+
line = strings.ReplaceAll(line, themes.MARK_AFTER, green_fg)
328+
self.lp.PrintStyled("fg=green", ">")
329+
self.lp.PrintStyled("fg=green bold", line)
330+
} else {
331+
self.lp.PrintStyled("fg=green", " ")
332+
self.lp.QueueWriteString(line)
328333
}
334+
self.lp.MoveCursorHorizontally(mw - l.width)
335+
self.lp.Println(SEPARATOR)
329336
}
330337
if self.themes_list != nil && self.themes_list.Len() > 0 {
331338
self.draw_theme_demo()
@@ -446,7 +453,10 @@ func (self *handler) draw_theme_demo() {
446453
if intense {
447454
s = "bright-" + s
448455
}
449-
buf.WriteString(self.lp.SprintStyled("fg="+c, c[:trunc]))
456+
if len(c) > trunc {
457+
c = c[:trunc]
458+
}
459+
buf.WriteString(self.lp.SprintStyled("fg="+c, c))
450460
buf.WriteString(" ")
451461
}
452462
text := strings.TrimSpace(buf.String())
@@ -487,25 +497,25 @@ func (self *handler) draw_theme_demo() {
487497
// accepting {{{
488498

489499
func (self *handler) on_accepting_key_event(ev *loop.KeyEvent) error {
490-
if ev.MatchesRelease("q") || ev.MatchesRelease("esc") {
500+
if ev.MatchesPressOrRepeat("q") || ev.MatchesPressOrRepeat("esc") {
491501
ev.Handled = true
492-
self.lp.Quit(0)
502+
self.quit_on_next_key_release = 0
493503
return nil
494504
}
495-
if ev.MatchesRelease("a") {
505+
if ev.MatchesPressOrRepeat("a") {
496506
ev.Handled = true
497507
self.state = BROWSING
498508
self.draw_screen()
499509
return nil
500510
}
501-
if ev.MatchesRelease("p") {
511+
if ev.MatchesPressOrRepeat("p") {
502512
ev.Handled = true
503513
self.themes_list.CurrentTheme().SaveInDir(utils.ConfigDir())
504514
self.update_recent()
505515
self.lp.Quit(0)
506516
return nil
507517
}
508-
if ev.MatchesRelease("m") {
518+
if ev.MatchesPressOrRepeat("m") {
509519
ev.Handled = true
510520
self.themes_list.CurrentTheme().SaveInConf(utils.ConfigDir(), self.opts.ReloadIn, self.opts.ConfigFileName)
511521
self.update_recent()
@@ -556,18 +566,36 @@ func (self *handler) draw_accepting_screen() {
556566
// }}}
557567

558568
// searching {{{
569+
570+
func (self *handler) update_search() {
571+
text := self.rl.AllText()
572+
if self.themes_list.UpdateSearch(text) {
573+
self.set_colors_to_current_theme()
574+
self.draw_screen()
575+
} else {
576+
self.draw_search_bar()
577+
}
578+
}
579+
580+
func (self *handler) on_text(text string, a, b bool) error {
581+
if self.state == SEARCHING {
582+
err := self.rl.OnText(text, a, b)
583+
if err != nil {
584+
return err
585+
}
586+
self.update_search()
587+
}
588+
return nil
589+
}
590+
559591
func (self *handler) on_searching_key_event(ev *loop.KeyEvent) error {
560-
if ev.MatchesRelease("enter") {
592+
if ev.MatchesPressOrRepeat("enter") {
561593
ev.Handled = true
562594
self.state = BROWSING
563595
self.draw_bottom_bar()
564596
return nil
565597
}
566-
if ev.MatchesPressOrRepeat("enter") || ev.MatchesPressOrRepeat("esc") {
567-
ev.Handled = true
568-
return nil
569-
}
570-
if ev.MatchesRelease("esc") {
598+
if ev.MatchesPressOrRepeat("esc") {
571599
ev.Handled = true
572600
self.state = BROWSING
573601
self.themes_list.UpdateSearch("")
@@ -579,12 +607,8 @@ func (self *handler) on_searching_key_event(ev *loop.KeyEvent) error {
579607
if err != nil {
580608
return err
581609
}
582-
text := self.rl.AllText()
583-
if self.themes_list.UpdateSearch(text) {
584-
self.set_colors_to_current_theme()
585-
self.draw_screen()
586-
} else {
587-
self.draw_search_bar()
610+
if ev.Handled {
611+
self.update_search()
588612
}
589613
return nil
590614
}

tools/themes/collection.go

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package themes
44

55
import (
66
"archive/zip"
7+
"bytes"
78
"encoding/json"
89
"errors"
910
"fmt"
@@ -20,6 +21,7 @@ import (
2021

2122
"kitty/tools/cli"
2223
"kitty/tools/config"
24+
"kitty/tools/tty"
2325
"kitty/tools/tui/subseq"
2426
"kitty/tools/utils"
2527
"kitty/tools/utils/style"
@@ -31,6 +33,7 @@ import (
3133
)
3234

3335
var _ = fmt.Print
36+
var DebugPrintln = tty.DebugPrintln
3437

3538
var AllColorSettingNames = map[string]bool{ // {{{
3639
// generated by gen-config.py do not edit
@@ -325,12 +328,33 @@ type JSONMetadata struct {
325328

326329
var ErrNoCacheFound = errors.New("No cache found and max cache age is negative")
327330

331+
func set_comment_in_zip_file(path string, comment string) error {
332+
src, err := zip.OpenReader(path)
333+
if err != nil {
334+
return err
335+
}
336+
defer src.Close()
337+
buf := bytes.Buffer{}
338+
dest := zip.NewWriter(&buf)
339+
dest.SetComment(comment)
340+
for _, sf := range src.File {
341+
err = dest.Copy(sf)
342+
if err != nil {
343+
return err
344+
}
345+
}
346+
dest.Close()
347+
utils.AtomicUpdateFile(path, buf.Bytes(), 0o644)
348+
return nil
349+
}
350+
328351
func fetch_cached(name, url, cache_path string, max_cache_age time.Duration) (string, error) {
329352
cache_path = filepath.Join(cache_path, name+".zip")
330353
zf, err := zip.OpenReader(cache_path)
331354
if err != nil && !errors.Is(err, fs.ErrNotExist) {
332355
return "", err
333356
}
357+
defer zf.Close()
334358

335359
var jm JSONMetadata
336360
if err == nil {
@@ -362,6 +386,12 @@ func fetch_cached(name, url, cache_path string, max_cache_age time.Duration) (st
362386
defer resp.Body.Close()
363387
if resp.StatusCode != http.StatusOK {
364388
if resp.StatusCode == http.StatusNotModified {
389+
jm.Timestamp = utils.ISO8601Format(time.Now())
390+
comment, _ := json.Marshal(jm)
391+
err = set_comment_in_zip_file(cache_path, utils.UnsafeBytesToString(comment))
392+
if err != nil {
393+
return "", err
394+
}
365395
return cache_path, nil
366396
}
367397
return "", fmt.Errorf("Failed to download %s with HTTP error: %s", url, resp.Status)
@@ -760,14 +790,18 @@ func (self *Themes) At(x int) *Theme {
760790
}
761791
func (self *Themes) Names() []string { return self.index_map }
762792

793+
func (self *Themes) create_index_map() {
794+
self.index_map = maps.Keys(self.name_map)
795+
self.index_map = utils.StableSortWithKey(self.index_map, strings.ToLower)
796+
}
797+
763798
func (self *Themes) Filtered(is_ok func(*Theme) bool) *Themes {
764799
themes := utils.Filter(maps.Values(self.name_map), is_ok)
765800
ans := Themes{name_map: make(map[string]*Theme, len(themes))}
766801
for _, theme := range themes {
767802
ans.name_map[theme.metadata.Name] = theme
768803
}
769-
ans.index_map = maps.Keys(ans.name_map)
770-
ans.index_map = utils.StableSortWithKey(ans.index_map, strings.ToLower)
804+
ans.create_index_map()
771805
return &ans
772806
}
773807

@@ -873,23 +907,24 @@ func (self *Themes) ApplySearch(expression string, marks ...string) []string {
873907
if len(marks) == 2 {
874908
mark_before, mark_after = marks[0], marks[1]
875909
}
876-
results := match(expression, maps.Keys(self.name_map))
910+
results := utils.Filter(match(expression, self.index_map), func(x *subseq.Match) bool { return x.Score > 0 })
877911
name_map := make(map[string]*Theme, len(results))
912+
for _, m := range results {
913+
name_map[m.Text] = self.name_map[m.Text]
914+
}
915+
self.name_map = name_map
916+
self.index_map = self.index_map[:0]
878917
ans := make([]string, 0, len(results))
879918
for _, m := range results {
880-
k := m.Text
881919
text := m.Text
882920
positions := m.Positions
883921
for i := len(positions) - 1; i >= 0; i-- {
884922
p := positions[i]
885923
text = text[:p] + mark_before + text[p:p+1] + mark_after + text[p+1:]
886924
}
887-
name_map[k] = self.name_map[k]
888925
ans = append(ans, text)
926+
self.index_map = append(self.index_map, m.Text)
889927
}
890-
self.name_map = name_map
891-
self.index_map = maps.Keys(name_map)
892-
self.index_map = utils.StableSortWithKey(self.index_map, strings.ToLower)
893928
return ans
894929
}
895930

@@ -905,8 +940,7 @@ func LoadThemes(cache_age time.Duration) (ans *Themes, closer io.Closer, err err
905940
if err = ans.add_from_dir(filepath.Join(utils.ConfigDir(), "themes")); err != nil {
906941
return nil, nil, err
907942
}
908-
ans.index_map = maps.Keys(ans.name_map)
909-
ans.index_map = utils.StableSortWithKey(ans.index_map, strings.ToLower)
943+
ans.create_index_map()
910944
return ans, closer, nil
911945
}
912946

tools/tui/readline/actions.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,9 @@ func (self *Readline) all_text() string {
5555
func (self *Readline) set_text(text string) {
5656
self.move_to_start()
5757
self.erase_chars_after_cursor(123456789, true)
58-
self.add_text(text)
58+
if text != "" {
59+
self.add_text(text)
60+
}
5961
self.move_to_end()
6062
}
6163

0 commit comments

Comments
 (0)