mirror of
https://github.com/wez/wezterm.git
synced 2024-11-26 08:25:50 +03:00
improve default hyperlink_rules. add default_hyperlink_rules()
refs: https://github.com/wez/wezterm/issues/928
This commit is contained in:
parent
c093b8861a
commit
6a9dfc409d
@ -1438,14 +1438,18 @@ fn default_initial_cols() -> u16 {
|
|||||||
80
|
80
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_hyperlink_rules() -> Vec<hyperlink::Rule> {
|
pub fn default_hyperlink_rules() -> Vec<hyperlink::Rule> {
|
||||||
vec![
|
vec![
|
||||||
// URL with a protocol
|
// First handle URLs wrapped with punctuation (i.e. brackets)
|
||||||
hyperlink::Rule::new(r"\b\w+://[\w.-]+\.[a-z]{2,15}\S*\b", "$0").unwrap(),
|
// e.g. [http://foo] (http://foo) <http://foo>
|
||||||
|
hyperlink::Rule::with_highlight(r"\((\w+://\S+)\)", "$1", 1).unwrap(),
|
||||||
|
hyperlink::Rule::with_highlight(r"\[(\w+://\S+)\]", "$1", 1).unwrap(),
|
||||||
|
hyperlink::Rule::with_highlight(r"<(\w+://\S+)>", "$1", 1).unwrap(),
|
||||||
|
// Then handle URLs not wrapped in brackets
|
||||||
|
// and include terminating ), / or - characters, if any
|
||||||
|
hyperlink::Rule::new(r"\b\w+://\S+[)/a-zA-Z0-9-]+", "$0").unwrap(),
|
||||||
// implicit mailto link
|
// implicit mailto link
|
||||||
hyperlink::Rule::new(r"\b\w+@[\w-]+(\.[\w-]+)+\b", "mailto:$0").unwrap(),
|
hyperlink::Rule::new(r"\b\w+@[\w-]+(\.[\w-]+)+\b", "mailto:$0").unwrap(),
|
||||||
// file://
|
|
||||||
hyperlink::Rule::new(r"\bfile://\S*\b", "$0").unwrap(),
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,6 +353,14 @@ end
|
|||||||
wezterm_mod.set("shell_quote_arg", lua.create_function(shell_quote_arg)?)?;
|
wezterm_mod.set("shell_quote_arg", lua.create_function(shell_quote_arg)?)?;
|
||||||
wezterm_mod.set("shell_split", lua.create_function(shell_split)?)?;
|
wezterm_mod.set("shell_split", lua.create_function(shell_split)?)?;
|
||||||
|
|
||||||
|
wezterm_mod.set(
|
||||||
|
"default_hyperlink_rules",
|
||||||
|
lua.create_function(move |lua, ()| {
|
||||||
|
let rules = crate::config::default_hyperlink_rules();
|
||||||
|
Ok(to_lua(lua, rules))
|
||||||
|
})?,
|
||||||
|
)?;
|
||||||
|
|
||||||
// Define our own os.getenv function that knows how to resolve current
|
// Define our own os.getenv function that knows how to resolve current
|
||||||
// environment values from eg: the registry on Windows, or for
|
// environment values from eg: the registry on Windows, or for
|
||||||
// the current SHELL value on unix, even if the user has changed
|
// the current SHELL value on unix, even if the user has changed
|
||||||
|
@ -79,6 +79,10 @@ As features stabilize some brief notes about them will accumulate here.
|
|||||||
[#1485](https://github.com/wez/wezterm/issues/1485)
|
[#1485](https://github.com/wez/wezterm/issues/1485)
|
||||||
* `wezterm sssh` now supports `%l` and `%L` tokens in config files.
|
* `wezterm sssh` now supports `%l` and `%L` tokens in config files.
|
||||||
[#3176](https://github.com/wez/wezterm/issues/3176)
|
[#3176](https://github.com/wez/wezterm/issues/3176)
|
||||||
|
* [hyperlink_rules](config/lua/config/hyperlink_rules.md) now support
|
||||||
|
specifying which capture group should be highlighted.
|
||||||
|
* [wezterm.default_hyperlink_rules](config/lua/wezterm/default_hyperlink_rules.md)
|
||||||
|
function makes it easier to extend the default set of hyperlink rules.
|
||||||
|
|
||||||
#### Fixed
|
#### Fixed
|
||||||
* X11: hanging or killing the IME could hang wezterm
|
* X11: hanging or killing the IME could hang wezterm
|
||||||
@ -131,6 +135,9 @@ As features stabilize some brief notes about them will accumulate here.
|
|||||||
[#3250](https://github.com/wez/wezterm/issues/3250)
|
[#3250](https://github.com/wez/wezterm/issues/3250)
|
||||||
* Config was not applied to non-zoomed panes when config was reloaded
|
* Config was not applied to non-zoomed panes when config was reloaded
|
||||||
[#3259](https://github.com/wez/wezterm/issues/3259)
|
[#3259](https://github.com/wez/wezterm/issues/3259)
|
||||||
|
* Default [hyperlink_rules](config/lua/config/hyperlink_rules.md) now match
|
||||||
|
URLs with port numbers
|
||||||
|
[#928](https://github.com/wez/wezterm/issues/928)
|
||||||
|
|
||||||
#### Changed
|
#### Changed
|
||||||
* `CTRL-SHIFT-P` now activates the new [command
|
* `CTRL-SHIFT-P` now activates the new [command
|
||||||
|
@ -3,5 +3,109 @@
|
|||||||
Defines rules to match text from the terminal output and generate
|
Defines rules to match text from the terminal output and generate
|
||||||
clickable links.
|
clickable links.
|
||||||
|
|
||||||
See [Hyperlinks](../../../hyperlinks.md) for more information and
|
The value is a list of rule entries. Each entry has the following fields:
|
||||||
examples.
|
|
||||||
|
* `regex` - the regular expression to match
|
||||||
|
* `format` - Controls what will be used to form the link. The string
|
||||||
|
can use placeholders like `$0`, `$1`, `$2` etc. that will be replaced
|
||||||
|
with that numbered capture group. So, `$0` will take the entire
|
||||||
|
region of text matched by the whole regex, while `$1` matches out
|
||||||
|
the first capture group. In the example below, `mailto:$0` is
|
||||||
|
used to prefix a protocol to the text to make it into an URL.
|
||||||
|
* `highlight` - (*Since: nightly builds only*) specifies the range
|
||||||
|
of the matched text that should be highlighted/underlined when
|
||||||
|
the mouse hovers over the link. The default is `0`, highlighting
|
||||||
|
the entire region of text matched by the regex.
|
||||||
|
|
||||||
|
Assigning `hyperlink_rules` overrides the built-in default rules.
|
||||||
|
|
||||||
|
The default value for `hyperlink_rules` can be retrieved using
|
||||||
|
[wezterm.default_hyperlink_rules()](../wezterm/default_hyperlink_rules.md),
|
||||||
|
and is shown below:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
return {
|
||||||
|
hyperlink_rules = {
|
||||||
|
-- Matches: a URL in parens: (URL)
|
||||||
|
{
|
||||||
|
regex = '\\((\\w+://\\S+)\\)',
|
||||||
|
format = '$1',
|
||||||
|
highlight = 1,
|
||||||
|
},
|
||||||
|
-- Matches: a URL in brackets: [URL]
|
||||||
|
{
|
||||||
|
regex = '\\[(\\w+://\\S+)\\]',
|
||||||
|
format = '$1',
|
||||||
|
highlight = 1,
|
||||||
|
},
|
||||||
|
-- Matches: a URL in curly braces: {URL}
|
||||||
|
{
|
||||||
|
regex = '\\{(\\w+://\\S+)\\}',
|
||||||
|
format = '$1',
|
||||||
|
highlight = 1,
|
||||||
|
},
|
||||||
|
-- Matches: a URL in angle brackets: <URL>
|
||||||
|
{
|
||||||
|
regex = '<(\\w+://\\S+)>',
|
||||||
|
format = '$1',
|
||||||
|
highlight = 1,
|
||||||
|
},
|
||||||
|
-- Then handle URLs not wrapped in brackets
|
||||||
|
{
|
||||||
|
regex = '\\b\\w+://\\S+[)/a-zA-Z0-9-]+',
|
||||||
|
format = '$0',
|
||||||
|
},
|
||||||
|
-- implicit mailto link
|
||||||
|
{
|
||||||
|
regex = '\\b\\w+@[\\w-]+(\\.[\\w-]+)+\\b',
|
||||||
|
format = 'mailto:$0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
In quoted Lua string literals the backslash character must be
|
||||||
|
quoted even if the following character isn't meaningful to Lua
|
||||||
|
when quoted by a backslash. That means that you'll always want to
|
||||||
|
double it up as `\\` when using it in a regex string.
|
||||||
|
|
||||||
|
Alternatively, you can use the alternative string literal
|
||||||
|
syntax; the following two examples are equivalent:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
regex = [[\b[tT](\d+)\b]]
|
||||||
|
```
|
||||||
|
|
||||||
|
```lua
|
||||||
|
regex = '\\b[tT](\\d+)\\b'
|
||||||
|
```
|
||||||
|
|
||||||
|
Some other examples include:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local wezterm = require 'wezterm'
|
||||||
|
|
||||||
|
-- Use the defaults as a base
|
||||||
|
local hyperlink_rules = wezterm.default_hyperlink_rules()
|
||||||
|
|
||||||
|
-- make task numbers clickable
|
||||||
|
-- the first matched regex group is captured in $1.
|
||||||
|
table.insert(hyperlink_rules, {
|
||||||
|
regex = [[\b[tt](\d+)\b]],
|
||||||
|
format = 'https://example.com/tasks/?t=$1',
|
||||||
|
})
|
||||||
|
|
||||||
|
-- make username/project paths clickable. this implies paths like the following are for github.
|
||||||
|
-- ( "nvim-treesitter/nvim-treesitter" | wbthomason/packer.nvim | wez/wezterm | "wez/wezterm.git" )
|
||||||
|
-- as long as a full url hyperlink regex exists above this it should not match a full url to
|
||||||
|
-- github or gitlab / bitbucket (i.e. https://gitlab.com/user/project.git is still a whole clickable url)
|
||||||
|
table.insert(hyperlink_rules, {
|
||||||
|
regex = [[["]?([\w\d]{1}[-\w\d]+)(/){1}([-\w\d\.]+)["]?]],
|
||||||
|
format = 'https://www.github.com/$1/$3',
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
hyperlink_rules = hyperlink_rules,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
6
docs/config/lua/wezterm/default_hyperlink_rules.md
Normal file
6
docs/config/lua/wezterm/default_hyperlink_rules.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# `wezterm.default_hyperlink_rules()`
|
||||||
|
|
||||||
|
*Since: nightly builds only*
|
||||||
|
|
||||||
|
Returns the compiled-in default values for [hyperlink_rules](../config/hyperlink_rules.md).
|
||||||
|
|
@ -5,66 +5,42 @@ wezterm has support for both implicit and explicit hyperlinks.
|
|||||||
Implicit hyperlinks are produced by running a series of rules over the output
|
Implicit hyperlinks are produced by running a series of rules over the output
|
||||||
displayed in the terminal to produce a hyperlink. There is a default rule
|
displayed in the terminal to produce a hyperlink. There is a default rule
|
||||||
to match URLs and make them clickable, but you can also specify your own rules
|
to match URLs and make them clickable, but you can also specify your own rules
|
||||||
to make your own links. As an example, at my place of work many of our internal
|
to make your own links.
|
||||||
tools use `T123` to indicate task number 123 in our internal task tracking system.
|
|
||||||
It is desirable to make this clickable, and that can be done with the following
|
As an example, at my place of work many of our internal tools use `T123` to
|
||||||
configuration in your `~/.wezterm.lua`:
|
indicate task number 123 in our internal task tracking system. It is desirable
|
||||||
|
to make this clickable, and that can be done with the following configuration
|
||||||
|
in your `~/.wezterm.lua`:
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
|
local wezterm = require 'wezterm'
|
||||||
|
|
||||||
|
-- Use the defaults as a base
|
||||||
|
local hyperlink_rules = wezterm.default_hyperlink_rules()
|
||||||
|
|
||||||
|
-- make task numbers clickable
|
||||||
|
-- the first matched regex group is captured in $1.
|
||||||
|
table.insert(hyperlink_rules, {
|
||||||
|
regex = [[\b[tt](\d+)\b]],
|
||||||
|
format = 'https://example.com/tasks/?t=$1',
|
||||||
|
})
|
||||||
|
|
||||||
|
-- make username/project paths clickable. this implies paths like the following are for github.
|
||||||
|
-- ( "nvim-treesitter/nvim-treesitter" | wbthomason/packer.nvim | wez/wezterm | "wez/wezterm.git" )
|
||||||
|
-- as long as a full url hyperlink regex exists above this it should not match a full url to
|
||||||
|
-- github or gitlab / bitbucket (i.e. https://gitlab.com/user/project.git is still a whole clickable url)
|
||||||
|
table.insert(hyperlink_rules, {
|
||||||
|
regex = [[["]?([\w\d]{1}[-\w\d]+)(/){1}([-\w\d\.]+)["]?]],
|
||||||
|
format = 'https://www.github.com/$1/$3',
|
||||||
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
hyperlink_rules = {
|
hyperlink_rules = hyperlink_rules,
|
||||||
-- Linkify things that look like URLs and the host has a TLD name.
|
|
||||||
-- Compiled-in default. Used if you don't specify any hyperlink_rules.
|
|
||||||
{
|
|
||||||
regex = '\\b\\w+://[\\w.-]+\\.[a-z]{2,15}\\S*\\b',
|
|
||||||
format = '$0',
|
|
||||||
},
|
|
||||||
|
|
||||||
-- linkify email addresses
|
|
||||||
-- Compiled-in default. Used if you don't specify any hyperlink_rules.
|
|
||||||
{
|
|
||||||
regex = [[\b\w+@[\w-]+(\.[\w-]+)+\b]],
|
|
||||||
format = 'mailto:$0',
|
|
||||||
},
|
|
||||||
|
|
||||||
-- file:// URI
|
|
||||||
-- Compiled-in default. Used if you don't specify any hyperlink_rules.
|
|
||||||
{
|
|
||||||
regex = [[\bfile://\S*\b]],
|
|
||||||
format = '$0',
|
|
||||||
},
|
|
||||||
|
|
||||||
-- Linkify things that look like URLs with numeric addresses as hosts.
|
|
||||||
-- E.g. http://127.0.0.1:8000 for a local development server,
|
|
||||||
-- or http://192.168.1.1 for the web interface of many routers.
|
|
||||||
{
|
|
||||||
regex = [[\b\w+://(?:[\d]{1,3}\.){3}[\d]{1,3}\S*\b]],
|
|
||||||
format = '$0',
|
|
||||||
},
|
|
||||||
|
|
||||||
-- Make task numbers clickable
|
|
||||||
-- The first matched regex group is captured in $1.
|
|
||||||
{
|
|
||||||
regex = [[\b[tT](\d+)\b]],
|
|
||||||
format = 'https://example.com/tasks/?t=$1',
|
|
||||||
},
|
|
||||||
|
|
||||||
-- Make username/project paths clickable. This implies paths like the following are for GitHub.
|
|
||||||
-- ( "nvim-treesitter/nvim-treesitter" | wbthomason/packer.nvim | wez/wezterm | "wez/wezterm.git" )
|
|
||||||
-- As long as a full URL hyperlink regex exists above this it should not match a full URL to
|
|
||||||
-- GitHub or GitLab / BitBucket (i.e. https://gitlab.com/user/project.git is still a whole clickable URL)
|
|
||||||
{
|
|
||||||
regex = [[["]?([\w\d]{1}[-\w\d]+)(/){1}([-\w\d\.]+)["]?]],
|
|
||||||
format = 'https://www.github.com/$1/$3',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that it is generally convenient to use literal strings (`[[...]]`)
|
See also [hyperlink_rules](config/lua/config/hyperlink_rules.md) and
|
||||||
when declaring your hyperlink rules, so you won't have to escape
|
[default_hyperlink_rules](config/lua/wezterm/default_hyperlink_rules.md).
|
||||||
backslashes. In the example above, all cases except the first use
|
|
||||||
literal strings for their regular expressions.
|
|
||||||
|
|
||||||
|
|
||||||
### Explicit Hyperlinks
|
### Explicit Hyperlinks
|
||||||
|
@ -160,6 +160,10 @@ pub struct Rule {
|
|||||||
/// with ambiguous replacement of `$11` vs `$1` in the case of
|
/// with ambiguous replacement of `$11` vs `$1` in the case of
|
||||||
/// more complex regexes.
|
/// more complex regexes.
|
||||||
pub format: String,
|
pub format: String,
|
||||||
|
|
||||||
|
/// Which capture to highlight
|
||||||
|
#[dynamic(default)]
|
||||||
|
pub highlight: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RegexWrap(Regex);
|
struct RegexWrap(Regex);
|
||||||
@ -221,6 +225,7 @@ pub struct RuleMatch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// An internal intermediate match result
|
/// An internal intermediate match result
|
||||||
|
#[derive(Debug)]
|
||||||
struct Match<'t> {
|
struct Match<'t> {
|
||||||
rule: &'t Rule,
|
rule: &'t Rule,
|
||||||
captures: Captures<'t>,
|
captures: Captures<'t>,
|
||||||
@ -229,16 +234,20 @@ struct Match<'t> {
|
|||||||
impl<'t> Match<'t> {
|
impl<'t> Match<'t> {
|
||||||
/// Returns the length of the matched text in bytes (not cells!)
|
/// Returns the length of the matched text in bytes (not cells!)
|
||||||
fn len(&self) -> usize {
|
fn len(&self) -> usize {
|
||||||
let c0 = self.captures.get(0).unwrap();
|
let c0 = self.highlight().unwrap();
|
||||||
c0.end() - c0.start()
|
c0.end() - c0.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the span of the matched text, measured in bytes (not cells!)
|
/// Returns the span of the matched text, measured in bytes (not cells!)
|
||||||
fn range(&self) -> Range<usize> {
|
fn range(&self) -> Range<usize> {
|
||||||
let c0 = self.captures.get(0).unwrap();
|
let c0 = self.highlight().unwrap();
|
||||||
c0.start()..c0.end()
|
c0.start()..c0.end()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn highlight(&self) -> Option<regex::Match> {
|
||||||
|
self.captures.get(self.rule.highlight)
|
||||||
|
}
|
||||||
|
|
||||||
/// Expand replacements in the format string to yield the URL
|
/// Expand replacements in the format string to yield the URL
|
||||||
/// The replacement is as described on Rule::format.
|
/// The replacement is as described on Rule::format.
|
||||||
fn expand(&self) -> String {
|
fn expand(&self) -> String {
|
||||||
@ -260,9 +269,14 @@ impl<'t> Match<'t> {
|
|||||||
impl Rule {
|
impl Rule {
|
||||||
/// Construct a new rule. It may fail if the regex is invalid.
|
/// Construct a new rule. It may fail if the regex is invalid.
|
||||||
pub fn new(regex: &str, format: &str) -> Result<Self> {
|
pub fn new(regex: &str, format: &str) -> Result<Self> {
|
||||||
|
Self::with_highlight(regex, format, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_highlight(regex: &str, format: &str, highlight: usize) -> Result<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
regex: Regex::new(regex)?,
|
regex: Regex::new(regex)?,
|
||||||
format: format.to_owned(),
|
format: format.to_owned(),
|
||||||
|
highlight,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,7 +286,10 @@ impl Rule {
|
|||||||
let mut matches = Vec::new();
|
let mut matches = Vec::new();
|
||||||
for rule in rules.iter() {
|
for rule in rules.iter() {
|
||||||
for captures in rule.regex.captures_iter(line) {
|
for captures in rule.regex.captures_iter(line) {
|
||||||
matches.push(Match { rule, captures });
|
let m = Match { rule, captures };
|
||||||
|
if m.highlight().is_some() {
|
||||||
|
matches.push(m);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Sort the matches by descending match length.
|
// Sort the matches by descending match length.
|
||||||
|
Loading…
Reference in New Issue
Block a user