More work on diffing images

This commit is contained in:
Kovid Goyal
2023-03-24 15:42:51 +05:30
parent c745961f47
commit 508a61bd1c
9 changed files with 221 additions and 57 deletions

View File

@@ -18,6 +18,7 @@ var _ = fmt.Print
var path_name_map, remote_dirs map[string]string
var mimetypes_cache, data_cache, hash_cache *utils.LRUCache[string, string]
var size_cache *utils.LRUCache[string, int64]
var lines_cache *utils.LRUCache[string, []string]
var highlighted_lines_cache *utils.LRUCache[string, []string]
var is_text_cache *utils.LRUCache[string, bool]
@@ -26,6 +27,7 @@ func init_caches() {
path_name_map = make(map[string]string, 32)
remote_dirs = make(map[string]string, 32)
const sz = 4096
size_cache = utils.NewLRUCache[string, int64](sz)
mimetypes_cache = utils.NewLRUCache[string, string](sz)
data_cache = utils.NewLRUCache[string, string](sz)
is_text_cache = utils.NewLRUCache[string, bool](sz)
@@ -67,6 +69,16 @@ func data_for_path(path string) (string, error) {
})
}
func size_for_path(path string) (int64, error) {
return size_cache.GetOrCreate(path, func(path string) (int64, error) {
s, err := os.Stat(path)
if err != nil {
return 0, err
}
return s.Size(), nil
})
}
func is_image(path string) bool {
return strings.HasPrefix(mimetype_for_path(path), "image/")
}

View File

