mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-05-13 16:37:27 +00:00
A new option to control when hyperlinks are underlined
While kitty is never going to underline detected URLs as the performance of that is absurd, underlining hyperlinks specifically is acceptable, since they dont require detection. See #6766
This commit is contained in:
parent
954b7f87a5
commit
b4f88b4f81
10 changed files with 65 additions and 7 deletions
|
|
@ -58,6 +58,8 @@ Detailed list of changes
|
|||
|
||||
- A new mouse action ``mouse_selection word_and_line_from_point`` to select the current word under the mouse cursor and extend to end of line (:pull:`6663`)
|
||||
|
||||
- A new option :opt:`underline_hyperlinks` to control when hyperlinks are underlined (:iss:`6766`)
|
||||
|
||||
- Allow using the full range of standard mouse cursor shapes when customizing the mouse cursor
|
||||
|
||||
- macOS: When running the default shell with the login program fix :file:`~/.hushlogin` not being respected when opening windows not in the home directory (:iss:`6689`)
|
||||
|
|
|
|||
|
|
@ -660,6 +660,10 @@ line_set_char(Line *self, unsigned int at, uint32_t ch, unsigned int width, Curs
|
|||
}
|
||||
self->cpu_cells[at].ch = ch;
|
||||
self->cpu_cells[at].hyperlink_id = hyperlink_id;
|
||||
if (OPT(underline_hyperlinks) == UNDERLINE_ALWAYS && hyperlink_id) {
|
||||
g->decoration_fg = ((OPT(url_color) & COL_MASK) << 8) | 2;
|
||||
g->attrs.decoration = OPT(url_style);
|
||||
}
|
||||
memset(self->cpu_cells[at].cc_idx, 0, sizeof(self->cpu_cells[at].cc_idx));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -486,7 +486,8 @@ opt('detect_urls', 'yes',
|
|||
long_text='''
|
||||
Detect URLs under the mouse. Detected URLs are highlighted with an underline and
|
||||
the mouse cursor becomes a hand over them. Even if this option is disabled, URLs
|
||||
are still clickable.
|
||||
are still clickable. See also the :opt:`underline_hyperlinks` option to control
|
||||
how hyperlinks (as opposed to plain text URLs) are displayed.
|
||||
'''
|
||||
)
|
||||
|
||||
|
|
@ -510,6 +511,16 @@ When the mouse hovers over a terminal hyperlink, show the actual URL that will
|
|||
be activated when the hyperlink is clicked.
|
||||
''')
|
||||
|
||||
|
||||
opt('underline_hyperlinks', 'hover', choices=('hover', 'always', 'never'),
|
||||
ctype='underline_hyperlinks', long_text='''
|
||||
Control how hyperlinks are underlined. They can either be underlined on mouse
|
||||
``hover``, ``always`` (i.e. permanently underlined) or ``never`` which means
|
||||
that kitty will not apply any underline styling to hyperlinks.
|
||||
Uses the :opt:`url_style` and :opt:`url_color` settings for the underline style.
|
||||
''')
|
||||
|
||||
|
||||
opt('copy_on_select', 'no',
|
||||
option_type='copy_on_select',
|
||||
long_text='''
|
||||
|
|
|
|||
8
kitty/options/parse.py
generated
8
kitty/options/parse.py
generated
|
|
@ -1308,6 +1308,14 @@ class Parser:
|
|||
|
||||
choices_for_undercurl_style = frozenset(('thin-sparse', 'thin-dense', 'thick-sparse', 'thick-dense'))
|
||||
|
||||
def underline_hyperlinks(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
||||
val = val.lower()
|
||||
if val not in self.choices_for_underline_hyperlinks:
|
||||
raise ValueError(f"The value {val} is not a valid choice for underline_hyperlinks")
|
||||
ans["underline_hyperlinks"] = val
|
||||
|
||||
choices_for_underline_hyperlinks = frozenset(('hover', 'always', 'never'))
|
||||
|
||||
def update_check_interval(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
||||
ans['update_check_interval'] = float(val)
|
||||
|
||||
|
|
|
|||
15
kitty/options/to-c-generated.h
generated
15
kitty/options/to-c-generated.h
generated
|
|
@ -291,6 +291,19 @@ convert_from_opts_show_hyperlink_targets(PyObject *py_opts, Options *opts) {
|
|||
Py_DECREF(ret);
|
||||
}
|
||||
|
||||
static void
|
||||
convert_from_python_underline_hyperlinks(PyObject *val, Options *opts) {
|
||||
opts->underline_hyperlinks = underline_hyperlinks(val);
|
||||
}
|
||||
|
||||
static void
|
||||
convert_from_opts_underline_hyperlinks(PyObject *py_opts, Options *opts) {
|
||||
PyObject *ret = PyObject_GetAttrString(py_opts, "underline_hyperlinks");
|
||||
if (ret == NULL) return;
|
||||
convert_from_python_underline_hyperlinks(ret, opts);
|
||||
Py_DECREF(ret);
|
||||
}
|
||||
|
||||
static void
|
||||
convert_from_python_select_by_word_characters(PyObject *val, Options *opts) {
|
||||
select_by_word_characters(val, opts);
|
||||
|
|
@ -1143,6 +1156,8 @@ convert_opts_from_python_opts(PyObject *py_opts, Options *opts) {
|
|||
if (PyErr_Occurred()) return false;
|
||||
convert_from_opts_show_hyperlink_targets(py_opts, opts);
|
||||
if (PyErr_Occurred()) return false;
|
||||
convert_from_opts_underline_hyperlinks(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_select_by_word_characters_forward(py_opts, opts);
|
||||
|
|
|
|||
|
|
@ -57,6 +57,16 @@ window_title_in(PyObject *title_in) {
|
|||
return ALL;
|
||||
}
|
||||
|
||||
static UnderlineHyperlinks
|
||||
underline_hyperlinks(PyObject *x) {
|
||||
const char *in = PyUnicode_AsUTF8(x);
|
||||
switch(in[0]) {
|
||||
case 'a': return UNDERLINE_ALWAYS;
|
||||
case 'n': return UNDERLINE_NEVER;
|
||||
default : return UNDERLINE_ON_HOVER;
|
||||
}
|
||||
}
|
||||
|
||||
static BackgroundImageLayout
|
||||
bglayout(PyObject *layout_name) {
|
||||
const char *name = PyUnicode_AsUTF8(layout_name);
|
||||
|
|
|
|||
3
kitty/options/types.py
generated
3
kitty/options/types.py
generated
|
|
@ -29,6 +29,7 @@ choices_for_tab_bar_style = typing.Literal['fade', 'hidden', 'powerline', 'separ
|
|||
choices_for_tab_powerline_style = typing.Literal['angled', 'round', 'slanted']
|
||||
choices_for_tab_switch_strategy = typing.Literal['last', 'left', 'previous', 'right']
|
||||
choices_for_undercurl_style = typing.Literal['thin-sparse', 'thin-dense', 'thick-sparse', 'thick-dense']
|
||||
choices_for_underline_hyperlinks = typing.Literal['hover', 'always', 'never']
|
||||
choices_for_window_logo_position = typing.Literal['top-left', 'top', 'top-right', 'left', 'center', 'right', 'bottom-left', 'bottom', 'bottom-right']
|
||||
|
||||
option_names = ( # {{{
|
||||
|
|
@ -432,6 +433,7 @@ option_names = ( # {{{
|
|||
'text_fg_override_threshold',
|
||||
'touch_scroll_multiplier',
|
||||
'undercurl_style',
|
||||
'underline_hyperlinks',
|
||||
'update_check_interval',
|
||||
'url_color',
|
||||
'url_excluded_characters',
|
||||
|
|
@ -588,6 +590,7 @@ class Options:
|
|||
text_fg_override_threshold: float = 0.0
|
||||
touch_scroll_multiplier: float = 1.0
|
||||
undercurl_style: choices_for_undercurl_style = 'thin-sparse'
|
||||
underline_hyperlinks: choices_for_underline_hyperlinks = 'hover'
|
||||
update_check_interval: float = 24.0
|
||||
url_color: Color = Color(0, 135, 189)
|
||||
url_excluded_characters: str = ''
|
||||
|
|
|
|||
|
|
@ -2809,7 +2809,9 @@ screen_apply_selection(Screen *self, void *address, size_t size) {
|
|||
}
|
||||
self->selections.last_rendered_count = self->selections.count;
|
||||
for (size_t i = 0; i < self->url_ranges.count; i++) {
|
||||
apply_selection(self, address, self->url_ranges.items + i, 2);
|
||||
Selection *s = self->url_ranges.items + i;
|
||||
if (OPT(underline_hyperlinks) == UNDERLINE_NEVER && s->is_hyperlink) continue;
|
||||
apply_selection(self, address, s, 2);
|
||||
}
|
||||
self->url_ranges.last_rendered_count = self->url_ranges.count;
|
||||
}
|
||||
|
|
@ -3945,12 +3947,13 @@ screen_start_selection(Screen *self, index_type x, index_type y, bool in_left_ha
|
|||
}
|
||||
|
||||
static void
|
||||
add_url_range(Screen *self, index_type start_x, index_type start_y, index_type end_x, index_type end_y) {
|
||||
add_url_range(Screen *self, index_type start_x, index_type start_y, index_type end_x, index_type end_y, bool is_hyperlink) {
|
||||
#define A(attr, val) r->attr = val;
|
||||
ensure_space_for(&self->url_ranges, items, Selection, self->url_ranges.count + 8, capacity, 8, false);
|
||||
Selection *r = self->url_ranges.items + self->url_ranges.count++;
|
||||
memset(r, 0, sizeof(Selection));
|
||||
r->last_rendered.y = INT_MAX;
|
||||
r->is_hyperlink = is_hyperlink;
|
||||
A(start.x, start_x); A(end.x, end_x); A(start.y, start_y); A(end.y, end_y);
|
||||
A(start_scrolled_by, self->scrolled_by); A(end_scrolled_by, self->scrolled_by);
|
||||
A(start.in_left_half_of_cell, true);
|
||||
|
|
@ -3960,7 +3963,7 @@ add_url_range(Screen *self, index_type start_x, index_type start_y, index_type e
|
|||
void
|
||||
screen_mark_url(Screen *self, index_type start_x, index_type start_y, index_type end_x, index_type end_y) {
|
||||
self->url_ranges.count = 0;
|
||||
if (start_x || start_y || end_x || end_y) add_url_range(self, start_x, start_y, end_x, end_y);
|
||||
if (start_x || start_y || end_x || end_y) add_url_range(self, start_x, start_y, end_x, end_y, false);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
|
@ -3972,7 +3975,7 @@ mark_hyperlinks_in_line(Screen *self, Line *line, hyperlink_id_type id, index_ty
|
|||
bool has_hyperlink = line->cpu_cells[x].hyperlink_id == id;
|
||||
if (in_range) {
|
||||
if (!has_hyperlink) {
|
||||
add_url_range(self, start, y, x - 1, y);
|
||||
add_url_range(self, start, y, x - 1, y, true);
|
||||
in_range = false;
|
||||
start = 0;
|
||||
}
|
||||
|
|
@ -3983,7 +3986,7 @@ mark_hyperlinks_in_line(Screen *self, Line *line, hyperlink_id_type id, index_ty
|
|||
}
|
||||
}
|
||||
}
|
||||
if (in_range) add_url_range(self, start, y, self->columns - 1, y);
|
||||
if (in_range) add_url_range(self, start, y, self->columns - 1, y, true);
|
||||
return found;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ typedef struct {
|
|||
typedef struct {
|
||||
SelectionBoundary start, end, input_start, input_current;
|
||||
unsigned int start_scrolled_by, end_scrolled_by;
|
||||
bool rectangle_select, adjusting_start;
|
||||
bool rectangle_select, adjusting_start, is_hyperlink;
|
||||
IterationData last_rendered;
|
||||
int sort_y, sort_x;
|
||||
struct {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ typedef struct {
|
|||
} UrlPrefix;
|
||||
|
||||
typedef enum AdjustmentUnit { POINT = 0, PERCENT = 1, PIXEL = 2 } AdjustmentUnit;
|
||||
typedef enum UnderlineHyperlinks { UNDERLINE_ON_HOVER = 0, UNDERLINE_ALWAYS = 1, UNDERLINE_NEVER = 2 } UnderlineHyperlinks;
|
||||
|
||||
struct MenuItem {
|
||||
const char* *location;
|
||||
|
|
@ -95,6 +96,7 @@ typedef struct {
|
|||
float val; AdjustmentUnit unit;
|
||||
} underline_position, underline_thickness, strikethrough_position, strikethrough_thickness, cell_width, cell_height, baseline;
|
||||
bool show_hyperlink_targets;
|
||||
UnderlineHyperlinks underline_hyperlinks;
|
||||
int background_blur;
|
||||
long macos_titlebar_color;
|
||||
unsigned long wayland_titlebar_color;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue