Add option select_by_word_characters_forward

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 <kgraefe@paktolos.net>
This commit is contained in:
Konrad Gräfe 2022-05-16 11:09:36 +02:00
parent d3656bf7e9
commit 8c41cc8d3e
7 changed files with 56 additions and 9 deletions

View File

@ -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='''

View File

@ -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)

View File

@ -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);

View File

@ -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;

View File

@ -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 = '.'

View File

@ -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;

View File

@ -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;