@@ -210,12 +210,7 @@ func (self *LogicalLines) IncrementScrollPosBy(pos *ScrollPos, amt int) (delta i
return
}
func image_lines(left_path, right_path string, columns, margin_size int, ans []*LogicalLine) ([]*LogicalLine, error) {
// TODO: Implement this
return ans, nil
}
func human_readable(size int) string {
func human_readable(size int64) string {
divisor, suffix := 1, "B"
for i, candidate := range []string{"B", "KB", "MB", "GB", "TB", "PB", "EB"} {
if size < (1 << ((i + 1) * 10)) {
@@ -253,33 +248,79 @@ func render_diff_line(number, text, ltype string, margin_size int, available_col
return margin + content
}
func binary_lines(left_path, right_path string, columns, margin_size int, ans []*LogicalLine) (ans2 []*LogicalLine, err error) {
func image_lines(left_path, right_path string, columns, margin_size int, ans []*LogicalLine) ([]*LogicalLine, error) {
available_cols := columns/2 - margin_size
fl := func(path string, formatter func(...any) string) string {
if err == nil {
var data string
data, err = data_for_path(path)
text := fmt.Sprintf("Binary file: %s", human_readable(len(data)))
text = place_in(text, available_cols)
return margin_format(strings.Repeat(` `, margin_size)) + formatter(text)
ll, err := first_binary_line(left_path, right_path, columns, margin_size, func(path string, formatter func(...any) string) (string, error) {
sz, err := size_for_path(path)
if err != nil {
return "", err
}
return ""
text := fmt.Sprintf("Size: %s", human_readable(sz))
res := image_collection.ResolutionOf(path)
if res.X > -1 {
text = fmt.Sprintf("Dimensions: %dx%d %s", res.X, res.Y, text)
}
text = place_in(text, available_cols)
return formatter(strings.Repeat(` `, margin_size) + text), err
})
if err != nil {
return nil, err
}
return append(ans, ll), nil
}
func first_binary_line(left_path, right_path string, columns, margin_size int, renderer func(path string, formatter func(...any) string) (string, error)) (*LogicalLine, error) {
available_cols := columns/2 - margin_size
line := ""
if left_path == "" {
filler := render_diff_line(``, ``, `filler`, margin_size, available_cols)
line = filler + fl(right_path, added_format)
r, err := renderer(right_path, added_format)
if err != nil {
return nil, err
}
line = filler + r
} else if right_path == "" {
filler := render_diff_line(``, ``, `filler`, margin_size, available_cols)
line = fl(left_path, removed_format) + filler
l, err := renderer(left_path, removed_format)
if err != nil {
return nil, err
}
line = l + filler
} else {
line = fl(left_path, removed_format) + fl(right_path, added_format)
l, err := renderer(left_path, removed_format)
if err != nil {
return nil, err
}
r, err := renderer(right_path, added_format)
if err != nil {
return nil, err
}
line = l + r
}
ll := LogicalLine{is_change_start: true, line_type: CHANGE_LINE, src: Reference{path: left_path, linenum: 0}, screen_lines: []string{line}}
if left_path == "" {
ll.src.path = right_path
}
return append(ans, &ll), err
return &ll, nil
}
func binary_lines(left_path, right_path string, columns, margin_size int, ans []*LogicalLine) (ans2 []*LogicalLine, err error) {
available_cols := columns/2 - margin_size
ll, err := first_binary_line(left_path, right_path, columns, margin_size, func(path string, formatter func(...any) string) (string, error) {
sz, err := size_for_path(path)
if err != nil {
return "", err
}
text := fmt.Sprintf("Binary file: %s", human_readable(sz))
text = place_in(text, available_cols)
return formatter(strings.Repeat(` `, margin_size) + text), err
})
if err != nil {
return nil, err
}
return append(ans, ll), nil
}
type DiffData struct {

View File

@@ -25,6 +25,7 @@ const (
COLLECTION ResultType = iota
DIFF
HIGHLIGHT
IMAGE_LOAD
)
type ScrollPos struct {
@@ -46,10 +47,11 @@ type AsyncResult struct {
diff_map map[string]*Patch
}
var image_collection *graphics.ImageCollection
type Handler struct {
async_results chan AsyncResult
shortcut_tracker config.ShortcutTracker
pending_keys []string
left, right string
collection *Collection
diff_map map[string]*Patch
@@ -78,8 +80,8 @@ func (self *Handler) calculate_statistics() {
var DebugPrintln = tty.DebugPrintln
func (self *Handler) initialize() {
self.pending_keys = make([]string, 0, 4)
self.rl = readline.New(self.lp, readline.RlInit{DontMarkPrompts: true, Prompt: "/"})
image_collection = graphics.NewImageCollection()
self.current_context_count = opts.Context
if self.current_context_count < 0 {
self.current_context_count = int(conf.Num_context_lines)
@@ -154,12 +156,42 @@ func (self *Handler) highlight_all() {
}
func (self *Handler) load_all_images() {
self.collection.Apply(func(path, item_type, changed_path string) error {
if path != "" && is_image(path) {
image_collection.AddPaths(path)
}
if changed_path != "" && is_image(changed_path) {
image_collection.AddPaths(changed_path)
}
return nil
})
go func() {
r := AsyncResult{rtype: IMAGE_LOAD}
image_collection.LoadAll()
self.async_results <- r
self.lp.WakeupMainThread()
}()
}
func (self *Handler) rerender_diff() error {
if self.diff_map != nil && self.collection != nil {
err := self.render_diff()
if err != nil {
return err
}
self.draw_screen()
}
return nil
}
func (self *Handler) handle_async_result(r AsyncResult) error {
switch r.rtype {
case COLLECTION:
self.collection = r.collection
self.generate_diff()
self.highlight_all()
self.load_all_images()
case DIFF:
self.diff_map = r.diff_map
self.calculate_statistics()
@@ -176,14 +208,8 @@ func (self *Handler) handle_async_result(r AsyncResult) error {
self.restore_position = nil
}
self.draw_screen()
case HIGHLIGHT:
if self.diff_map != nil && self.collection != nil {
err := self.render_diff()
if err != nil {
return err
}
self.draw_screen()
}
case HIGHLIGHT, IMAGE_LOAD:
return self.rerender_diff()
}
return nil
}

View File

@@ -11,6 +11,7 @@ import (
"kitty/tools/tui/graphics"
"kitty/tools/tui/loop"
"kitty/tools/utils"
"kitty/tools/utils/images"
"kitty/tools/utils/shm"
)
@@ -58,7 +59,7 @@ func DetectSupport(timeout time.Duration) (memory, files, direct bool, err error
}
direct_query_id = g(graphics.GRT_transmission_direct, "123")
tf, err := graphics.CreateTempInRAM()
tf, err := images.CreateTempInRAM()
if err == nil {
file_query_id = g(graphics.GRT_transmission_tempfile, tf.Name())
temp_files_to_delete = append(temp_files_to_delete, tf.Name())

View File

@@ -18,6 +18,7 @@ import (
"kitty/tools/tty"
"kitty/tools/tui/graphics"
"kitty/tools/utils"
"kitty/tools/utils/images"
"kitty/tools/utils/shm"
)
@@ -142,7 +143,7 @@ func (self *opened_input) PutOnFilesystem() (err error) {
if self.name_to_unlink != "" {
return
}
f, err := graphics.CreateTempInRAM()
f, err := images.CreateTempInRAM()
if err != nil {
return fmt.Errorf("Failed to create a temporary file to store input data with error: %w", err)
}

View File

@@ -152,7 +152,7 @@ func transmit_file(imgd *image_data, frame_num int, frame *image_frame) (err err
frame.shm.Close()
frame.shm = nil
} else {
f, err := graphics.CreateTempInRAM()
f, err := images.CreateTempInRAM()
if err != nil {
return fmt.Errorf("Failed to create a temp file for image data transmission: %w", err)
}