Skip to content

Commit

Permalink
Cycle through opening links with webbrowser module, xdg-open or --run…
Browse files Browse the repository at this point in the history
… argument

Related to firecat53#51.
  • Loading branch information
firecat53 committed May 29, 2019
1 parent 2d10807 commit 728569a
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 17 deletions.
16 changes: 11 additions & 5 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ Relative to urlview, urlscan has the following additional features:
- Execute an arbitrary function (for example, copy URL to clipboard) instead of
opening URL in a browser.

- Use `l` to cycle through whether URLs are opened using the Python webbrowser
module (default), xdg-open (if installed) or opened by a function passed on
the command line with `--run`.

- Configure colors and keybindings via ~/.config/urlscan/config.json. Generate
default config file for editing by running `urlscan -g`. Cycle through
available palettes with `p`.
Expand Down Expand Up @@ -106,11 +110,12 @@ context from around the URLs in the curses browser, and the '-d' flag removes
duplicate URLs. Files can also be piped to urlscan using normal shell pipe
mechanisms: `cat <something> | urlscan` or `urlscan < <something>`

Instead of opening a web browser, the selected URL can be passed as the
argument to a command using `--run <command>`. Alternatively, the URL can be
piped to the command using `--run <command> --pipe`. Using --run with --pipe is
preferred if the command supports it, as it is marginally more secure and
tolerant of special characters in the URL.
Instead of opening a web browser, the selected URL can be passed as the argument
to a command using `--run "<command> {}"`. Note the use of `{}` in the command
string to denote the selected URL. Alternatively, the URL can be piped to the
command using `--run <command> --pipe`. Using --run with --pipe is preferred if
the command supports it, as it is marginally more secure and tolerant of special
characters in the URL.

Theming
-------
Expand Down Expand Up @@ -139,6 +144,7 @@ The follow actions are supported:
- `context` -- show/hide context (default: `c`)
- `down` -- cursor down (default: `j`)
- `help_menu` -- show/hide help menu (default: `F1`)
- `link_handler` -- cycle link handling (webbrowser, xdg-open or --run) (default: `l`)
- `open_url` -- open selected URL (default: `space` or `enter`)
- `palette` -- cycle through palettes (default: `p`)
- `quit` -- quit (default: `q` or `Q`)
Expand Down
3 changes: 3 additions & 0 deletions conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
URLINTERNALPATTERN = r'[{}()@\w/\\\-%?!&.=:;+,#~]'
URLTRAILINGPATTERN = r'[{}(@\w/\-%&=+#]'
HTTPURLPATTERN = (r'(?:(https?|file|ftps?)://' + URLINTERNALPATTERN + r'*' + URLTRAILINGPATTERN + r')')
18 changes: 13 additions & 5 deletions urlscan.1
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.\" Hey, EMACS: -*- nroff -*-

.TH URLSCAN 1 "April 4, 2019"
.TH URLSCAN 1 "May 29, 2019"

.SH NAME
urlscan \- browse the URLs in an email message from a terminal
Expand Down Expand Up @@ -52,10 +52,15 @@ for color options and allowed values.

\fB7.\fR \fBu\fR will unescape the highlighted URL if necessary.

\fB8.\fR Run a command with the selected URL as the argument or pipe the selected
URL to a command using the \fB--run\fR and \fB--pipe\fR arguments.
\fB8.\fR Run a command with the selected URL as the argument or pipe the
selected URL to a command using the \fB--run\fR and \fB--pipe\fR arguments.

\fB9.\fR \fBF1\fR shows the help menu.
\fB9.\fR Use \fBl\fR to cycle through whether URLs are opened using the Python
webbrowser module (default), xdg-open (if installed) or a function passed on the
command line with \fB--run\fR. The \fB--run\fR function will respect the value
of \fB--pipe\fR.

\fB10.\fR \fBF1\fR shows the help menu.

.SH OPTIONS
.TP
Expand All @@ -78,9 +83,10 @@ Useful for scripting (implies \fB\-\-compact\fR).
.TP
.B \-r, \-\-run \<expression\>
Execute \<expression\> in place of opening URL with a browser. Use {} in
\<expression\> to substitute in the URL. Example:
\<expression\> to substitute in the URL. Examples:

