diff --git a/tools/cmd/diff/render.go b/tools/cmd/diff/render.go index cd22d08c1..1743e0069 100644 --- a/tools/cmd/diff/render.go +++ b/tools/cmd/diff/render.go @@ -35,6 +35,15 @@ type LogicalLine struct { is_change_start bool } +func (self *LogicalLine) IncrementScrollPosBy(pos *ScrollPos, amt int) (delta int) { + if len(self.screen_lines) > 0 { + npos := utils.Max(0, utils.Min(pos.screen_line+amt, len(self.screen_lines)-1)) + delta = npos - pos.screen_line + pos.screen_line = npos + } + return +} + func fit_in(text string, count int) string { truncated := wcswidth.TruncateToVisualLength(text, count) if len(truncated) >= len(text) { @@ -130,6 +139,57 @@ type LogicalLines struct { margin_size, columns int } +func (self *LogicalLines) At(i int) *LogicalLine { return self.lines[i] } +func (self *LogicalLines) Len() int { return len(self.lines) } + +func (self *LogicalLines) Minus(a, b ScrollPos) (delta int) { + // a - b + amt := 1 + if a.Less(b) { + amt = -1 + } + for a != b { + d := self.IncrementScrollPosBy(&a, amt) + if d == 0 { + break + } + delta += d + } + return +} + +func (self *LogicalLines) IncrementScrollPosBy(pos *ScrollPos, amt int) (delta int) { + if pos.logical_line < 0 || pos.logical_line >= len(self.lines) || amt == 0 { + return + } + one := 1 + if amt < 0 { + one = -1 + } + for amt != 0 { + line := self.lines[pos.logical_line] + d := line.IncrementScrollPosBy(pos, amt) + if d == 0 { + nlp := pos.logical_line + one + if nlp < 0 || nlp >= len(self.lines) { + break + } + pos.logical_line = nlp + if one > 0 { + pos.screen_line = 0 + } else { + pos.screen_line = len(self.lines[nlp].screen_lines) - 1 + } + delta += one + amt -= one + } else { + amt -= d + delta += d + } + } + return +} + func image_lines(left_path, right_path string, columns, margin_size int, ans []*LogicalLine) ([]*LogicalLine, error) { // TODO: Implement this return ans, nil diff --git a/tools/cmd/diff/ui.go b/tools/cmd/diff/ui.go index 49b0d3189..3332b6b09 100644 --- a/tools/cmd/diff/ui.go +++ b/tools/cmd/diff/ui.go @@ -24,6 +24,10 @@ type ScrollPos struct { logical_line, screen_line int } +func (self ScrollPos) Less(other ScrollPos) bool { + return self.logical_line < other.logical_line || (self.logical_line == other.logical_line && self.screen_line < other.screen_line) +} + type AsyncResult struct { err error rtype ResultType @@ -43,7 +47,7 @@ type Handler struct { current_context_count, original_context_count int added_count, removed_count int screen_size struct{ rows, columns, num_lines int } - scroll_pos ScrollPos + scroll_pos, max_scroll_pos ScrollPos } func (self *Handler) calculate_statistics() { @@ -161,10 +165,26 @@ func (self *Handler) on_resize(old_size, new_size loop.ScreenSize) error { } func (self *Handler) render_diff() (err error) { + if self.screen_size.columns < 8 { + return fmt.Errorf("Screen too narrow, need at least 8 columns") + } + if self.screen_size.rows < 2 { + return fmt.Errorf("Screen too short, need at least 2 rows") + } self.logical_lines, err = render(self.collection, self.diff_map, self.screen_size.columns) if err != nil { return err } + last := self.logical_lines.Len() - 1 + self.max_scroll_pos.logical_line = last + if last > -1 { + self.max_scroll_pos.screen_line = len(self.logical_lines.At(last).screen_lines) - 1 + } else { + self.max_scroll_pos.screen_line = 0 + } + DebugPrintln(self.max_scroll_pos) + self.logical_lines.IncrementScrollPosBy(&self.max_scroll_pos, -self.screen_size.num_lines+1) + DebugPrintln(self.max_scroll_pos) return nil // TODO: current search see python implementation } @@ -210,8 +230,14 @@ func (self *Handler) on_key_event(ev *loop.KeyEvent) error { return nil } -func (self *Handler) scroll_lines(amt int) { - // TODO: Implement me +func (self *Handler) scroll_lines(amt int) (delta int) { + before := self.scroll_pos + delta = self.logical_lines.IncrementScrollPosBy(&self.scroll_pos, amt) + if delta > 0 && self.max_scroll_pos.Less(self.scroll_pos) { + self.scroll_pos = self.max_scroll_pos + delta = self.logical_lines.Minus(self.scroll_pos, before) + } + return } func (self *Handler) dispatch_action(name, args string) error { @@ -221,7 +247,11 @@ func (self *Handler) dispatch_action(name, args string) error { case `scroll_by`: amt, err := strconv.Atoi(args) if err == nil { - self.scroll_lines(amt) + if self.scroll_lines(amt) == 0 { + self.lp.Beep() + } else { + self.draw_screen() + } } else { self.lp.Beep() }