kitty/tools/cmd/diff/render.go
2023-03-27 07:53:54 +05:30

149 lines
4.6 KiB
Go

// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>
package diff
import (
"fmt"
"kitty/tools/utils"
"kitty/tools/utils/style"
"kitty/tools/wcswidth"
"strconv"
"strings"
)
var _ = fmt.Print
type LineType int
const (
TITLE_LINE LineType = iota
CHANGE_LINE
IMAGE_LINE
EMPTY_LINE
)
type Reference struct {
path string
linenum int
}
type LogicalLine struct {
src Reference
line_type LineType
screen_lines []string
}
func fit_in(text string, count int) string {
truncated := wcswidth.TruncateToVisualLength(text, count)
if len(truncated) >= len(text) {
return text
}
if count > 1 {
truncated = wcswidth.TruncateToVisualLength(text, count-1)
}
return truncated + ``
}
func fill_in(text string, sz int) string {
w := wcswidth.Stringwidth(text)
if w < sz {
text += strings.Repeat(` `, (sz - w))
}
return text
}
func place_in(text string, sz int) string {
return fill_in(fit_in(text, sz), sz)
}
var title_format, text_format, margin_format, added_format, removed_format, added_margin_format, removed_margin_format, filler_format, margin_filler_format, hunk_margin_format, hunk_format func(...any) string
func create_formatters() {
ctx := style.Context{AllowEscapeCodes: true}
text_format = ctx.SprintFunc(fmt.Sprintf("bg=%s", conf.Background.AsRGBSharp()))
filler_format = ctx.SprintFunc(fmt.Sprintf("bg=%s", conf.Filler_bg.AsRGBSharp()))
if conf.Margin_filler_bg.IsNull {
margin_filler_format = ctx.SprintFunc(fmt.Sprintf("bg=%s", conf.Filler_bg.AsRGBSharp()))
} else {
margin_filler_format = ctx.SprintFunc(fmt.Sprintf("bg=%s", conf.Margin_filler_bg.Color.AsRGBSharp()))
}
added_format = ctx.SprintFunc(fmt.Sprintf("bg=%s", conf.Added_bg.AsRGBSharp()))
added_margin_format = ctx.SprintFunc(fmt.Sprintf("fg=%s bg=%s", conf.Margin_fg.AsRGBSharp(), conf.Added_margin_bg.AsRGBSharp()))
removed_format = ctx.SprintFunc(fmt.Sprintf("bg=%s", conf.Removed_bg.AsRGBSharp()))
removed_margin_format = ctx.SprintFunc(fmt.Sprintf("fg=%s bg=%s", conf.Margin_fg.AsRGBSharp(), conf.Removed_margin_bg.AsRGBSharp()))
title_format = ctx.SprintFunc(fmt.Sprintf("fg=%s bg=%s bold", conf.Title_fg.AsRGBSharp(), conf.Title_bg.AsRGBSharp()))
margin_format = ctx.SprintFunc(fmt.Sprintf("fg=%s bg=%s", conf.Margin_fg.AsRGBSharp(), conf.Margin_bg.AsRGBSharp()))
hunk_format = ctx.SprintFunc(fmt.Sprintf("fg=%s bg=%s", conf.Margin_fg.AsRGBSharp(), conf.Hunk_bg.AsRGBSharp()))
hunk_margin_format = ctx.SprintFunc(fmt.Sprintf("fg=%s bg=%s", conf.Margin_fg.AsRGBSharp(), conf.Hunk_margin_bg.AsRGBSharp()))
}
func title_lines(left_path, right_path string, columns, margin_size int, ans []*LogicalLine) []*LogicalLine {
left_name, right_name := path_name_map[left_path], path_name_map[right_path]
name := ""
m := strings.Repeat(` `, margin_size)
if right_name != "" && right_name != left_name {
n1 := fit_in(m+sanitize(left_name), columns/2-margin_size)
n1 = place_in(n1, columns/2)
n2 := fit_in(m+sanitize(right_name), columns/2-margin_size)
n2 = place_in(n2, columns/2)
name = n1 + n2
} else {
name = place_in(m+sanitize(left_name), columns)
}
ll := LogicalLine{line_type: TITLE_LINE, src: Reference{path: left_path, linenum: 0}}
l1 := ll
l1.screen_lines = []string{title_format(name)}
l2 := ll
l2.screen_lines = []string{title_format(strings.Repeat("━", columns+1))}
return append(ans, &l1, &l2)
}
type LogicalLines struct {
lines []*LogicalLine
margin_size, columns int
}
func render(collection *Collection, diff_map map[string]*Patch, columns int) (*LogicalLines, error) {
largest_line_number := 0
collection.Apply(func(path, typ, changed_path string) error {
if typ == "diff" {
patch := diff_map[path]
if patch != nil {
largest_line_number = utils.Max(largest_line_number, patch.largest_line_number)
}
}
return nil
})
margin_size := utils.Max(3, len(strconv.Itoa(largest_line_number))+1)
ans := make([]*LogicalLine, 0, 1024)
empty_line := LogicalLine{line_type: EMPTY_LINE}
err := collection.Apply(func(path, item_type, changed_path string) error {
ans = title_lines(path, changed_path, columns, margin_size, ans)
defer func() {
el := empty_line
ans = append(ans, &el)
}()
is_binary := !is_path_text(path)
if !is_binary && item_type == `diff` && !is_path_text(changed_path) {
is_binary = true
}
is_img := is_binary && is_image(path) || (item_type == `diff` && is_image(changed_path))
_ = is_img
return nil
})
if err != nil {
return nil, err
}
return &LogicalLines{lines: ans[:len(ans)-1], margin_size: margin_size, columns: columns}, nil
}
func (self *LogicalLines) num_of_screen_lines() (ans int) {
for _, l := range self.lines {
ans += len(l.screen_lines)
}
return
}