diff --git a/kitty/options/definition.py b/kitty/options/definition.py index b5bccc8d4..ab55b9f5c 100644 --- a/kitty/options/definition.py +++ b/kitty/options/definition.py @@ -378,6 +378,15 @@ are still clickable. ''' ) +opt('url_excluded_characters', '', + ctype='!url_excluded_characters', + long_text=''' +Characters not considered part of a URL under the mouse. In addition to these +characters any character that is marked as the unicode C and Z categories will +be excluded. +''' + ) + opt('copy_on_select', 'no', option_type='copy_on_select', long_text=''' diff --git a/kitty/options/parse.py b/kitty/options/parse.py index c84cb4f41..93db1a6f8 100644 --- a/kitty/options/parse.py +++ b/kitty/options/parse.py @@ -1206,6 +1206,9 @@ class Parser: def url_color(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['url_color'] = to_color(val) + def url_excluded_characters(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + ans['url_excluded_characters'] = str(val) + def url_prefixes(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['url_prefixes'] = url_prefixes(val) diff --git a/kitty/options/to-c-generated.h b/kitty/options/to-c-generated.h index 7fa2db6b1..860c7d295 100644 --- a/kitty/options/to-c-generated.h +++ b/kitty/options/to-c-generated.h @@ -265,6 +265,19 @@ convert_from_opts_detect_urls(PyObject *py_opts, Options *opts) { Py_DECREF(ret); } +static void +convert_from_python_url_excluded_characters(PyObject *val, Options *opts) { + url_excluded_characters(val, opts); +} + +static void +convert_from_opts_url_excluded_characters(PyObject *py_opts, Options *opts) { + PyObject *ret = PyObject_GetAttrString(py_opts, "url_excluded_characters"); + if (ret == NULL) return; + convert_from_python_url_excluded_characters(ret, opts); + Py_DECREF(ret); +} + static void convert_from_python_select_by_word_characters(PyObject *val, Options *opts) { select_by_word_characters(val, opts); @@ -931,6 +944,8 @@ convert_opts_from_python_opts(PyObject *py_opts, Options *opts) { if (PyErr_Occurred()) return false; convert_from_opts_detect_urls(py_opts, opts); if (PyErr_Occurred()) return false; + convert_from_opts_url_excluded_characters(py_opts, opts); + if (PyErr_Occurred()) return false; convert_from_opts_select_by_word_characters(py_opts, opts); if (PyErr_Occurred()) return false; convert_from_opts_click_interval(py_opts, opts); diff --git a/kitty/options/to-c.h b/kitty/options/to-c.h index 2875f1d23..1eab9215b 100644 --- a/kitty/options/to-c.h +++ b/kitty/options/to-c.h @@ -124,6 +124,15 @@ url_prefixes(PyObject *up, Options *opts) { } } +static void +url_excluded_characters(PyObject *chars, Options *opts) { + if (!PyUnicode_Check(chars)) { PyErr_SetString(PyExc_TypeError, "url_excluded_characters must be a string"); return; } + for (size_t i = 0; i < MIN((size_t)PyUnicode_GET_LENGTH(chars), sizeof(opts->url_excluded_characters)/sizeof(opts->url_excluded_characters[0])); i++) { + opts->url_excluded_characters[i] = PyUnicode_READ(PyUnicode_KIND(chars), PyUnicode_DATA(chars), i); + } + opts->url_excluded_characters_count = PyUnicode_GET_LENGTH(chars); +} + static void select_by_word_characters(PyObject *chars, Options *opts) { if (!PyUnicode_Check(chars)) { PyErr_SetString(PyExc_TypeError, "select_by_word_characters must be a string"); return; } diff --git a/kitty/options/types.py b/kitty/options/types.py index 6b3632ecb..3d4f1150c 100644 --- a/kitty/options/types.py +++ b/kitty/options/types.py @@ -417,6 +417,7 @@ option_names = ( # {{{ 'touch_scroll_multiplier', 'update_check_interval', 'url_color', + 'url_excluded_characters', 'url_prefixes', 'url_style', 'visual_bell_duration', @@ -546,6 +547,7 @@ class Options: touch_scroll_multiplier: float = 1.0 update_check_interval: float = 24.0 url_color: Color = Color(red=0, green=135, blue=189) + url_excluded_characters: str = '' url_prefixes: typing.Tuple[str, ...] = ('http', 'https', 'file', 'ftp', 'gemini', 'irc', 'gopher', 'mailto', 'news', 'git') url_style: int = 3 visual_bell_duration: float = 0 diff --git a/kitty/state.h b/kitty/state.h index 66f6f70ec..84a4463f8 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -70,6 +70,7 @@ typedef struct { UrlPrefix *values; size_t num, max_prefix_len; } url_prefixes; + char_type url_excluded_characters[256]; size_t url_excluded_characters_count; bool detect_urls; bool tab_bar_hidden; double font_size; diff --git a/kitty/unicode-data.h b/kitty/unicode-data.h index 54c599552..26d1d5d23 100644 --- a/kitty/unicode-data.h +++ b/kitty/unicode-data.h @@ -1,5 +1,6 @@ #pragma once #include "data-types.h" +#include "state.h" #define VS15 1285 #define VS16 1286 @@ -13,6 +14,8 @@ combining_type mark_for_codepoint(char_type c); static inline bool is_url_char(uint32_t ch) { + for (size_t i = 0; i < OPT(url_excluded_characters_count); i++) + if (ch == OPT(url_excluded_characters)[i]) return false; return ch && !is_CZ_category(ch); }