diff --git a/Cargo.lock b/Cargo.lock index 38ab8044e968..21eef77ed418 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -529,6 +529,7 @@ dependencies = [ "helix-tui", "log", "once_cell", + "open", "serde", "serde_json", "slotmap", @@ -736,6 +737,16 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +[[package]] +name = "open" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4a3100141f1733ea40b53381b0ae3117330735ef22309a190ac57b9576ea716" +dependencies = [ + "pathdiff", + "windows-sys", +] + [[package]] name = "parking_lot" version = "0.12.1" @@ -759,6 +770,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + [[package]] name = "percent-encoding" version = "2.2.0" diff --git a/book/src/keymap.md b/book/src/keymap.md index 7fd146a6928c..7597c15b7f74 100644 --- a/book/src/keymap.md +++ b/book/src/keymap.md @@ -195,6 +195,7 @@ Jumps to various locations. | `g` | Go to line number `` else start of file | `goto_file_start` | | `e` | Go to the end of the file | `goto_last_line` | | `f` | Go to files in the selection | `goto_file` | +| `u` | Go to url in the selection | `goto_url` | | `h` | Go to the start of the line | `goto_line_start` | | `l` | Go to the end of the line | `goto_line_end` | | `s` | Go to first non-whitespace character of the line | `goto_first_nonwhitespace` | diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 93e82b84491b..076ca4d3cfb3 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -291,6 +291,7 @@ impl MappableCommand { goto_file, "Goto files in selection", goto_file_hsplit, "Goto files in selection (hsplit)", goto_file_vsplit, "Goto files in selection (vsplit)", + goto_url, "Goto url in selection", goto_reference, "Goto references", goto_window_top, "Goto window top", goto_window_center, "Goto window center", @@ -1052,6 +1053,36 @@ fn goto_file_impl(cx: &mut Context, action: Action) { } } +fn goto_url(cx: &mut Context) { + use helix_lsp::Url; + + let (view, doc) = current_ref!(cx.editor); + let selection = doc.selection(view.id).primary(); + let text = doc.text(); + + let text = if selection.to() - selection.from() == 1 { + let current_word = movement::move_next_long_word_start( + text.slice(..), + movement::move_prev_long_word_start(text.slice(..), selection, 1), + 1, + ); + text.slice(current_word.from()..current_word.to()) + .to_string() + } else { + text.slice(selection.from()..selection.to()).to_string() + }; + + match Url::parse(&text) { + Ok(url) => { + cx.editor.open_url(url); + } + Err(e) => { + cx.editor + .set_error(format!("Failed to parse url '{}': {}", text, e.to_string())); + } + } +} + fn extend_word_impl(cx: &mut Context, extend_fn: F) where F: Fn(RopeSlice, Range, usize) -> Range, diff --git a/helix-term/src/keymap/default.rs b/helix-term/src/keymap/default.rs index 6c327ee6bc47..15617f658a69 100644 --- a/helix-term/src/keymap/default.rs +++ b/helix-term/src/keymap/default.rs @@ -40,6 +40,7 @@ pub fn default() -> HashMap { "g" => goto_file_start, "e" => goto_last_line, "f" => goto_file, + "u" => goto_url, "h" => goto_line_start, "l" => goto_line_end, "s" => goto_first_nonwhitespace, diff --git a/helix-view/Cargo.toml b/helix-view/Cargo.toml index b96a537d089f..d312bfcedd20 100644 --- a/helix-view/Cargo.toml +++ b/helix-view/Cargo.toml @@ -43,6 +43,8 @@ log = "~0.4" which = "4.2" +open = "3" + [target.'cfg(windows)'.dependencies] clipboard-win = { version = "4.4", features = ["std"] } diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 60b3880c6e14..e11dcb9505c2 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -1079,6 +1079,12 @@ impl Editor { self._refresh(); } + pub fn open_url(&mut self, url: lsp::Url) { + if let Err(e) = open::that(url.as_str()) { + self.set_error(format!("Failed to open url '{}': {:?}", url.as_str(), e)); + } + } + pub fn close_document(&mut self, doc_id: DocumentId, force: bool) -> Result<(), CloseError> { let doc = match self.documents.get(&doc_id) { Some(doc) => doc,