From 75387bf69d84a5ccc003ec3cfe47eed5286b0123 Mon Sep 17 00:00:00 2001 From: j Date: Thu, 26 Jun 2025 20:36:17 +0200 Subject: [PATCH] Add scroll_offset parameter to scroll_to_prompt --- kitty/fast_data_types.pyi | 2 +- kitty/options/utils.py | 27 +++++++++++++++++++++++++-- kitty/screen.c | 9 ++++++--- kitty/window.py | 23 +++++++++++++++-------- 4 files changed, 47 insertions(+), 14 deletions(-) diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index 7fc45699b..932f0f18e 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -1305,7 +1305,7 @@ class Screen: def scroll_to_next_mark(self, mark: int = 0, backwards: bool = True) -> bool: pass - def scroll_to_prompt(self, num_of_prompts: int = -1) -> bool: + def scroll_to_prompt(self, num_of_prompts: int = -1, scroll_offset = 0) -> bool: pass def set_last_visited_prompt(self, visual_y: int = 0) -> bool: diff --git a/kitty/options/utils.py b/kitty/options/utils.py index 395a31ff5..d9ff6678a 100644 --- a/kitty/options/utils.py +++ b/kitty/options/utils.py @@ -318,17 +318,40 @@ def remote_control_script(func: str, rest: str) -> FuncArgsType: return func, args -@func_with_args('nth_os_window', 'nth_window', 'scroll_to_prompt', 'visual_window_select_action_trigger', 'next_layout') +@func_with_args('nth_os_window', 'nth_window', 'visual_window_select_action_trigger', 'next_layout') def single_integer_arg(func: str, rest: str) -> FuncArgsType: try: num = int(rest) except Exception: if rest: log_error(f'Invalid number for {func}: {rest}') - num = -1 if func == 'scroll_to_prompt' else 1 + num = 1 return func, [num] +@func_with_args('scroll_to_prompt') +def scroll_to_prompt(func: str, rest: str) -> FuncArgsType: + vals = rest.strip().split() + if len(vals) > 2: + log_error('scroll_to_prompt needs one or two arguments, using defaults') + args = [-1, 0] + else: + try: + args = [int(vals[0])] + except Exception: + log_error(f'{vals[0]} is not a valid number of prompts to jump') + args = [-1] + if len(vals) == 2: + try: + args.append(int(vals[1])) + except Exception: + log_error(f'{vals[1]} is not a valid scroll offset') + args.append(0) + else: + args.append(0) + return func, args + + @func_with_args('sleep') def sleep(func: str, sleep_time: str) -> FuncArgsType: mult = 1 diff --git a/kitty/screen.c b/kitty/screen.c index dbeca2792..a40c5981b 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -2891,7 +2891,7 @@ shell_prompt_marking(Screen *self, char *buf) { } static bool -screen_history_scroll_to_prompt(Screen *self, int num_of_prompts_to_jump) { +screen_history_scroll_to_prompt(Screen *self, int num_of_prompts_to_jump, int scroll_offset) { if (self->linebuf != self->main_linebuf) return false; unsigned int old = self->scrolled_by; if (num_of_prompts_to_jump == 0) { @@ -2903,6 +2903,7 @@ screen_history_scroll_to_prompt(Screen *self, int num_of_prompts_to_jump) { int y = -self->scrolled_by; #define ensure_y_ok if (y >= (int)self->lines || -y > (int)self->historybuf->count) return false; ensure_y_ok; + y += scroll_offset; while (num_of_prompts_to_jump) { y += delta; ensure_y_ok; @@ -2910,6 +2911,7 @@ screen_history_scroll_to_prompt(Screen *self, int num_of_prompts_to_jump) { num_of_prompts_to_jump--; } } + y -= scroll_offset; #undef ensure_y_ok self->scrolled_by = y >= 0 ? 0 : -y; screen_set_last_visited_prompt(self, 0); @@ -4728,8 +4730,9 @@ scroll(Screen *self, PyObject *args) { static PyObject* scroll_to_prompt(Screen *self, PyObject *args) { int num_of_prompts = -1; - if (!PyArg_ParseTuple(args, "|i", &num_of_prompts)) return NULL; - if (screen_history_scroll_to_prompt(self, num_of_prompts)) { Py_RETURN_TRUE; } + int scroll_offset = 0; + if (!PyArg_ParseTuple(args, "|ii", &num_of_prompts, &scroll_offset)) return NULL; + if (screen_history_scroll_to_prompt(self, num_of_prompts, scroll_offset)) { Py_RETURN_TRUE; } Py_RETURN_FALSE; } diff --git a/kitty/window.py b/kitty/window.py index e3a63ba0f..7771bc92b 100644 --- a/kitty/window.py +++ b/kitty/window.py @@ -2029,18 +2029,25 @@ class Window: @ac('sc', ''' Scroll to the previous/next shell command prompt Allows easy jumping from one command to the next. Requires working - :ref:`shell_integration`. Takes a single, optional, number as argument which is - the number of prompts to jump, negative values jump up and positive values jump down. - A value of zero will jump to the last prompt visited by this action. + :ref:`shell_integration`. Takes two optional numbers as arguments: + + The first is the number of prompts to jump; negative values jump up and + positive values jump down. A value of zero will jump to the last prompt + visited by this action. Defaults to -1 + + The second is the number of lines to show above the prompt that was + jumped to. This is somewhat like `less`'s `--jump-target` option or + vim's `scrolloff` setting. Defaults to 0. + For example:: - map ctrl+p scroll_to_prompt -1 # jump to previous - map ctrl+n scroll_to_prompt 1 # jump to next - map ctrl+o scroll_to_prompt 0 # jump to last visited + map ctrl+p scroll_to_prompt -1 3 # jump to previous, showing 3 lines prior + map ctrl+n scroll_to_prompt 1 # jump to next + map ctrl+o scroll_to_prompt 0 # jump to last visited ''') - def scroll_to_prompt(self, num_of_prompts: int = -1) -> bool | None: + def scroll_to_prompt(self, num_of_prompts: int = -1, scroll_offset: int = 0) -> bool | None: if self.screen.is_main_linebuf(): - self.screen.scroll_to_prompt(num_of_prompts) + self.screen.scroll_to_prompt(num_of_prompts, scroll_offset) return None return True