Replace list_fonts with choose-fonts kitten

This commit is contained in:
Kovid Goyal
2024-05-06 12:17:25 +05:30
parent 405f5ce148
commit f2d5631c47
10 changed files with 251 additions and 110 deletions

View File

View File

@@ -0,0 +1,167 @@
package choose_fonts
import (
"encoding/json"
"fmt"
"io"
"os"
"os/exec"
"strings"
"sync"
"time"
"kitty/tools/utils"
)
var _ = fmt.Print
type kitty_font_backend_type struct {
from io.ReadCloser
to io.WriteCloser
json_decoder *json.Decoder
cmd *exec.Cmd
stderr strings.Builder
lock sync.Mutex
r io.ReadCloser
w io.WriteCloser
wait_for_exit chan error
started, exited, failed bool
timeout time.Duration
}
func (k *kitty_font_backend_type) start() (err error) {
exe := utils.KittyExe()
if exe == "" {
exe = utils.Which("kitty")
}
if exe == "" {
return fmt.Errorf("Failed to find the kitty executable, this kitten requires the kitty executable to be present. You can use the environment variable KITTY_PATH_TO_KITTY_EXE to specify the path to the kitty executable")
}
k.cmd = exec.Command(exe, "+runpy", "from kittens.choose_fonts.backend import main; main()")
k.cmd.Stderr = &k.stderr
if k.r, k.to, err = os.Pipe(); err != nil {
return err
}
k.cmd.Stdin = k.r
if k.from, k.w, err = os.Pipe(); err != nil {
return err
}
k.cmd.Stdout = k.w
k.json_decoder = json.NewDecoder(k.from)
if err = k.cmd.Start(); err != nil {
return err
}
k.started = true
k.timeout = 60 * time.Second
k.wait_for_exit = make(chan error)
go func() {
k.wait_for_exit <- k.cmd.Wait()
}()
return
}
var kitty_font_backend kitty_font_backend_type
func (k *kitty_font_backend_type) send(v any) error {
data, err := json.Marshal(v)
if err != nil {
return fmt.Errorf("Could not encode message to kitty with error: %w", err)
}
c := make(chan error)
go func() {
if _, err = k.to.Write(data); err != nil {
c <- fmt.Errorf("Failed to send message to kitty with I/O error: %w", err)
return
}
if _, err = k.to.Write([]byte{'\n'}); err != nil {
c <- fmt.Errorf("Failed to send message to kitty with I/O error: %w", err)
return
}
c <- nil
}()
select {
case err := <-c:
return err
case <-time.After(k.timeout):
return fmt.Errorf("Timed out waiting to write to kitty font backend after %v", k.timeout)
case err := <-k.wait_for_exit:
k.exited = true
if err == nil {
err = fmt.Errorf("kitty font backend exited with no error while waiting for a response from it")
} else {
k.failed = true
}
return err
}
}
func (k *kitty_font_backend_type) query(action string, cmd map[string]any, result any) error {
k.lock.Lock()
defer k.lock.Unlock()
if cmd == nil {
cmd = make(map[string]any)
}
cmd["action"] = action
if err := k.send(cmd); err != nil {
return err
}
c := make(chan error)
go func() {
if err := k.json_decoder.Decode(result); err != nil {
c <- fmt.Errorf("Failed to decode JSON from kitty with error: %w", err)
}
c <- nil
}()
select {
case err := <-c:
return err
case <-time.After(k.timeout):
return fmt.Errorf("Timed out waiting for response from kitty font backend after %v", k.timeout)
case err := <-k.wait_for_exit:
k.exited = true
if err == nil {
err = fmt.Errorf("kitty font backed exited with no error while waiting for a response from it")
} else {
k.failed = true
}
return err
}
}
func (k *kitty_font_backend_type) release() (err error) {
if k.r != nil {
k.r.Close()
k.r = nil
}
if k.to != nil {
k.to.Close()
k.to = nil
}
if k.w != nil {
k.w.Close()
k.w = nil
}
if k.from != nil {
k.from.Close()
k.from = nil
}
if k.started && !k.exited {
timeout := 2 * time.Second
select {
case err = <-k.wait_for_exit:
k.exited = true
if err != nil {
k.failed = true
}
case <-time.After(timeout):
k.failed = true
err = fmt.Errorf("Timed out waiting for kitty font backend to exit for %v", timeout)
}
}
if k.failed {
os.Stderr.WriteString(k.stderr.String())
}
return
}

View File

