mirror of
https://github.com/kovidgoyal/kitty
synced 2026-06-06 01:05:48 +02:00
Replace list_fonts with choose-fonts kitten
This commit is contained in:
0
kittens/choose_fonts/__init__.py
Normal file
0
kittens/choose_fonts/__init__.py
Normal file
167
kittens/choose_fonts/backend.go
Normal file
167
kittens/choose_fonts/backend.go
Normal 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
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package list_fonts
|
||||
package choose_fonts
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
75
kittens/choose_fonts/main.go
Normal file
75
kittens/choose_fonts/main.go
Normal 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"
|
||||
}
|
||||
0
kittens/choose_fonts/main.py
Normal file
0
kittens/choose_fonts/main.py
Normal file
@@ -1,4 +1,4 @@
|
||||
package list_fonts
|
||||
package choose_fonts
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
}()
|
||||
}
|
||||
@@ -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()
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -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__",
|
||||
|
||||
Reference in New Issue
Block a user