Refactor config patching code to make it re-useable

This commit is contained in:
Kovid Goyal 2024-05-18 12:45:55 +05:30
parent 7fafdeef48
commit f15eebec02
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
2 changed files with 105 additions and 87 deletions

View File

@ -12,10 +12,14 @@ import (
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"sync"
"kitty/tools/utils"
"github.com/shirou/gopsutil/v3/process"
"golang.org/x/sys/unix"
)
var _ = fmt.Print
@ -274,3 +278,94 @@ func (self *ConfigParser) ParseOverrides(overrides ...string) error {
self.seen_includes = make(map[string]bool)
return self.parse(&s, "<overrides>", utils.ConfigDir(), 0)
}
func is_kitty_gui_cmdline(cmd ...string) bool {
if len(cmd) == 0 {
return false
}
if filepath.Base(cmd[0]) != "kitty" {
return false
}
if len(cmd) == 1 {
return true
}
s := cmd[1][:1]
switch s {
case `@`:
return false
case `+`:
if cmd[1] == `+` {
return len(cmd) > 2 && cmd[2] == `open`
}
return cmd[1] == `+open`
}
return true
}
type Patcher struct {
Write_backup bool
Mode fs.FileMode
}
func (self Patcher) Patch(path, sentinel, content string, settings_to_comment_out ...string) (updated bool, err error) {
if self.Mode == 0 {
self.Mode = 0o644
}
backup_path := path
if q, err := filepath.EvalSymlinks(path); err == nil {
path = q
}
raw, err := os.ReadFile(path)
if err != nil && !errors.Is(err, fs.ErrNotExist) {
return false, err
}
if raw == nil {
raw = []byte{}
}
pat := utils.MustCompile(fmt.Sprintf(`(?m)^\s*(%s)\b`, strings.Join(settings_to_comment_out, "|")))
text := pat.ReplaceAllString(utils.UnsafeBytesToString(raw), `# $1`)
pat = utils.MustCompile(fmt.Sprintf(`(?ms)^# BEGIN_%s.+?# END_%s`, sentinel, sentinel))
replaced := false
addition := fmt.Sprintf("# BEGIN_%s\n%s\n# END_%s", sentinel, content, sentinel)
ntext := pat.ReplaceAllStringFunc(text, func(string) string {
replaced = true
return addition
})
if !replaced {
if text != "" {
text += "\n\n"
}
ntext = text + addition
}
nraw := utils.UnsafeStringToBytes(ntext)
if !bytes.Equal(raw, nraw) {
if len(raw) > 0 && self.Write_backup {
_ = os.WriteFile(backup_path+".bak", raw, self.Mode)
}
return true, utils.AtomicUpdateFile(path, nraw, self.Mode)
}
return false, nil
}
func ReloadConfigInKitty(in_parent_only bool) error {
if in_parent_only {
if pid, err := strconv.Atoi(os.Getenv("KITTY_PID")); err == nil {
if p, err := process.NewProcess(int32(pid)); err == nil {
if c, err := p.CmdlineSlice(); err == nil && is_kitty_gui_cmdline(c...) {
return p.SendSignal(unix.SIGUSR1)
}
}
}
return nil
}
if all, err := process.Processes(); err == nil {
for _, p := range all {
if c, err := p.CmdlineSlice(); err == nil && is_kitty_gui_cmdline(c...) {
_ = p.SendSignal(unix.SIGUSR1)
}
}
}
return nil
}

View File

@ -28,9 +28,6 @@ import (
"kitty/tools/tui/subseq"
"kitty/tools/utils"
"kitty/tools/utils/style"
"github.com/shirou/gopsutil/v3/process"
"golang.org/x/sys/unix"
)
var _ = fmt.Print
@ -580,76 +577,6 @@ func (self *Theme) Code() (string, error) {
return self.load_code()
}
func patch_conf(text, theme_name string) string {
addition := fmt.Sprintf("# BEGIN_KITTY_THEME\n# %s\ninclude current-theme.conf\n# END_KITTY_THEME", theme_name)
pat := utils.MustCompile(`(?ms)^# BEGIN_KITTY_THEME.+?# END_KITTY_THEME`)
replaced := false
ntext := pat.ReplaceAllStringFunc(text, func(string) string {
replaced = true
return addition
})
if !replaced {
if text != "" {
text += "\n\n"
}
ntext = text + addition
}
pat = utils.MustCompile(fmt.Sprintf(`(?m)^\s*(%s)\b`, strings.Join(utils.Keys(AllColorSettingNames), "|")))
return pat.ReplaceAllString(ntext, `# $1`)
}
func is_kitty_gui_cmdline(cmd ...string) bool {
if len(cmd) == 0 {
return false
}
if filepath.Base(cmd[0]) != "kitty" {
return false
}
if len(cmd) == 1 {
return true
}
s := cmd[1][:1]
switch s {
case `@`:
return false
case `+`:
if cmd[1] == `+` {
return len(cmd) > 2 && cmd[2] == `open`
}
return cmd[1] == `+open`
}
return true
}
type ReloadDestination string
const (
RELOAD_IN_PARENT ReloadDestination = "parent"
RELOAD_IN_ALL ReloadDestination = "all"
)
func reload_config(reload_in ReloadDestination) bool {
switch reload_in {
case RELOAD_IN_PARENT:
if pid, err := strconv.Atoi(os.Getenv("KITTY_PID")); err == nil {
if p, err := process.NewProcess(int32(pid)); err == nil {
if c, err := p.CmdlineSlice(); err == nil && is_kitty_gui_cmdline(c...) {
return p.SendSignal(unix.SIGUSR1) == nil
}
}
}
case RELOAD_IN_ALL:
if all, err := process.Processes(); err == nil {
for _, p := range all {
if c, err := p.CmdlineSlice(); err == nil && is_kitty_gui_cmdline(c...) {
_ = p.SendSignal(unix.SIGUSR1)
}
}
return true
}
}
return false
}
func (self *Theme) SaveInDir(dirpath string) (err error) {
path := filepath.Join(dirpath, self.Name()+".conf")
code, err := self.Code()
@ -674,22 +601,18 @@ func (self *Theme) SaveInConf(config_dir, reload_in, config_file_name string) (e
if !filepath.IsAbs(config_file_name) {
confpath = filepath.Join(config_dir, config_file_name)
}
if q, err := filepath.EvalSymlinks(confpath); err == nil {
confpath = q
patcher := config.Patcher{Write_backup: true}
if _, err = patcher.Patch(
confpath, "KITTY_THEME", fmt.Sprintf("# %s\ninclude current-theme.conf", self.metadata.Name),
utils.Keys(AllColorSettingNames)...); err != nil {
return
}
raw, err := os.ReadFile(confpath)
if err != nil && !errors.Is(err, fs.ErrNotExist) {
return err
switch reload_in {
case "parent":
config.ReloadConfigInKitty(true)
case "all":
config.ReloadConfigInKitty(false)
}
nraw := patch_conf(utils.UnsafeBytesToString(raw), self.metadata.Name)
if len(raw) > 0 {
_ = os.WriteFile(confpath+".bak", raw, 0o600)
}
err = utils.AtomicUpdateFile(confpath, utils.UnsafeStringToBytes(nraw), 0o600)
if err != nil {
return err
}
reload_config(ReloadDestination(reload_in))
return
}