@@ -1,4 +1,4 @@
package list_fonts
package choose_fonts
import (
"fmt"

View File

@@ -0,0 +1,75 @@
package choose_fonts
import (
"fmt"
"kitty/tools/cli"
"kitty/tools/tty"
"kitty/tools/tui/loop"
)
var _ = fmt.Print
var debugprintln = tty.DebugPrintln
func main() (rc int, err error) {
if err = kitty_font_backend.start(); err != nil {
return 1, err
}
defer func() {
if werr := kitty_font_backend.release(); werr != nil {
if err == nil {
err = werr
}
if rc == 0 {
rc = 1
}
}
}()
lp, err := loop.New()
if err != nil {
return 1, err
}
lp.MouseTrackingMode(loop.FULL_MOUSE_TRACKING)
h := &handler{lp: lp}
lp.OnInitialize = func() (string, error) {
lp.AllowLineWrapping(false)
lp.SetWindowTitle(`Choose a font for kitty`)
h.initialize()
return "", nil
}
lp.OnWakeup = h.on_wakeup
lp.OnFinalize = func() string {
h.finalize()
lp.SetCursorVisible(true)
return ``
}
lp.OnMouseEvent = h.on_mouse_event
lp.OnResize = func(_, _ loop.ScreenSize) error {
return h.draw_screen()
}
lp.OnKeyEvent = h.on_key_event
lp.OnText = h.on_text
err = lp.Run()
if err != nil {
return 1, err
}
ds := lp.DeathSignalName()
if ds != "" {
fmt.Println("Killed by signal: ", ds)
lp.KillIfSignalled()
return 1, nil
}
return lp.ExitCode(), nil
}
func EntryPoint(root *cli.Command) {
ans := root.AddSubCommand(&cli.Command{
Name: "choose-fonts",
Run: func(cmd *cli.Command, args []string) (rc int, err error) {
return main()
},
})
clone := root.AddClone(ans.Group, ans)
clone.Hidden = false
clone.Name = "choose_fonts"
}

View File

View File

@@ -1,4 +1,4 @@
package list_fonts
package choose_fonts
import (
"fmt"

View File

@@ -1,4 +1,4 @@
package list_fonts
package choose_fonts
import (
"fmt"
@@ -95,7 +95,7 @@ func ensure_variable_data_for_fonts(fonts ...ListedFont) error {
}
variable_data_cache_mutex.Unlock()
var data []VariableData
if err := query_kitty("read_variable_data", map[string]any{"descriptors": descriptors}, &data); err != nil {
if err := kitty_font_backend.query("read_variable_data", map[string]any{"descriptors": descriptors}, &data); err != nil {
return err
}
variable_data_cache_mutex.Lock()

View File

@@ -1,4 +1,4 @@
package list_fonts
package choose_fonts
import (
"fmt"
@@ -241,7 +241,7 @@ func (h *handler) initialize() {
h.draw_screen()
initialize_variable_data_cache()
go func() {
h.set_worker_error(query_kitty("", nil, &h.fonts))
h.set_worker_error(kitty_font_backend.query("list_all_fonts", nil, &h.fonts))
h.lp.WakeupMainThread()
}()
}

View File

@@ -1,101 +0,0 @@
package list_fonts
import (
"encoding/json"
"fmt"
"os"
"sync"
"kitty/tools/cli"
"kitty/tools/tty"
"kitty/tools/tui/loop"
)
var _ = fmt.Print
var debugprintln = tty.DebugPrintln
var json_decoder *json.Decoder
func json_decode(v any) error {
if err := json_decoder.Decode(v); err != nil {
return fmt.Errorf("Failed to decode JSON from kitty with error: %w", err)
}
return nil
}
func to_kitty(v any) error {
data, err := json.Marshal(v)
if err != nil {
return fmt.Errorf("Could not encode message to kitty with error: %w", err)
}
if _, err = os.Stdout.Write(data); err != nil {
return fmt.Errorf("Failed to send message to kitty with I/O error: %w", err)
}
if _, err = os.Stdout.WriteString("\n"); err != nil {
return fmt.Errorf("Failed to send message to kitty with I/O error: %w", err)
}
return nil
}
var query_kitty_lock sync.Mutex
func query_kitty(action string, cmd map[string]any, result any) error {
query_kitty_lock.Lock()
defer query_kitty_lock.Unlock()
if action != "" {
cmd["action"] = action
if err := to_kitty(cmd); err != nil {
return err
}
}
return json_decode(result)
}
func main() (rc int, err error) {
json_decoder = json.NewDecoder(os.Stdin)
lp, err := loop.New()
if err != nil {
return 1, err
}
lp.MouseTrackingMode(loop.FULL_MOUSE_TRACKING)
h := &handler{lp: lp}
lp.OnInitialize = func() (string, error) {
lp.AllowLineWrapping(false)
lp.SetWindowTitle(`Choose a font for kitty`)
h.initialize()
return "", nil
}
lp.OnWakeup = h.on_wakeup
lp.OnFinalize = func() string {
h.finalize()
lp.SetCursorVisible(true)
return ``
}
lp.OnMouseEvent = h.on_mouse_event
lp.OnResize = func(_, _ loop.ScreenSize) error {
return h.draw_screen()
}
lp.OnKeyEvent = h.on_key_event
lp.OnText = h.on_text
err = lp.Run()
if err != nil {
return 1, err
}
ds := lp.DeathSignalName()
if ds != "" {
fmt.Println("Killed by signal: ", ds)
lp.KillIfSignalled()
return 1, nil
}
return lp.ExitCode(), nil
}
func EntryPoint(root *cli.Command) {
root = root.AddSubCommand(&cli.Command{
Name: "__list_fonts__",
Hidden: true,
Run: func(cmd *cli.Command, args []string) (rc int, err error) {
return main()
},
})
}

View File

@@ -6,6 +6,7 @@ import (
"fmt"
"kitty/kittens/ask"
"kitty/kittens/choose_fonts"
"kitty/kittens/clipboard"
"kitty/kittens/diff"
"kitty/kittens/hints"
@@ -20,7 +21,6 @@ import (
"kitty/tools/cmd/at"
"kitty/tools/cmd/benchmark"
"kitty/tools/cmd/edit_in_kitty"
"kitty/tools/cmd/list_fonts"
"kitty/tools/cmd/mouse_demo"
"kitty/tools/cmd/pytest"
"kitty/tools/cmd/run_shell"
@@ -77,10 +77,10 @@ func KittyToolEntryPoints(root *cli.Command) {
run_shell.EntryPoint(root)
// show_error
show_error.EntryPoint(root)
// choose-fonts
choose_fonts.EntryPoint(root)
// __pytest__
pytest.EntryPoint(root)
// __list_fonts__
list_fonts.EntryPoint(root)
// __hold_till_enter__
root.AddSubCommand(&cli.Command{
Name: "__hold_till_enter__",