1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-26 06:42:12 +03:00

use termwiz::Hyperlink

This commit is contained in:
Wez Furlong 2018-08-04 00:26:24 -07:00
parent f37de9cbe5
commit 1ec83706e7
6 changed files with 31 additions and 119 deletions

View File

@ -124,10 +124,10 @@ impl<'a> term::TerminalHost for TabHost<'a> {
fn click_link(&mut self, link: &Rc<Hyperlink>) {
// TODO: make this configurable
let mut cmd = Command::new("xdg-open");
cmd.arg(&link.url);
cmd.arg(&link.uri());
match cmd.spawn() {
Ok(_) => {}
Err(err) => eprintln!("failed to spawn xdg-open {}: {:?}", link.url, err),
Err(err) => eprintln!("failed to spawn xdg-open {}: {:?}", link.uri(), err),
}
}

View File

@ -12,60 +12,7 @@ use std::collections::HashMap;
use std::ops::Range;
use std::rc::Rc;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Hyperlink {
/// The target
pub url: String,
/// The identifier. This can be used by the render layer to determine
/// which cells to underline on hover. This is for the usecase where
/// an application has drawn windows in the window and the URL has
/// wrapped lines within such a window.
pub id: String,
/// If the link was produced by an implicit or matching rule,
/// this field will be set to true.
pub implicit: bool,
}
impl Hyperlink {
pub fn new(url: &str, params: &HashMap<&str, &str>) -> Self {
let id = params.get("id").unwrap_or(&"");
Self {
url: url.into(),
id: (**id).into(),
implicit: false,
}
}
pub fn with_id(url: &str, id: &str) -> Self {
Self {
url: url.into(),
id: id.into(),
implicit: false,
}
}
}
/// The spec says that the escape sequence is of the form:
/// OSC 8 ; params ; URI BEL|ST
/// params is an optional list of key=value assignments,
/// separated by the : character. Example: id=xyz123:foo=bar:baz=quux.
/// This function parses such a string and returns the mapping
/// of key to value. Malformed input causes subsequent key/value pairs
/// to be skipped, returning the data successfully parsed out so far.
pub fn parse_link_params(params: &str) -> HashMap<&str, &str> {
let mut map = HashMap::new();
for kv in params.split(':') {
let mut iter = kv.splitn(2, '=');
let key = iter.next();
let value = iter.next();
match (key, value) {
(Some(key), Some(value)) => map.insert(key, value),
_ => break,
};
}
map
}
pub use termwiz::escape::osc::Hyperlink;
/// In addition to handling explicit escape sequences to enable
/// hyperlinks, we also support defining rules that match text
@ -174,11 +121,10 @@ impl Rule {
.into_iter()
.map(|m| {
let url = m.expand();
let link = Rc::new(Hyperlink {
let link = Rc::new(Hyperlink::new_with_params(
url,
id: "".to_owned(),
implicit: true,
});
hashmap!{"implicit".into() => "1".into()},
));
RuleMatch {
link,
range: m.range(),
@ -203,11 +149,10 @@ mod test {
Rule::match_hyperlinks(" http://example.com", &rules),
vec![RuleMatch {
range: 2..20,
link: Rc::new(Hyperlink {
url: "http://example.com".to_owned(),
id: "".to_owned(),
implicit: true,
}),
link: Rc::new(Hyperlink::new_with_params(
"http://example.com",
hashmap!{"implicit".into()=>"1".into()},
)),
}]
);
@ -217,47 +162,19 @@ mod test {
// Longest match first
RuleMatch {
range: 18..34,
link: Rc::new(Hyperlink {
url: "mailto:woot@example.com".to_owned(),
id: "".to_owned(),
implicit: true,
}),
link: Rc::new(Hyperlink::new_with_params(
"mailto:woot@example.com",
hashmap!{"implicit".into()=>"1".into()},
)),
},
RuleMatch {
range: 2..17,
link: Rc::new(Hyperlink {
url: "mailto:foo@example.com".to_owned(),
id: "".to_owned(),
implicit: true,
}),
link: Rc::new(Hyperlink::new_with_params(
"mailto:foo@example.com",
hashmap!{"implicit".into()=>"1".into()},
)),
},
]
);
}
#[test]
fn parse_link() {
assert_eq!(parse_link_params(""), hashmap!{});
assert_eq!(parse_link_params("foo"), hashmap!{});
assert_eq!(
parse_link_params("foo=bar=baz"),
hashmap!{"foo" => "bar=baz"}
);
assert_eq!(parse_link_params("foo=bar"), hashmap!{"foo" => "bar"});
assert_eq!(
parse_link_params("id=1234:foo=bar"),
hashmap!{
"id" => "1234",
"foo" => "bar"
}
);
assert_eq!(
parse_link_params("id=1234:foo=bar:"),
hashmap!{
"id" => "1234",
"foo" => "bar"
}
);
}
}

View File

@ -3,7 +3,6 @@
extern crate bitflags;
#[macro_use]
extern crate failure;
#[cfg(test)]
#[macro_use]
extern crate maplit;
extern crate palette;

View File

@ -165,7 +165,7 @@ impl Line {
for mut cell in &mut self.cells {
let link = cell.attrs.hyperlink.take();
cell.attrs.hyperlink = match link.as_ref() {
Some(link) if link.implicit => None,
Some(link) if link.params().contains_key("implicit") => None,
Some(link) => Some(Rc::clone(link)),
None => None,
};

View File

@ -1,5 +1,4 @@
use super::*;
use std::collections::HashMap;
use std::fmt::Write;
use termwiz::escape::csi::{
Cursor, DecPrivateMode, DecPrivateModeCode, Device, Edit, EraseInDisplay, EraseInLine, Mode,
@ -1532,19 +1531,15 @@ impl<'a> Performer<'a> {
self.host.set_title(&title);
}
OperatingSystemCommand::SetIconName(_) => {}
OperatingSystemCommand::SetHyperlink(Some(link)) => {
let params: HashMap<_, _> = link
.params()
.iter()
.map(|(a, b)| (a.as_str(), b.as_str()))
.collect();
self.set_hyperlink(Some(Hyperlink::new(link.uri(), &params)));
}
OperatingSystemCommand::SetHyperlink(None) => {
self.set_hyperlink(None);
OperatingSystemCommand::SetHyperlink(link) => {
self.set_hyperlink(link);
}
OperatingSystemCommand::Unspecified(unspec) => {
eprintln!("Unhandled {:?}", unspec);
eprint!("Unhandled OSC ");
for item in unspec {
eprint!(" {}", String::from_utf8_lossy(&item));
}
eprintln!("");
}
OperatingSystemCommand::ClearSelection(_) => {

View File

@ -7,7 +7,7 @@ mod c1;
mod csi;
mod selection;
use termwiz::escape::csi::{Edit, EraseInDisplay, EraseInLine};
use termwiz::escape::CSI;
use termwiz::escape::{OperatingSystemCommand, CSI};
#[derive(Default, Debug)]
struct TestHost {
@ -100,7 +100,8 @@ impl TestTerm {
}
fn hyperlink(&mut self, link: &Rc<Hyperlink>) {
self.print(format!("\x1b]8;id={};{}\x1b\\", link.id, link.url));
let osc = OperatingSystemCommand::SetHyperlink(Some(link.as_ref().clone()));
self.print(format!("{}", osc));
}
fn hyperlink_off(&mut self) {
@ -465,7 +466,7 @@ fn test_scrollup() {
#[test]
fn test_hyperlinks() {
let mut term = TestTerm::new(3, 5, 0);
let link = Rc::new(Hyperlink::with_id("http://example.com", ""));
let link = Rc::new(Hyperlink::new("http://example.com"));
term.hyperlink(&link);
term.print("hello");
term.hyperlink_off();
@ -499,7 +500,7 @@ fn test_hyperlinks() {
Compare::TEXT | Compare::ATTRS,
);
let otherlink = Rc::new(Hyperlink::with_id("http://example.com/other", "w00t"));
let otherlink = Rc::new(Hyperlink::new_with_id("http://example.com/other", "w00t"));
// Switching link and turning it off
term.hyperlink(&otherlink);