// License: GPLv3 Copyright: 2023, Kovid Goyal, package diff import ( "fmt" "kitty" "kitty/tools/config" "kitty/tools/tui/loop" "kitty/tools/utils" "path/filepath" "strconv" ) var _ = fmt.Print type SelectionBoundary struct { line ScrollPos x int } type MouseSelection struct { start, end SelectionBoundary is_active bool min_x, max_x int } type KittyOpts struct { Wheel_scroll_multiplier int } func read_relevant_kitty_opts(path string) KittyOpts { ans := KittyOpts{Wheel_scroll_multiplier: kitty.KittyConfigDefaults.Wheel_scroll_multiplier} handle_line := func(key, val string) error { switch key { case "wheel_scroll_multiplier": v, err := strconv.Atoi(val) if err == nil { ans.Wheel_scroll_multiplier = v } } return nil } cp := config.ConfigParser{LineHandler: handle_line} cp.ParseFiles(path) return ans } var RelevantKittyOpts = (&utils.Once[KittyOpts]{Run: func() KittyOpts { return read_relevant_kitty_opts(filepath.Join(utils.ConfigDir(), "kitty.conf")) }}).Get func (self *Handler) handle_wheel_event(up bool) { amt := RelevantKittyOpts().Wheel_scroll_multiplier if up { amt *= -1 } self.dispatch_action(`scroll_by`, strconv.Itoa(amt)) } func (self *Handler) start_mouse_selection(ev *loop.MouseEvent) { self.mouse_selection = MouseSelection{} ms := &self.mouse_selection available_cols := self.logical_lines.columns / 2 if ev.Cell.Y >= self.screen_size.num_lines || ev.Cell.X < self.logical_lines.margin_size || (ev.Cell.X >= available_cols && ev.Cell.X < available_cols+self.logical_lines.margin_size) { return } pos := self.scroll_pos self.logical_lines.IncrementScrollPosBy(&pos, ev.Cell.Y) ll := self.logical_lines.At(pos.logical_line) if ll.line_type == EMPTY_LINE || ll.line_type == IMAGE_LINE { return } ms.start.line = pos ms.start.x = ev.Cell.X ms.min_x = self.logical_lines.margin_size ms.max_x = available_cols - 1 if ms.start.x >= available_cols { ms.min_x += available_cols ms.max_x += available_cols } ms.start.x = utils.Max(ms.min_x, utils.Min(ms.start.x, ms.max_x)) ms.end = ms.start ms.is_active = true } func (self *Handler) update_mouse_selection(ev *loop.MouseEvent) { ms := &self.mouse_selection if !self.mouse_selection.is_active { return } pos := self.scroll_pos y := ev.Cell.Y y = utils.Max(0, utils.Min(y, self.screen_size.num_lines-1)) self.logical_lines.IncrementScrollPosBy(&pos, y) ms.end.x = ev.Cell.X ms.end.x = utils.Max(ms.min_x, utils.Min(ms.end.x, ms.max_x)) ms.end.line = pos self.draw_screen() } func (self *Handler) clear_mouse_selection() { self.mouse_selection = MouseSelection{} } func (self *Handler) finish_mouse_selection(ev *loop.MouseEvent) { self.update_mouse_selection(ev) ms := &self.mouse_selection if !self.mouse_selection.is_active { return } ms.is_active = false } func format_part_of_line(sgr string, start_x, end_x, y int) string { // 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) } func (self *Handler) add_mouse_selection_to_line(line string, line_pos ScrollPos, y int) string { ms := &self.mouse_selection if !ms.is_active { return line } a, b := ms.start.line, ms.end.line ax, bx := ms.start.x, ms.end.x if b.Less(a) { a, b = b, a ax, bx = bx, ax } if a.Less(line_pos) { if line_pos.Less(b) { line += format_part_of_line(selection_sgr, 0, ms.max_x, y) } else if b == line_pos { line += format_part_of_line(selection_sgr, 0, bx, y) } } else if a == line_pos { if line_pos.Less(b) { line += format_part_of_line(selection_sgr, ax, ms.max_x, y) } else if b == line_pos { line += format_part_of_line(selection_sgr, ax, bx, y) } } return line }