mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-05-13 08:26:56 +00:00
Start work on config watcher kitten
This commit is contained in:
parent
a4608c77a6
commit
80ad647336
5 changed files with 157 additions and 3 deletions
1
go.mod
1
go.mod
|
|
@ -20,6 +20,7 @@ require (
|
|||
github.com/kovidgoyal/imaging v1.8.21
|
||||
github.com/nwaples/rardecode/v2 v2.2.2
|
||||
github.com/seancfoley/ipaddress-go v1.7.1
|
||||
github.com/sgtdi/fswatcher v1.2.0
|
||||
github.com/shirou/gopsutil/v4 v4.26.3
|
||||
github.com/ulikunitz/xz v0.5.15
|
||||
github.com/zeebo/xxh3 v1.1.0
|
||||
|
|
|
|||
2
go.sum
2
go.sum
|
|
@ -52,6 +52,8 @@ github.com/seancfoley/bintree v1.3.1 h1:cqmmQK7Jm4aw8gna0bP+huu5leVOgHGSJBEpUx3E
|
|||
github.com/seancfoley/bintree v1.3.1/go.mod h1:hIUabL8OFYyFVTQ6azeajbopogQc2l5C/hiXMcemWNU=
|
||||
github.com/seancfoley/ipaddress-go v1.7.1 h1:fDWryS+L8iaaH5RxIKbY0xB5Z+Zxk8xoXLN4S4eAPdQ=
|
||||
github.com/seancfoley/ipaddress-go v1.7.1/go.mod h1:TQRZgv+9jdvzHmKoPGBMxyiaVmoI0rYpfEk8Q/sL/Iw=
|
||||
github.com/sgtdi/fswatcher v1.2.0 h1:uSJuMc3/Eo/vaPnZWpJ42EFYb5j38cZENmkszOV0yhw=
|
||||
github.com/sgtdi/fswatcher v1.2.0/go.mod h1:smzXnaqu0SYJQNIwGLLkvRkpH4RdEACB7avMSsSaqjQ=
|
||||
github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc=
|
||||
github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
|
|
|
|||
|
|
@ -134,6 +134,7 @@ func KittyToolEntryPoints(root *cli.Command) {
|
|||
return confirm_and_run_exe(args)
|
||||
},
|
||||
})
|
||||
// __watch_conf__
|
||||
|
||||
// __convert_image__
|
||||
images.ConvertEntryPoint(root)
|
||||
|
|
|
|||
|
|
@ -39,9 +39,10 @@ type ConfigLine struct {
|
|||
}
|
||||
|
||||
type ConfigParser struct {
|
||||
LineHandler func(key, val string) error
|
||||
CommentsHandler func(line string) error
|
||||
SourceHandler func(text, path string)
|
||||
LineHandler func(key, val string) error
|
||||
CommentsHandler func(line string) error
|
||||
SourceHandler func(text, path string)
|
||||
AllIncludedFiles *utils.Set[string]
|
||||
|
||||
bad_lines []ConfigLine
|
||||
seen_includes map[string]bool
|
||||
|
|
@ -111,11 +112,16 @@ func ExpandVars(x string) string {
|
|||
})
|
||||
}
|
||||
|
||||
const OverridesFileName = "<overrides>"
|
||||
|
||||
func (self *ConfigParser) parse(scanner Scanner, name, base_path_for_includes string, depth int) error {
|
||||
if self.seen_includes[name] { // avoid include loops
|
||||
return nil
|
||||
}
|
||||
self.seen_includes[name] = true
|
||||
if self.AllIncludedFiles != nil && name != OverridesFileName {
|
||||
self.AllIncludedFiles.Add(name)
|
||||
}
|
||||
|
||||
recurse := func(r io.Reader, nname, base_path_for_includes string) error {
|
||||
if depth > 32 {
|
||||
|
|
|
|||
144
tools/watch/api.go
Normal file
144
tools/watch/api.go
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
package watch
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sgtdi/fswatcher"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/kovidgoyal/kitty/tools/cli"
|
||||
"github.com/kovidgoyal/kitty/tools/config"
|
||||
"github.com/kovidgoyal/kitty/tools/utils"
|
||||
)
|
||||
|
||||
var _ = fmt.Print
|
||||
|
||||
// watch_dir starts fswatcher in a background goroutine and pipes events to a custom channel.
|
||||
func watch_dir(ctx context.Context, path string, debounce time.Duration, eventChan chan<- fswatcher.WatchEvent) error {
|
||||
w, err := fswatcher.New(
|
||||
fswatcher.WithPath(path),
|
||||
fswatcher.WithCooldown(debounce),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go w.Watch(ctx)
|
||||
go func() {
|
||||
for event := range w.Events() {
|
||||
eventChan <- event
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
type config_file_collection struct {
|
||||
mutex sync.Mutex
|
||||
config_paths []string
|
||||
dirs_to_watch []string
|
||||
}
|
||||
|
||||
func (cfc *config_file_collection) get_list_of_config_files() *utils.Set[string] {
|
||||
cp := config.ConfigParser{
|
||||
AllIncludedFiles: utils.NewSet[string](), LineHandler: func(k, v string) error { return nil }}
|
||||
cp.ParseFiles(cfc.config_paths...)
|
||||
for _, path := range cfc.config_paths {
|
||||
path = filepath.Clean(path)
|
||||
cp.AllIncludedFiles.Add(path)
|
||||
for _, q := range []string{"dark-theme.auto.conf", "light-theme.auto.conf", "no-preference-theme.auto.conf"} {
|
||||
q = filepath.Join(filepath.Dir(path), q)
|
||||
cp.AllIncludedFiles.Add(filepath.Clean(q))
|
||||
}
|
||||
}
|
||||
return cp.AllIncludedFiles
|
||||
}
|
||||
|
||||
func (cfc *config_file_collection) EventIsSignificant(ev fswatcher.WatchEvent) bool {
|
||||
cfc.mutex.Lock()
|
||||
defer cfc.mutex.Unlock()
|
||||
conf_files := cfc.get_list_of_config_files()
|
||||
q := filepath.Clean(ev.Path)
|
||||
return conf_files.Has(q)
|
||||
}
|
||||
|
||||
func watch_for_kitty_config_changes(action func() error, debounce_time time.Duration, config_paths []string) error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
event_chan := make(chan fswatcher.WatchEvent)
|
||||
dirs := utils.NewSet[string](len(config_paths))
|
||||
for _, path := range config_paths {
|
||||
if parent := filepath.Dir(path); parent != "" && parent != "." && parent != "/" {
|
||||
dirs.Add(path)
|
||||
}
|
||||
}
|
||||
if dirs.Len() == 0 {
|
||||
return fmt.Errorf("No directories to watch provided")
|
||||
}
|
||||
cfc := config_file_collection{config_paths: config_paths, dirs_to_watch: dirs.AsSlice()}
|
||||
|
||||
filtered_action := func(ev fswatcher.WatchEvent) error {
|
||||
if cfc.EventIsSignificant(ev) {
|
||||
return action()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
for _, path := range cfc.dirs_to_watch {
|
||||
if err := watch_dir(ctx, path, debounce_time, event_chan); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
stdinClosed := make(chan struct{})
|
||||
go func() {
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
for scanner.Scan() {
|
||||
}
|
||||
close(stdinClosed)
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case event := <-event_chan:
|
||||
if err := filtered_action(event); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to signal kitty in event: %s with error: %s\n", event, err)
|
||||
}
|
||||
case <-stdinClosed:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func signal_kitty_to_reload_config(kitty_pid int) error {
|
||||
return unix.Kill(kitty_pid, unix.SIGUSR1)
|
||||
}
|
||||
|
||||
func EntryPoint(root *cli.Command) {
|
||||
root.AddSubCommand(&cli.Command{
|
||||
Name: "__watch_conf__",
|
||||
Hidden: true,
|
||||
OnlyArgsAllowed: true,
|
||||
Run: func(cmd *cli.Command, args []string) (rc int, err error) {
|
||||
if len(args) < 3 {
|
||||
return 1, fmt.Errorf("Usage: __watch_conf__ kitty_pid debounce_time_ms config_paths...")
|
||||
}
|
||||
kitty_pid, err := strconv.Atoi(args[0])
|
||||
if err != nil {
|
||||
return 1, err
|
||||
}
|
||||
debounce_time_ms, err := strconv.ParseUint(args[1], 10, 64)
|
||||
if err != nil {
|
||||
return 1, err
|
||||
}
|
||||
if err = watch_for_kitty_config_changes(
|
||||
func() error { return signal_kitty_to_reload_config(kitty_pid) },
|
||||
time.Millisecond*time.Duration(debounce_time_ms), args[2:]); err != nil {
|
||||
return 1, err
|
||||
}
|
||||
return 0, nil
|
||||
},
|
||||
})
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue