1use alacritty_terminal::term::cell::{
2 Cell,
3 Flags,
4};
5use linkify::{
6 LinkFinder,
7 LinkKind,
8};
9
10thread_local! {
11 static FINDER: LinkFinder = {
12 let mut f = LinkFinder::new();
13 f.kinds(&[LinkKind::Url]);
14 f
15 };
16}
17
18pub(crate) fn link_ranges(row: &[Cell]) -> Vec<(usize, usize)> {
22 let mut ranges = Vec::new();
23
24 let mut run_start: Option<usize> = None;
25 for (col, cell) in row.iter().enumerate() {
26 if cell.flags.contains(Flags::WIDE_CHAR_SPACER) {
27 continue;
28 }
29 if cell.hyperlink().is_some() {
30 run_start.get_or_insert(col);
31 } else if let Some(start) = run_start.take() {
32 ranges.push((start, col));
33 }
34 }
35 if let Some(start) = run_start {
36 ranges.push((start, row.len()));
37 }
38
39 if row_has_url_marker(row) {
40 let (text, byte_to_col) = row_text(row);
41 FINDER.with(|f| {
42 for link in f.links(&text) {
43 ranges.push((byte_to_col[link.start()], byte_to_col[link.end() - 1] + 1));
44 }
45 });
46 }
47
48 ranges
49}
50
51pub(crate) fn url_at(row: &[Cell], col: usize) -> Option<String> {
53 if !row_has_url_marker(row) {
54 return None;
55 }
56 let (text, byte_to_col) = row_text(row);
57 FINDER.with(|f| {
58 f.links(&text).find_map(|link| {
59 let start = byte_to_col[link.start()];
60 let end = byte_to_col[link.end() - 1] + 1;
61 (col >= start && col < end).then(|| link.as_str().to_owned())
62 })
63 })
64}
65
66fn row_has_url_marker(row: &[Cell]) -> bool {
68 let (mut a, mut b) = ('\0', '\0');
69 for cell in row
70 .iter()
71 .filter(|c| !c.flags.contains(Flags::WIDE_CHAR_SPACER))
72 {
73 if a == ':' && b == '/' && cell.c == '/' {
74 return true;
75 }
76 a = b;
77 b = cell.c;
78 }
79 false
80}
81
82fn row_text(row: &[Cell]) -> (String, Vec<usize>) {
85 let mut text = String::with_capacity(row.len());
86 let mut byte_to_col = Vec::with_capacity(row.len());
87 for (col, cell) in row.iter().enumerate() {
88 if cell.flags.contains(Flags::WIDE_CHAR_SPACER) {
89 continue;
90 }
91 let c = match cell.c {
92 '\0' | '\t' => ' ',
93 c => c,
94 };
95 text.push(c);
96 byte_to_col.resize(text.len(), col);
97 }
98 (text, byte_to_col)
99}