From ee82cb5a5278865ba213909c7bf83fa852488a4b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 18 Mar 2023 15:14:31 +0530 Subject: [PATCH] More work on porting diff kitten --- tools/cmd/diff/collect.go | 10 +-- tools/cmd/diff/main.go | 4 ++ tools/cmd/diff/patch.go | 4 +- tools/cmd/diff/ui.go | 125 +++++++++++++++++++++++++++++++++++++ tools/themes/collection.go | 17 ++--- tools/tui/loop/api.go | 14 +++++ 6 files changed, 160 insertions(+), 14 deletions(-) create mode 100644 tools/cmd/diff/ui.go diff --git a/tools/cmd/diff/collect.go b/tools/cmd/diff/collect.go index 6568b99c5..a736b894f 100644 --- a/tools/cmd/diff/collect.go +++ b/tools/cmd/diff/collect.go @@ -163,17 +163,17 @@ func (self *Collection) Len() int { return len(self.all_paths) } func (self *Collection) Items() int { return len(self.all_paths) } -func (self *Collection) Apply(f func(path, typ, data string) error) error { +func (self *Collection) Apply(f func(path, typ, changed_path string) error) error { for _, path := range self.all_paths { typ := self.type_map[path] - data := "" + changed_path := "" switch typ { case "diff": - data = self.changes[path] + changed_path = self.changes[path] case "rename": - data = self.renames[path] + changed_path = self.renames[path] } - if err := f(path, typ, data); err != nil { + if err := f(path, typ, changed_path); err != nil { return err } } diff --git a/tools/cmd/diff/main.go b/tools/cmd/diff/main.go index ab4b51587..0927ebce5 100644 --- a/tools/cmd/diff/main.go +++ b/tools/cmd/diff/main.go @@ -63,6 +63,7 @@ func get_ssh_file(hostname, rpath string) (string, error) { } cmd = append(cmd, rpath) c := exec.Command(cmd[0], cmd[1:]...) + c.Stdin, c.Stderr = os.Stdin, os.Stderr stdout, err := c.Output() if err != nil { return "", fmt.Errorf("Failed to ssh into remote host %s to get file %s with error: %w", hostname, rpath, err) @@ -134,12 +135,15 @@ func main(_ *cli.Command, opts_ *Options, args []string) (rc int, err error) { if err != nil { return 1, err } + h := Handler{left: left, right: right, lp: lp} lp.OnInitialize = func() (string, error) { lp.SetCursorVisible(false) lp.AllowLineWrapping(false) lp.SetWindowTitle(fmt.Sprintf("%s vs. %s", left, right)) + h.initialize() return "", nil } + lp.OnWakeup = h.on_wakeup lp.OnFinalize = func() string { lp.SetCursorVisible(true) return "" diff --git a/tools/cmd/diff/patch.go b/tools/cmd/diff/patch.go index 055f1a4f7..5033f7a91 100644 --- a/tools/cmd/diff/patch.go +++ b/tools/cmd/diff/patch.go @@ -321,7 +321,9 @@ func do_diff(file1, file2 string, context_count int) (ans *Patch, err error) { return } -func diff(jobs []struct{ file1, file2 string }, context_count int) (ans map[string]*Patch, err error) { +type diff_job struct{ file1, file2 string } + +func diff(jobs []diff_job, context_count int) (ans map[string]*Patch, err error) { ans = make(map[string]*Patch) ctx := images.Context{} type result struct { diff --git a/tools/cmd/diff/ui.go b/tools/cmd/diff/ui.go new file mode 100644 index 000000000..4d16087ed --- /dev/null +++ b/tools/cmd/diff/ui.go @@ -0,0 +1,125 @@ +// License: GPLv3 Copyright: 2023, Kovid Goyal, + +package diff + +import ( + "fmt" + "kitty/tools/tui/loop" +) + +var _ = fmt.Print + +type ResultType int + +const ( + COLLECTION ResultType = iota + DIFF + HIGHLIGHT +) + +type Reference struct { +} + +type AsyncResult struct { + err error + rtype ResultType + collection *Collection + diff_map map[string]*Patch +} + +type Handler struct { + async_results chan AsyncResult + left, right string + collection *Collection + diff_map map[string]*Patch + lp *loop.Loop + current_context_count, original_context_count int + added_count, removed_count int +} + +func (self *Handler) calculate_statistics() { + self.added_count, self.removed_count = self.collection.added_count, self.collection.removed_count + for _, patch := range self.diff_map { + self.added_count += patch.added_count + self.removed_count += patch.removed_count + } +} + +func (self *Handler) initialize() { + self.current_context_count = opts.Context + if self.current_context_count < 0 { + self.current_context_count = int(conf.Num_context_lines) + } + self.original_context_count = self.current_context_count + self.lp.SetDefaultColor(loop.FOREGROUND, conf.Foreground) + self.lp.SetDefaultColor(loop.CURSOR, conf.Foreground) + self.lp.SetDefaultColor(loop.BACKGROUND, conf.Background) + self.lp.SetDefaultColor(loop.SELECTION_BG, conf.Select_bg) + if !conf.Select_fg.IsNull { + self.lp.SetDefaultColor(loop.SELECTION_FG, conf.Select_fg.Color) + } + self.async_results = make(chan AsyncResult, 32) + go func() { + r := AsyncResult{} + r.collection, r.err = create_collection(self.left, self.right) + self.async_results <- r + self.lp.WakeupMainThread() + }() +} + +func (self *Handler) generate_diff() { + self.diff_map = nil + jobs := make([]diff_job, 0, 32) + self.collection.Apply(func(path, typ, changed_path string) error { + if typ == "diff" { + if is_path_text(path) && is_path_text(changed_path) { + jobs = append(jobs, diff_job{path, changed_path}) + } + } + return nil + }) + go func() { + r := AsyncResult{rtype: DIFF} + r.diff_map, r.err = diff(jobs, self.current_context_count) + self.async_results <- r + self.lp.WakeupMainThread() + }() +} + +func (self *Handler) on_wakeup() error { + var r AsyncResult + for { + select { + case r = <-self.async_results: + if r.err != nil { + return r.err + } + r.err = self.handle_async_result(r) + if r.err != nil { + return r.err + } + default: + return nil + } + } +} + +func (self *Handler) handle_async_result(r AsyncResult) error { + switch r.rtype { + case COLLECTION: + self.collection = r.collection + self.generate_diff() + case DIFF: + self.diff_map = r.diff_map + self.calculate_statistics() + self.render_diff() + self.scroll_pos = 0 + if self.restore_position != nil { + self.set_current_position(self.restore_position) + self.restore_position = nil + } + self.draw_screen() + case HIGHLIGHT: + } + return nil +} diff --git a/tools/themes/collection.go b/tools/themes/collection.go index 1afff8f65..928aa1295 100644 --- a/tools/themes/collection.go +++ b/tools/themes/collection.go @@ -22,6 +22,7 @@ import ( "kitty/tools/cli" "kitty/tools/config" "kitty/tools/tty" + "kitty/tools/tui/loop" "kitty/tools/tui/subseq" "kitty/tools/utils" "kitty/tools/utils/style" @@ -716,7 +717,7 @@ func ColorSettingsAsEscapeCodes(settings map[string]string) string { w.WriteString(sharp) } - set_default_color := func(name, defval string, num int) { + set_default_color := func(name, defval string, num loop.DefaultColor) { w.WriteString("\033]") defer func() { w.WriteString("\033\\") }() val, found := settings[name] @@ -726,20 +727,20 @@ func ColorSettingsAsEscapeCodes(settings map[string]string) string { if val != "" { rgba, err := style.ParseColor(val) if err == nil { - w.WriteString(strconv.Itoa(num)) + w.WriteString(strconv.Itoa(int(num))) w.WriteByte(';') w.WriteString(rgba.AsRGBSharp()) return } } w.WriteByte('1') - w.WriteString(strconv.Itoa(num)) + w.WriteString(strconv.Itoa(int(num))) } - set_default_color("foreground", style.DefaultColors.Foreground, 10) - set_default_color("background", style.DefaultColors.Background, 11) - set_default_color("cursor", style.DefaultColors.Cursor, 12) - set_default_color("selection_background", style.DefaultColors.SelectionBg, 17) - set_default_color("selection_foreground", style.DefaultColors.SelectionFg, 19) + set_default_color("foreground", style.DefaultColors.Foreground, loop.FOREGROUND) + set_default_color("background", style.DefaultColors.Background, loop.BACKGROUND) + set_default_color("cursor", style.DefaultColors.Cursor, loop.CURSOR) + set_default_color("selection_background", style.DefaultColors.SelectionBg, loop.SELECTION_BG) + set_default_color("selection_foreground", style.DefaultColors.SelectionFg, loop.SELECTION_FG) w.WriteString("\033]4") for i := 0; i < 256; i++ { diff --git a/tools/tui/loop/api.go b/tools/tui/loop/api.go index ae6b037ad..7b575283d 100644 --- a/tools/tui/loop/api.go +++ b/tools/tui/loop/api.go @@ -381,3 +381,17 @@ func (self *Loop) Quit(exit_code int) { self.exit_code = exit_code self.keep_going = false } + +type DefaultColor int + +const ( + BACKGROUND DefaultColor = 11 + FOREGROUND = 10 + CURSOR = 12 + SELECTION_BG = 17 + SELECTION_FG = 18 +) + +func (self *Loop) SetDefaultColor(which DefaultColor, val style.RGBA) { + self.QueueWriteString(fmt.Sprintf("\033]%d;%s\033\\", int(which), val.AsRGBSharp())) +}