$ urlscan --run 'echo {} | xclip -i' file.txt
$ urlscan --run 'tmux set buffer {}'
.TP
.B \-p, \-\-pipe
Pipe the selected URL to the command specified by `--run`. This is preferred
Expand Down Expand Up @@ -137,6 +143,8 @@ The follow actions are supported:
.TP
\fBhelp_menu\fR \-\- show/hide help menu (Default: \fBF1\fR)
.TP
\fBlink_handler\fR \-\- cycle link handling (webbrowser, xdg-open or custom) (Default: \fBl\fR)
.TP
\fBopen_url\fR \-\- open selected URL (Default: \fBspace\fR or \fBenter\fR)
.TP
\fBpalette\fR \-\- cycle through palettes (Default: \fBp\fR)
Expand Down
43 changes: 36 additions & 7 deletions urlscan/urlchoose.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from os.path import dirname, exists, expanduser
import re
import shlex
from subprocess import Popen, PIPE
from subprocess import call, Popen, PIPE, DEVNULL
import sys
from threading import Thread
from time import sleep
Expand Down Expand Up @@ -114,6 +114,7 @@ def __init__(self, extractedurls, compact=False, nohelp=False, dedupe=False,
'j': self._down,
'k': self._up,
'P': self._clipboard_pri,
'l': self._link_handler,
'p': self._palette,
'Q': self._quit,
'q': self._quit,
Expand Down Expand Up @@ -168,6 +169,11 @@ def __init__(self, extractedurls, compact=False, nohelp=False, dedupe=False,
pass
except FileNotFoundError:
pass
try:
call(['xdg-open'], stdout=DEVNULL)
self.xdg = True
except OSError:
self.xdg = False
self.shorten = shorten
self.compact = compact
self.run = run
Expand All @@ -194,10 +200,16 @@ def __init__(self, extractedurls, compact=False, nohelp=False, dedupe=False,
self.items_org = grp_list(self.items)
listbox = urwid.ListBox(self.items)
self.header = (":: F1 - help/keybindings :: "
":: q - quit :: "
"/ - search :: ")
"q - quit :: "
"/ - search :: "
"URL opening mode - {}")
self.link_open_modes = ["Web Browser", "Xdg-Open"] if self.xdg is True else ["Web Browser"]
if self.run:
self.link_open_modes.insert(0, self.run)
self.nohelp = nohelp
if nohelp is False:
self.headerwid = urwid.AttrMap(urwid.Text(self.header), 'header')
self.headerwid = urwid.AttrMap(urwid.Text(
self.header.format(self.link_open_modes[0])), 'header')
else:
self.headerwid = None
self.top = urwid.Frame(listbox, self.headerwid)
Expand Down Expand Up @@ -299,7 +311,8 @@ def _quit(self):

def _open_url(self):
"""<Enter> or <space>"""
load_text = "Loading URL..." if not self.run else "Executing: {}".format(self.run)
load_text = "Loading URL..." if self.link_open_modes[0] != self.run \
else "Executing: {}".format(self.run)
if os.environ.get('BROWSER') not in ['elinks', 'links', 'w3m', 'lynx']:
self._footer_start_thread(load_text, 5)

Expand All @@ -322,6 +335,7 @@ def _help_menu(self):
"context -- show/hide context\n"
"down -- cursor down\n"
"help_menu -- show/hide help menu\n"
"link_handler -- cycle through xdg-open, webbrowser and user-defined function\n"
"open_url -- open selected URL\n"
"palette -- cycle through palettes\n"
"quit -- quit\n"
Expand Down Expand Up @@ -596,6 +610,18 @@ def draw_screen(self, size):
def _get_search(self):
return lambda: self.search, lambda: self.enter

def _link_handler(self):
"""Function to cycle through opening links via webbrowser module,
xdg-open or custom expression passed with --run.
"""
mode = self.link_open_modes.pop()
self.link_open_modes.insert(0, mode)
if self.nohelp is False:
self.headerwid = urwid.AttrMap(urwid.Text(
self.header.format(self.link_open_modes[0])), 'header')
self.top.header = self.headerwid

def mkbrowseto(self, url):
"""Create the urwid callback function to open the web browser or call
another function with the URL.
Expand All @@ -621,9 +647,12 @@ def browse(*args):
if self._get_search()[1]() is True:
self.search = False
self.enter = False
elif not self.run:
elif self.link_open_modes[0] == "Web Browser":
webbrowser.open(url)
elif self.run and self.pipe:
elif self.link_open_modes[0] == "Xdg-Open":
run = 'xdg-open "{}"'.format(url)
process = Popen(shlex.split(run), stdout=PIPE, stdin=PIPE)
elif self.link_open_modes[0] == self.run and self.pipe:
process = Popen(shlex.split(self.run), stdout=PIPE, stdin=PIPE)
process.communicate(input=url.encode(sys.getdefaultencoding()))
else:
Expand Down

0 comments on commit 728569a

Please sign in to comment.