More work on porting diff kitten

This commit is contained in:
Kovid Goyal 2023-03-16 21:46:00 +05:30
parent d208670172
commit 3c550bcd28
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
6 changed files with 172 additions and 19 deletions

48
tools/cmd/diff/collect.go Normal file
View File

@ -0,0 +1,48 @@
// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>
package diff
import (
"fmt"
"os"
"path/filepath"
)
var _ = fmt.Print
var path_name_map, remote_dirs map[string]string
type Collection struct {
}
func create_collection(left, right string) (ans *Collection, err error) {
path_name_map = make(map[string]string, 32)
remote_dirs = make(map[string]string, 32)
ans = &Collection{}
left_stat, err := os.Stat(left)
if err != nil {
return nil, err
}
if left_stat.IsDir() {
err = ans.collect_files(left, right)
if err != nil {
return nil, err
}
} else {
pl, err := filepath.Abs(left)
if err != nil {
return nil, err
}
pr, err := filepath.Abs(right)
if err != nil {
return nil, err
}
path_name_map[pl] = resolve_remote_name(pl, left)
path_name_map[pr] = resolve_remote_name(pr, right)
err = ans.add_change(pl, pr)
if err != nil {
return nil, err
}
}
err = ans.finalize()
return ans, err
}

View File

@ -4,6 +4,7 @@ package diff
import (
"fmt"
"os"
"kitty/tools/cli"
"kitty/tools/config"
@ -26,6 +27,18 @@ var conf *Config
var opts *Options
var lp *loop.Loop
func isdir(path string) bool {
if s, err := os.Stat(path); err == nil {
return s.IsDir()
}
return false
}
func exists(path string) bool {
_, err := os.Stat(path)
return err == nil
}
func main(_ *cli.Command, opts_ *Options, args []string) (rc int, err error) {
opts = opts_
conf, err = load_config(opts)
@ -35,7 +48,19 @@ func main(_ *cli.Command, opts_ *Options, args []string) (rc int, err error) {
if len(args) != 2 {
return 1, fmt.Errorf("You must specify exactly two files/directories to compare")
}
left, right := args[0], args[1]
if err = set_diff_command(conf.Diff_cmd); err != nil {
return 1, err
}
left, right := get_remote_file(args[0]), get_remote_file(args[1])
if isdir(left) != isdir(right) {
return 1, fmt.Errorf("The items to be diffed should both be either directories or files. Comparing a directory to a file is not valid.'")
}
if !exists(left) {
return 1, fmt.Errorf("%s does not exist", left)
}
if !exists(right) {
return 1, fmt.Errorf("%s does not exist", right)
}
lp, err = loop.New()
if err != nil {
return 1, err

84
tools/cmd/diff/patch.go Normal file
View File

@ -0,0 +1,84 @@
// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>
package diff
import (
"bytes"
"errors"
"fmt"
"kitty/tools/utils"
"kitty/tools/utils/shlex"
"os/exec"
"path/filepath"
"strconv"
"strings"
)
var _ = fmt.Print
const GIT_DIFF = `git diff --no-color --no-ext-diff --exit-code -U_CONTEXT_ --no-index --`
const DIFF_DIFF = `diff -p -U _CONTEXT_ --`
var diff_cmd []string
var GitExe = (&utils.Once[string]{Run: func() string {
return utils.FindExe("git")
}}).Get
var DiffExe = (&utils.Once[string]{Run: func() string {
return utils.FindExe("diff")
}}).Get
func find_differ() error {
if GitExe() != "git" && exec.Command(GitExe(), "--help").Run() == nil {
diff_cmd, _ = shlex.Split(GIT_DIFF)
return nil
}
if DiffExe() != "diff" && exec.Command(DiffExe(), "--help").Run() == nil {
diff_cmd, _ = shlex.Split(DIFF_DIFF)
return nil
}
return fmt.Errorf("Neither the git nor the diff programs were found in PATH")
}
func set_diff_command(q string) error {
if q == "auto" {
return find_differ()
}
c, err := shlex.Split(q)
if err == nil {
diff_cmd = c
}
return err
}
func run_diff(file1, file2 string, num_of_context_lines int) (ok, is_different bool, patch string, err error) {
context := strconv.Itoa(num_of_context_lines)
cmd := utils.Map(func(x string) string {
return strings.ReplaceAll(x, "_CONTEXT_", context)
}, diff_cmd)
// we resolve symlinks because git diff does not follow symlinks, while diff
// does. We want consistent behavior, also for integration with git difftool
// we always want symlinks to be followed.
path1, err := filepath.EvalSymlinks(file1)
if err != nil {
return
}
path2, err := filepath.EvalSymlinks(file2)
if err != nil {
return
}
cmd = append(cmd, path1, path2)
c := exec.Command(cmd[0], cmd[1:]...)
stdout, stderr := bytes.Buffer{}, bytes.Buffer{}
c.Stdout, c.Stderr = &stdout, &stderr
err = c.Run()
if err != nil {
var e *exec.ExitError
if errors.As(err, &e) && e.ExitCode() == 1 {
return true, true, stdout.String(), nil
}
return false, false, stderr.String(), err
}
return true, false, stdout.String(), nil
}

View File

@ -23,15 +23,7 @@ import (
var _ = fmt.Print
var RgExe = (&utils.Once[string]{Run: func() string {
ans := utils.Which("rg")
if ans != "" {
return ans
}
ans = utils.Which("rg", utils.DefaultExeSearchPaths()...)
if ans == "" {
ans = "rg"
}
return ans
return utils.FindExe("rg")
}}).Get
func get_options_for_rg() (expecting_args map[string]bool, alias_map map[string]string, err error) {

View File

@ -18,15 +18,7 @@ import (
var _ = fmt.Print
var SSHExe = (&utils.Once[string]{Run: func() string {
ans := utils.Which("ssh")
if ans != "" {
return ans
}
ans = utils.Which("ssh", utils.DefaultExeSearchPaths()...)
if ans == "" {
ans = "ssh"
}
return ans
return utils.FindExe("ssh")
}}).Get
var SSHOptions = (&utils.Once[map[string]string]{Run: func() (ssh_options map[string]string) {

View File

@ -47,3 +47,15 @@ func Which(cmd string, paths ...string) string {
}
return ""
}
func FindExe(name string) string {
ans := Which(name)
if ans != "" {
return ans
}
ans = Which(name, DefaultExeSearchPaths()...)
if ans == "" {
ans = name
}
return ans
}