mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-23 08:47:47 +02:00
Port click on URL code to C
This commit is contained in:
@@ -21,7 +21,7 @@ from .keys import (
|
|||||||
)
|
)
|
||||||
from .session import create_session
|
from .session import create_session
|
||||||
from .tabs import SpecialWindow, TabManager
|
from .tabs import SpecialWindow, TabManager
|
||||||
from .utils import get_primary_selection, safe_print, set_primary_selection
|
from .utils import get_primary_selection, safe_print, set_primary_selection, open_url
|
||||||
from .window import load_shader_programs
|
from .window import load_shader_programs
|
||||||
|
|
||||||
|
|
||||||
@@ -247,6 +247,10 @@ class Boss:
|
|||||||
k = get_key_map(w.screen)[GLFW_KEY_UP if upwards else GLFW_KEY_DOWN]
|
k = get_key_map(w.screen)[GLFW_KEY_UP if upwards else GLFW_KEY_DOWN]
|
||||||
w.write_to_child(k * amt)
|
w.write_to_child(k * amt)
|
||||||
|
|
||||||
|
def open_url(self, url):
|
||||||
|
if url:
|
||||||
|
open_url(url, self.opts.open_url_with)
|
||||||
|
|
||||||
def gui_close_window(self, window):
|
def gui_close_window(self, window):
|
||||||
window.destroy()
|
window.destroy()
|
||||||
for tab in self.tab_manager:
|
for tab in self.tab_manager:
|
||||||
|
|||||||
21
kitty/line.c
21
kitty/line.c
@@ -54,6 +54,8 @@ line_text_at(char_type ch, combining_type cc) {
|
|||||||
return ans;
|
return ans;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// URL detection {{{
|
||||||
|
|
||||||
static const char* url_prefixes[4] = {"https", "http", "file", "ftp"};
|
static const char* url_prefixes[4] = {"https", "http", "file", "ftp"};
|
||||||
static size_t url_prefix_lengths[sizeof(url_prefixes)/sizeof(url_prefixes[0])] = {0};
|
static size_t url_prefix_lengths[sizeof(url_prefixes)/sizeof(url_prefixes[0])] = {0};
|
||||||
typedef enum URL_PARSER_STATES {ANY, FIRST_SLASH, SECOND_SLASH} URL_PARSER_STATE;
|
typedef enum URL_PARSER_STATES {ANY, FIRST_SLASH, SECOND_SLASH} URL_PARSER_STATE;
|
||||||
@@ -137,12 +139,30 @@ line_url_start_at(Line *self, index_type x) {
|
|||||||
return self->xnum;
|
return self->xnum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
index_type
|
||||||
|
line_url_end_at(Line *self, index_type x) {
|
||||||
|
index_type ans = x;
|
||||||
|
if (x >= self->xnum || self->xnum <= MIN_URL_LEN + 3) return 0;
|
||||||
|
while (ans < self->xnum && is_url_char(self->cells[ans].ch & CHAR_MASK)) ans++;
|
||||||
|
ans--;
|
||||||
|
while (ans > x && can_strip_from_end_of_url(self->cells[ans].ch & CHAR_MASK)) ans--;
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
url_start_at(Line *self, PyObject *x) {
|
url_start_at(Line *self, PyObject *x) {
|
||||||
#define url_start_at_doc "url_start_at(x) -> Return the start cell number for a URL containing x or self->xnum if not found"
|
#define url_start_at_doc "url_start_at(x) -> Return the start cell number for a URL containing x or self->xnum if not found"
|
||||||
return PyLong_FromUnsignedLong((unsigned long)line_url_start_at(self, PyLong_AsUnsignedLong(x)));
|
return PyLong_FromUnsignedLong((unsigned long)line_url_start_at(self, PyLong_AsUnsignedLong(x)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
url_end_at(Line *self, PyObject *x) {
|
||||||
|
#define url_end_at_doc "url_end_at(x) -> Return the end cell number for a URL containing x or 0 if not found"
|
||||||
|
return PyLong_FromUnsignedLong((unsigned long)line_url_end_at(self, PyLong_AsUnsignedLong(x)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// }}}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
text_at(Line* self, Py_ssize_t xval) {
|
text_at(Line* self, Py_ssize_t xval) {
|
||||||
#define text_at_doc "[x] -> Return the text in the specified cell"
|
#define text_at_doc "[x] -> Return the text in the specified cell"
|
||||||
@@ -567,6 +587,7 @@ static PyMethodDef methods[] = {
|
|||||||
METHOD(is_continued, METH_NOARGS)
|
METHOD(is_continued, METH_NOARGS)
|
||||||
METHOD(width, METH_O)
|
METHOD(width, METH_O)
|
||||||
METHOD(url_start_at, METH_O)
|
METHOD(url_start_at, METH_O)
|
||||||
|
METHOD(url_end_at, METH_O)
|
||||||
|
|
||||||
{NULL} /* Sentinel */
|
{NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ void line_set_char(Line *, unsigned int , uint32_t , unsigned int , Cursor *);
|
|||||||
void line_right_shift(Line *, unsigned int , unsigned int );
|
void line_right_shift(Line *, unsigned int , unsigned int );
|
||||||
void line_add_combining_char(Line *, uint32_t , unsigned int );
|
void line_add_combining_char(Line *, uint32_t , unsigned int );
|
||||||
index_type line_url_start_at(Line *self, index_type x);
|
index_type line_url_start_at(Line *self, index_type x);
|
||||||
|
index_type line_url_end_at(Line *self, index_type x);
|
||||||
index_type line_as_ansi(Line *self, Py_UCS4 *buf, index_type buflen);
|
index_type line_as_ansi(Line *self, Py_UCS4 *buf, index_type buflen);
|
||||||
unsigned int line_length(Line *self);
|
unsigned int line_length(Line *self);
|
||||||
PyObject* unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc, char leading_char);
|
PyObject* unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc, char leading_char);
|
||||||
|
|||||||
@@ -216,6 +216,20 @@ HANDLER(add_click) {
|
|||||||
#undef N
|
#undef N
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
open_url(Window *w) {
|
||||||
|
Line *line = screen_visual_line(w->render_data.screen, w->mouse_cell_y);
|
||||||
|
if (line) {
|
||||||
|
index_type start = line_url_start_at(line, w->mouse_cell_x);
|
||||||
|
if (start < line->xnum) {
|
||||||
|
index_type end = line_url_end_at(line, w->mouse_cell_x);
|
||||||
|
if (end > start) {
|
||||||
|
call_boss(open_url, "N", unicode_in_range(line, start, end + 1, true, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
HANDLER(handle_button_event) {
|
HANDLER(handle_button_event) {
|
||||||
Tab *t = global_state.tabs + global_state.active_tab;
|
Tab *t = global_state.tabs + global_state.active_tab;
|
||||||
bool is_release = !global_state.mouse_button_pressed[button];
|
bool is_release = !global_state.mouse_button_pressed[button];
|
||||||
@@ -236,7 +250,7 @@ HANDLER(handle_button_event) {
|
|||||||
update_drag(true, w, is_release);
|
update_drag(true, w, is_release);
|
||||||
if (is_release) {
|
if (is_release) {
|
||||||
if (modifiers == (int)OPT(open_url_modifiers)) {
|
if (modifiers == (int)OPT(open_url_modifiers)) {
|
||||||
// TODO: click_url
|
open_url(w);
|
||||||
} else {
|
} else {
|
||||||
if (is_release) add_click(w, button, modifiers, window_idx);
|
if (is_release) add_click(w, button, modifiers, window_idx);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,3 +30,12 @@ normalize(uint32_t ch, uint32_t cc1, uint32_t cc2) {
|
|||||||
if (ans && cc2) ans = uc_composition(ans, cc2);
|
if (ans && cc2) ans = uc_composition(ans, cc2);
|
||||||
return ans;
|
return ans;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
can_strip_from_end_of_url(uint32_t ch) {
|
||||||
|
// remove trailing punctuation
|
||||||
|
return (
|
||||||
|
(uc_is_general_category_withtable(ch, UC_CATEGORY_MASK_P) && ch != '/') ||
|
||||||
|
ch == '>'
|
||||||
|
) ? true : false;
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
# vim:fileencoding=utf-8
|
# vim:fileencoding=utf-8
|
||||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
import re
|
|
||||||
import sys
|
import sys
|
||||||
import weakref
|
import weakref
|
||||||
from collections import deque
|
from collections import deque
|
||||||
@@ -23,9 +22,7 @@ from .fast_data_types import (
|
|||||||
)
|
)
|
||||||
from .rgb import to_color
|
from .rgb import to_color
|
||||||
from .terminfo import get_capabilities
|
from .terminfo import get_capabilities
|
||||||
from .utils import (
|
from .utils import color_as_int, load_shaders, parse_color_set, sanitize_title
|
||||||
color_as_int, load_shaders, open_url, parse_color_set, sanitize_title
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class DynamicColor(Enum):
|
class DynamicColor(Enum):
|
||||||
@@ -233,30 +230,6 @@ class Window:
|
|||||||
def text_for_selection(self):
|
def text_for_selection(self):
|
||||||
return ''.join(self.screen.text_for_selection())
|
return ''.join(self.screen.text_for_selection())
|
||||||
|
|
||||||
# mouse handling {{{
|
|
||||||
def click_url(self, x, y):
|
|
||||||
x, y = self.cell_for_pos(x, y)
|
|
||||||
if x is not None:
|
|
||||||
l = self.screen.visual_line(y)
|
|
||||||
if l is not None:
|
|
||||||
text = str(l)
|
|
||||||
for m in self.url_pat.finditer(text):
|
|
||||||
if m.start() <= x < m.end():
|
|
||||||
url = ''.join(l[i] for i in range(*m.span())).rstrip('.')
|
|
||||||
# Remove trailing "] and similar
|
|
||||||
url = re.sub(r'''["'][)}\]]$''', '', url)
|
|
||||||
# Remove closing trailing character if it is matched by it's
|
|
||||||
# corresponding opening character before the url
|
|
||||||
if m.start() > 0:
|
|
||||||
before = l[m.start() - 1]
|
|
||||||
closing = {'(': ')', '[': ']', '{': '}', '<': '>', '"': '"', "'": "'", '`': '`', '|': '|', ':': ':'}.get(before)
|
|
||||||
if closing is not None and url.endswith(closing):
|
|
||||||
url = url[:-1]
|
|
||||||
if url:
|
|
||||||
open_url(url, self.opts.open_url_with)
|
|
||||||
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
def destroy(self):
|
def destroy(self):
|
||||||
if self.vao_id is not None:
|
if self.vao_id is not None:
|
||||||
remove_vao(self.vao_id)
|
remove_vao(self.vao_id)
|
||||||
|
|||||||
@@ -222,6 +222,14 @@ class TestDataTypes(BaseTest):
|
|||||||
l.set_text(t, 0, len(t), C())
|
l.set_text(t, 0, len(t), C())
|
||||||
return l
|
return l
|
||||||
|
|
||||||
|
for trail in '.,]>)\\':
|
||||||
|
l = create("http://xyz.com" + trail)
|
||||||
|
self.ae(l.url_end_at(0), len(l) - 2)
|
||||||
|
l = create("ftp://abc/")
|
||||||
|
self.ae(l.url_end_at(0), len(l) - 1)
|
||||||
|
l = create("http://-abcd] ")
|
||||||
|
self.ae(l.url_end_at(0), len(l) - 3)
|
||||||
|
|
||||||
def lspace_test(n, scheme='http'):
|
def lspace_test(n, scheme='http'):
|
||||||
l = create(' ' * n + scheme + '://acme.com')
|
l = create(' ' * n + scheme + '://acme.com')
|
||||||
for i in range(0, n):
|
for i in range(0, n):
|
||||||
|
|||||||
Reference in New Issue
Block a user