From 8c41cc8d3ef9a856245b900c3e990daf4fa6ed08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konrad=20Gr=C3=A4fe?= Date: Mon, 16 May 2022 11:09:36 +0200 Subject: [PATCH] Add option select_by_word_characters_forward MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds an option to select separate characters for forward and backward word extension on double click. If it is empty the old behavior is preserved. This is the default. If it is not empty, select_by_word_characters_forward will be used for extending the selection in forward direction (right) and select_by_word_characters will be used for extending in backward direction (left). Signed-off-by: Konrad Gräfe --- kitty/options/definition.py | 12 ++++++++++++ kitty/options/parse.py | 3 +++ kitty/options/to-c-generated.h | 15 +++++++++++++++ kitty/options/to-c.h | 6 ++++++ kitty/options/types.py | 2 ++ kitty/screen.c | 26 +++++++++++++++++--------- kitty/state.h | 1 + 7 files changed, 56 insertions(+), 9 deletions(-) diff --git a/kitty/options/definition.py b/kitty/options/definition.py index 95ed228bd..c0d1d74e3 100644 --- a/kitty/options/definition.py +++ b/kitty/options/definition.py @@ -492,6 +492,18 @@ Unicode database will be matched. ''' ) +opt('select_by_word_characters_forward', '', + ctype='!select_by_word_characters_forward', + long_text=''' +Characters considered part of a word when extending the selection forward on +double clicking. In addition to these characters any character that is marked +as an alphanumeric character in the Unicode database will be matched. + +If empty (default) :opt:`select_by_word_characters` will be used for both +directions. +''' + ) + opt('click_interval', '-1.0', option_type='float', ctype='time', long_text=''' diff --git a/kitty/options/parse.py b/kitty/options/parse.py index 9eb4e557b..9037a12a0 100644 --- a/kitty/options/parse.py +++ b/kitty/options/parse.py @@ -1163,6 +1163,9 @@ class Parser: def select_by_word_characters(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['select_by_word_characters'] = str(val) + def select_by_word_characters_forward(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + ans['select_by_word_characters_forward'] = str(val) + def selection_background(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['selection_background'] = to_color_or_none(val) diff --git a/kitty/options/to-c-generated.h b/kitty/options/to-c-generated.h index 566e02aa8..ace890ccd 100644 --- a/kitty/options/to-c-generated.h +++ b/kitty/options/to-c-generated.h @@ -304,6 +304,19 @@ convert_from_opts_select_by_word_characters(PyObject *py_opts, Options *opts) { Py_DECREF(ret); } +static void +convert_from_python_select_by_word_characters_forward(PyObject *val, Options *opts) { + select_by_word_characters_forward(val, opts); +} + +static void +convert_from_opts_select_by_word_characters_forward(PyObject *py_opts, Options *opts) { + PyObject *ret = PyObject_GetAttrString(py_opts, "select_by_word_characters_forward"); + if (ret == NULL) return; + convert_from_python_select_by_word_characters_forward(ret, opts); + Py_DECREF(ret); +} + static void convert_from_python_click_interval(PyObject *val, Options *opts) { opts->click_interval = parse_s_double_to_monotonic_t(val); @@ -1067,6 +1080,8 @@ convert_opts_from_python_opts(PyObject *py_opts, Options *opts) { if (PyErr_Occurred()) return false; convert_from_opts_select_by_word_characters(py_opts, opts); if (PyErr_Occurred()) return false; + convert_from_opts_select_by_word_characters_forward(py_opts, opts); + if (PyErr_Occurred()) return false; convert_from_opts_click_interval(py_opts, opts); if (PyErr_Occurred()) return false; convert_from_opts_focus_follows_mouse(py_opts, opts); diff --git a/kitty/options/to-c.h b/kitty/options/to-c.h index 78b09f202..0a80e8226 100644 --- a/kitty/options/to-c.h +++ b/kitty/options/to-c.h @@ -187,6 +187,12 @@ select_by_word_characters(PyObject *chars, Options *opts) { opts->select_by_word_characters = list_of_chars(chars); } +static void +select_by_word_characters_forward(PyObject *chars, Options *opts) { + free(opts->select_by_word_characters_forward); + opts->select_by_word_characters_forward = list_of_chars(chars); +} + static void tab_bar_style(PyObject *val, Options *opts) { opts->tab_bar_hidden = PyUnicode_CompareWithASCIIString(val, "hidden") == 0 ? true: false; diff --git a/kitty/options/types.py b/kitty/options/types.py index 348600484..01a4bf5a7 100644 --- a/kitty/options/types.py +++ b/kitty/options/types.py @@ -411,6 +411,7 @@ option_names = ( # {{{ 'scrollback_pager', 'scrollback_pager_history_size', 'select_by_word_characters', + 'select_by_word_characters_forward', 'selection_background', 'selection_foreground', 'shell', @@ -561,6 +562,7 @@ class Options: scrollback_pager: typing.List[str] = ['less', '--chop-long-lines', '--RAW-CONTROL-CHARS', '+INPUT_LINE_NUMBER'] scrollback_pager_history_size: int = 0 select_by_word_characters: str = '@-./_~?&=%+#' + select_by_word_characters_forward: str = '' selection_background: typing.Optional[kitty.fast_data_types.Color] = Color(255, 250, 205) selection_foreground: typing.Optional[kitty.fast_data_types.Color] = Color(0, 0, 0) shell: str = '.' diff --git a/kitty/screen.c b/kitty/screen.c index 43c7076b4..ad13e38f0 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -3222,7 +3222,15 @@ screen_selection_range_for_line(Screen *self, index_type y, index_type *start, i } static bool -is_opt_word_char(char_type ch) { +is_opt_word_char(char_type ch, bool forward) { + if (forward && OPT(select_by_word_characters_forward)) { + for (const char_type *p = OPT(select_by_word_characters_forward); *p; p++) { + if (ch == *p) return true; + } + if (*OPT(select_by_word_characters_forward)) { + return false; + } + } if (OPT(select_by_word_characters)) { for (const char_type *p = OPT(select_by_word_characters); *p; p++) { if (ch == *p) return true; @@ -3232,9 +3240,9 @@ is_opt_word_char(char_type ch) { } static bool -is_char_ok_for_word_extension(Line* line, index_type x) { +is_char_ok_for_word_extension(Line* line, index_type x, bool forward) { char_type ch = line->cpu_cells[x].ch; - if (is_word_char(ch) || is_opt_word_char(ch)) return true; + if (is_word_char(ch) || is_opt_word_char(ch, forward)) return true; // pass : from :// so that common URLs are matched if (ch == ':' && x + 2 < line->xnum && line->cpu_cells[x+1].ch == '/' && line->cpu_cells[x+2].ch == '/') return true; return false; @@ -3247,26 +3255,26 @@ screen_selection_range_for_word(Screen *self, const index_type x, const index_ty Line *line = visual_line_(self, y); *y1 = y; *y2 = y; -#define is_ok(x) is_char_ok_for_word_extension(line, x) - if (!is_ok(x)) { +#define is_ok(x, forward) is_char_ok_for_word_extension(line, x, forward) + if (!is_ok(x, false)) { if (initial_selection) return false; *s = x; *e = x; return true; } start = x; end = x; while(true) { - while(start > 0 && is_ok(start - 1)) start--; + while(start > 0 && is_ok(start - 1, false)) start--; if (start > 0 || !line->attrs.continued || *y1 == 0) break; line = visual_line_(self, *y1 - 1); - if (!is_ok(self->columns - 1)) break; + if (!is_ok(self->columns - 1, false)) break; (*y1)--; start = self->columns - 1; } line = visual_line_(self, *y2); while(true) { - while(end < self->columns - 1 && is_ok(end + 1)) end++; + while(end < self->columns - 1 && is_ok(end + 1, true)) end++; if (end < self->columns - 1 || *y2 >= self->lines - 1) break; line = visual_line_(self, *y2 + 1); - if (!line->attrs.continued || !is_ok(0)) break; + if (!line->attrs.continued || !is_ok(0, true)) break; (*y2)++; end = 0; } *s = start; *e = end; diff --git a/kitty/state.h b/kitty/state.h index 23d6b2f04..21f42a019 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -33,6 +33,7 @@ typedef struct { unsigned int scrollback_pager_history_size; bool scrollback_fill_enlarged_window; char_type *select_by_word_characters; + char_type *select_by_word_characters_forward; color_type url_color, background, foreground, active_border_color, inactive_border_color, bell_border_color, tab_bar_background, tab_bar_margin_color; color_type mark1_foreground, mark1_background, mark2_foreground, mark2_background, mark3_foreground, mark3_background; monotonic_t repaint_delay, input_delay;