From 468168b9de9e6399aeec652509ccfd0537633be4 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 29 Mar 2023 13:22:34 +0530 Subject: [PATCH] Refactor diff search to use new render layout --- kittens/diff/render.go | 5 +-- kittens/diff/search.go | 74 ++++++++++++++++++++++-------------------- tools/tui/mouse.go | 2 +- 3 files changed, 43 insertions(+), 38 deletions(-) diff --git a/kittens/diff/render.go b/kittens/diff/render.go index 934b40743..a41ef268c 100644 --- a/kittens/diff/render.go +++ b/kittens/diff/render.go @@ -59,7 +59,7 @@ type LogicalLine struct { } func (self *LogicalLine) render_screen_line(n int, lp *loop.Loop, margin_size, columns int) { - if n >= len(self.screen_lines) { + if n >= len(self.screen_lines) || n < 0 { return } sl := self.screen_lines[n] @@ -147,7 +147,7 @@ func place_in(text string, sz int) string { } var format_as_sgr struct { - title, margin, added, removed, added_margin, removed_margin, filler, margin_filler, hunk_margin, hunk, selection string + title, margin, added, removed, added_margin, removed_margin, filler, margin_filler, hunk_margin, hunk, selection, search string } var statusline_format, added_count_format, removed_count_format, message_format, selection_format func(...any) string @@ -173,6 +173,7 @@ func create_formatters() { format_as_sgr.margin = only_open(fmt.Sprintf("fg=%s bg=%s", conf.Margin_fg.AsRGBSharp(), conf.Margin_bg.AsRGBSharp())) format_as_sgr.hunk = only_open(fmt.Sprintf("fg=%s bg=%s", conf.Margin_fg.AsRGBSharp(), conf.Hunk_bg.AsRGBSharp())) format_as_sgr.hunk_margin = only_open(fmt.Sprintf("fg=%s bg=%s", conf.Margin_fg.AsRGBSharp(), conf.Hunk_margin_bg.AsRGBSharp())) + format_as_sgr.search = only_open(fmt.Sprintf("fg=%s bg=%s", conf.Search_fg.AsRGBSharp(), conf.Search_bg.AsRGBSharp())) statusline_format = ctx.SprintFunc(fmt.Sprintf("fg=%s", conf.Margin_fg.AsRGBSharp())) added_count_format = ctx.SprintFunc(fmt.Sprintf("fg=%s", conf.Highlight_added_bg.AsRGBSharp())) removed_count_format = ctx.SprintFunc(fmt.Sprintf("fg=%s", conf.Highlight_removed_bg.AsRGBSharp())) diff --git a/kittens/diff/search.go b/kittens/diff/search.go index c2a8a868f..7eb8286be 100644 --- a/kittens/diff/search.go +++ b/kittens/diff/search.go @@ -8,7 +8,7 @@ import ( "strings" "sync" - "kitty/tools/tui/sgr" + "kitty/tools/tui" "kitty/tools/utils" "kitty/tools/utils/images" "kitty/tools/wcswidth" @@ -20,7 +20,7 @@ var _ = fmt.Print type Search struct { pat *regexp.Regexp - matches map[ScrollPos][]*sgr.Span + matches map[ScrollPos][]Span } func (self *Search) Len() int { return len(self.matches) } @@ -28,12 +28,16 @@ func (self *Search) Len() int { return len(self.matches) } func (self *Search) find_matches_in_lines(clean_lines []string, origin int, send_result func(screen_line, offset, size int)) { lengths := utils.Map(func(x string) int { return len(x) }, clean_lines) offsets := make([]int, len(clean_lines)) + cell_lengths := utils.Map(wcswidth.Stringwidth, clean_lines) + cell_offsets := make([]int, len(clean_lines)) for i := range clean_lines { if i > 0 { offsets[i] = offsets[i-1] + lengths[i-1] + cell_offsets[i] = cell_offsets[i-1] + cell_lengths[i-1] } } - matches := self.pat.FindAllStringIndex(strings.Join(clean_lines, ""), -1) + joined_text := strings.Join(clean_lines, "") + matches := self.pat.FindAllStringIndex(joined_text, -1) pos := 0 find_pos := func(start int) int { @@ -57,15 +61,17 @@ func (self *Search) find_matches_in_lines(clean_lines []string, origin int, send end_line := find_pos(end) if end_line > -1 { for i := start_line; i <= end_line; i++ { - offset := 0 + cell_start := 0 if i == start_line { - offset = start - offsets[i] + byte_offset := start - offsets[i] + cell_start = wcswidth.Stringwidth(clean_lines[i][:byte_offset]) } - size := len(clean_lines[i]) - offset + cell_end := cell_lengths[i] if i == end_line { - size = (end - offsets[i]) - offset + byte_offset := end - offsets[i] + cell_end = wcswidth.Stringwidth(clean_lines[i][:byte_offset]) } - send_result(i, origin+offset, size) + send_result(i, origin+cell_start, cell_end-cell_start) } } } @@ -77,20 +83,12 @@ func (self *Search) find_matches_in_line(line *LogicalLine, margin_size, cols in half_width := cols / 2 right_offset := half_width + margin_size left_clean_lines, right_clean_lines := make([]string, len(line.screen_lines)), make([]string, len(line.screen_lines)) - lt := line.line_type - for i, line := range line.screen_lines { - line = wcswidth.StripEscapeCodes(line) - if lt == HUNK_TITLE_LINE || lt == FULL_TITLE_LINE { - if len(line) > margin_size { - left_clean_lines[i] = line[margin_size:] - } + for i, sl := range line.screen_lines { + if line.is_full_width { + left_clean_lines[i] = wcswidth.StripEscapeCodes(sl.left.marked_up_text) } else { - if len(line) >= half_width+1 { - left_clean_lines[i] = line[margin_size:half_width] - } - if len(line) > right_offset { - right_clean_lines[i] = line[right_offset:] - } + left_clean_lines[i] = wcswidth.StripEscapeCodes(sl.left.marked_up_text) + right_clean_lines[i] = wcswidth.StripEscapeCodes(sl.right.marked_up_text) } } self.find_matches_in_lines(left_clean_lines, margin_size, send_result) @@ -101,14 +99,14 @@ func (self *Search) Has(pos ScrollPos) bool { return len(self.matches[pos]) > 0 } +type Span struct{ start, end int } + func (self *Search) search(logical_lines *LogicalLines) { margin_size := logical_lines.margin_size cols := logical_lines.columns - self.matches = make(map[ScrollPos][]*sgr.Span) + self.matches = make(map[ScrollPos][]Span) ctx := images.Context{} mutex := sync.Mutex{} - s := sgr.NewSpan(0, 0) - s.SetForeground(conf.Search_fg).SetBackground(conf.Search_bg) ctx.Parallel(0, logical_lines.Len(), func(nums <-chan int) { for i := range nums { line := logical_lines.At(i) @@ -116,30 +114,36 @@ func (self *Search) search(logical_lines *LogicalLines) { continue } self.find_matches_in_line(line, margin_size, cols, func(screen_line, offset, size int) { - mutex.Lock() - defer mutex.Unlock() - sn := *s - sn.Offset, sn.Size = offset, size - pos := ScrollPos{i, screen_line} - self.matches[pos] = append(self.matches[pos], &sn) + if size > 0 { + mutex.Lock() + defer mutex.Unlock() + pos := ScrollPos{i, screen_line} + self.matches[pos] = append(self.matches[pos], Span{offset, offset + size - 1}) + } }) } }) for _, spans := range self.matches { - slices.SortFunc(spans, func(a, b *sgr.Span) bool { return a.Offset < b.Offset }) + slices.SortFunc(spans, func(a, b Span) bool { return a.start < b.start }) } } -func (self *Search) markup_line(line string, pos ScrollPos) string { +func (self *Search) markup_line(pos ScrollPos, y int) string { spans := self.matches[pos] if spans == nil { - return line + return "" } - return sgr.InsertFormatting(line, spans...) + sgr := format_as_sgr.search[2:] + sgr = sgr[:len(sgr)-1] + ans := make([]byte, 0, 32) + for _, span := range spans { + ans = append(ans, tui.FormatPartOfLine(sgr, span.start, span.end, y)...) + } + return utils.UnsafeBytesToString(ans) } func do_search(pat *regexp.Regexp, logical_lines *LogicalLines) *Search { - ans := &Search{pat: pat, matches: make(map[ScrollPos][]*sgr.Span)} + ans := &Search{pat: pat, matches: make(map[ScrollPos][]Span)} ans.search(logical_lines) return ans } diff --git a/tools/tui/mouse.go b/tools/tui/mouse.go index dc0b6a8b4..19ba7130e 100644 --- a/tools/tui/mouse.go +++ b/tools/tui/mouse.go @@ -140,7 +140,7 @@ func (ms *MouseSelection) LineBounds(line_pos LinePos) (start_x, end_x int) { return -1, -1 } -func FormatPartOfLine(sgr string, start_x, end_x, y int) string { +func FormatPartOfLine(sgr string, start_x, end_x, y int) string { // uses zero based indices // DECCARA used to set formatting in specified region using zero based indexing return fmt.Sprintf("\x1b[%d;%d;%d;%d;%s$r", y+1, start_x+1, y+1, end_x+1, sgr) }