Merge branch 'blyxxyz-clip'

This commit is contained in:
Florian Bruhin 2016-08-10 20:48:54 +02:00
commit 3d4fe5dde1
11 changed files with 172 additions and 91 deletions

View File

@ -30,6 +30,8 @@ Added
(to report bugs which are difficult to reproduce).
- New `hide-unmatched-rapid-hints` option to not hide hint unmatched hint labels
in rapid mode.
- New `{clipboard}` and `{primary}` replacements for the commandline which
replace the `:paste` command.
Changed
~~~~~~~
@ -65,6 +67,12 @@ Changed
- The `:buffer` completion now also filters using the first column (id).
- `:undo` has been improved to reopen tabs at the position they were closed.
Deprecated
~~~~~~~~~~
- The `:paste` command got deprecated as `:open` with `{clipboard}` and
`{primary}` can be used instead.
Removed
~~~~~~~

View File

@ -145,12 +145,12 @@ Contributors, sorted by the number of commits in descending order:
* Antoni Boucher
* Lamar Pavel
* Bruno Oliveira
* Jan Verbeek
* Alexander Cogneau
* Marshall Lochbaum
* Jakub Klinkovský
* Felix Van der Jeugt
* Martin Tournoij
* Jan Verbeek
* Raphael Pierzina
* Joel Torstensson
* Patric Schmitz

View File

@ -38,7 +38,6 @@
|<<messages,messages>>|Show a log of past messages.
|<<navigate,navigate>>|Open typical prev/next links or navigate using the URL path.
|<<open,open>>|Open a URL in the current/[count]th tab.
|<<paste,paste>>|Open a page from the clipboard.
|<<print,print>>|Print the current/[count]th tab.
|<<quickmark-add,quickmark-add>>|Add a new quickmark.
|<<quickmark-del,quickmark-del>>|Delete a quickmark.
@ -487,6 +486,8 @@ Syntax: +:open [*--implicit*] [*--bg*] [*--tab*] [*--window*] ['url']+
Open a URL in the current/[count]th tab.
If the URL contains newlines, each line gets opened in its own tab.
==== positional arguments
* +'url'+: The URL to open.
@ -503,20 +504,6 @@ The tab index to open the URL in.
==== note
* This command does not split arguments after the last argument and handles quotes literally.
[[paste]]
=== paste
Syntax: +:paste [*--sel*] [*--tab*] [*--bg*] [*--window*]+
Open a page from the clipboard.
If the pasted text contains newlines, each line gets opened in its own tab.
==== optional arguments
* +*-s*+, +*--sel*+: Use the primary selection instead of the clipboard.
* +*-t*+, +*--tab*+: Open in a new tab.
* +*-b*+, +*--bg*+: Open in a background tab.
* +*-w*+, +*--window*+: Open in new window.
[[print]]
=== print
Syntax: +:print [*--preview*] [*--pdf* 'file']+

View File

@ -236,6 +236,8 @@ class CommandDispatcher:
bg=False, tab=False, window=False, count=None):
"""Open a URL in the current/[count]th tab.
If the URL contains newlines, each line gets opened in its own tab.
Args:
url: The URL to open.
bg: Open in a new background tab.
@ -247,35 +249,73 @@ class CommandDispatcher:
"""
if url is None:
if tab or bg or window:
url = config.get('general', 'default-page')
urls = [config.get('general', 'default-page')]
else:
raise cmdexc.CommandError("No URL given, but -t/-b/-w is not "
"set!")
else:
try:
url = objreg.get('quickmark-manager').get(url)
except urlmarks.Error:
try:
url = urlutils.fuzzy_url(url)
except urlutils.InvalidUrlError as e:
# We don't use cmdexc.CommandError here as this can be
# called async from edit_url
message.error(self._win_id, str(e))
return
if tab or bg or window:
self._open(url, tab, bg, window, not implicit)
else:
curtab = self._cntwidget(count)
if curtab is None:
if count is None:
# We want to open a URL in the current tab, but none exists
# yet.
self._tabbed_browser.tabopen(url)
else:
# Explicit count with a tab that doesn't exist.
return
urls = self._parse_url_input(url)
for i, cur_url in enumerate(urls):
if not window and i > 0:
tab = False
bg = True
if tab or bg or window:
self._open(cur_url, tab, bg, window, not implicit)
else:
curtab.openurl(url)
curtab = self._cntwidget(count)
if curtab is None:
if count is None:
# We want to open a URL in the current tab, but none
# exists yet.
self._tabbed_browser.tabopen(cur_url)
else:
# Explicit count with a tab that doesn't exist.
return
else:
curtab.openurl(cur_url)
def _parse_url(self, url, *, force_search=False):
"""Parse a URL or quickmark or search query.
Args:
url: The URL to parse.
force_search: Whether to force a search even if the content can be
interpreted as a URL or a path.
Return:
A URL that can be opened.
"""
try:
return objreg.get('quickmark-manager').get(url)
except urlmarks.Error:
try:
return urlutils.fuzzy_url(url, force_search=force_search)
except urlutils.InvalidUrlError as e:
# We don't use cmdexc.CommandError here as this can be
# called async from edit_url
message.error(self._win_id, str(e))
return None
def _parse_url_input(self, url):
"""Parse a URL or newline-separated list of URLs.
Args:
url: The URL or list to parse.
Return:
A list of URLs that can be opened.
"""
force_search = False
urllist = [u for u in url.split('\n') if u.strip()]
if (len(urllist) > 1 and not urlutils.is_url(urllist[0]) and
urlutils.get_path_if_valid(urllist[0], check_exists=True)
is None):
urllist = [url]
force_search = True
for cur_url in urllist:
parsed = self._parse_url(cur_url, force_search=force_search)
if parsed is not None:
yield parsed
@cmdutils.register(instance='command-dispatcher', name='reload',
scope='window')
@ -796,7 +836,8 @@ class CommandDispatcher:
else:
raise cmdexc.CommandError("Last tab")
@cmdutils.register(instance='command-dispatcher', scope='window')
@cmdutils.register(instance='command-dispatcher', scope='window',
deprecated="Use :open {clipboard}")
def paste(self, sel=False, tab=False, bg=False, window=False):
"""Open a page from the clipboard.
@ -810,15 +851,12 @@ class CommandDispatcher:
window: Open in new window.
"""
force_search = False
if sel and utils.supports_selection():
target = "Primary selection"
else:
if not utils.supports_selection():
sel = False
target = "Clipboard"
text = utils.get_clipboard(selection=sel)
if not text.strip():
raise cmdexc.CommandError("{} is empty.".format(target))
log.misc.debug("{} contained: {!r}".format(target, text))
try:
text = utils.get_clipboard(selection=sel)
except utils.ClipboardError as e:
raise cmdexc.CommandError(e)
text_urls = [u for u in text.split('\n') if u.strip()]
if (len(text_urls) > 1 and not urlutils.is_url(text_urls[0]) and
urlutils.get_path_if_valid(
@ -1463,9 +1501,12 @@ class CommandDispatcher:
raise cmdexc.CommandError("Focused element is not editable!")
try:
sel = utils.get_clipboard(selection=True)
except utils.SelectionUnsupportedError:
sel = utils.get_clipboard()
try:
sel = utils.get_clipboard(selection=True)
except utils.SelectionUnsupportedError:
sel = utils.get_clipboard()
except utils.ClipboardEmptyError:
return
log.misc.debug("Pasting primary selection into element {}".format(
elem.debug_text()))

View File

@ -26,7 +26,7 @@ from PyQt5.QtCore import pyqtSlot, QUrl, QObject
from qutebrowser.config import config, configexc
from qutebrowser.commands import cmdexc, cmdutils
from qutebrowser.utils import message, objreg, qtutils
from qutebrowser.utils import message, objreg, qtutils, utils
from qutebrowser.misc import split
@ -49,21 +49,29 @@ def _current_url(tabbed_browser):
def replace_variables(win_id, arglist):
"""Utility function to replace variables like {url} in a list of args."""
variables = {
'{url}': lambda: _current_url(tabbed_browser).toString(
QUrl.FullyEncoded | QUrl.RemovePassword),
'{url:pretty}': lambda: _current_url(tabbed_browser).toString(
QUrl.RemovePassword),
'{clipboard}': utils.get_clipboard,
'{primary}': lambda: utils.get_clipboard(selection=True),
}
values = {}
args = []
tabbed_browser = objreg.get('tabbed-browser', scope='window',
window=win_id)
if any('{url}' in arg for arg in arglist):
url = _current_url(tabbed_browser).toString(QUrl.FullyEncoded |
QUrl.RemovePassword)
if any('{url:pretty}' in arg for arg in arglist):
pretty_url = _current_url(tabbed_browser).toString(QUrl.RemovePassword)
for arg in arglist:
if '{url}' in arg:
args.append(arg.replace('{url}', url))
elif '{url:pretty}' in arg:
args.append(arg.replace('{url:pretty}', pretty_url))
else:
try:
for arg in arglist:
for var, func in variables.items():
if var in arg:
if var not in values:
values[var] = func()
arg = arg.replace(var, values[var])
args.append(arg)
except utils.ClipboardError as e:
raise cmdexc.CommandError(e)
return args

View File

@ -1523,12 +1523,12 @@ KEY_DATA = collections.OrderedDict([
('yank domain -s', ['yD']),
('yank pretty-url', ['yp']),
('yank pretty-url -s', ['yP']),
('paste', ['pp']),
('paste -s', ['pP']),
('paste -t', ['Pp']),
('paste -ts', ['PP']),
('paste -w', ['wp']),
('paste -ws', ['wP']),
('open {clipboard}', ['pp']),
('open {primary}', ['pP']),
('open -t {clipboard}', ['Pp']),
('open -t {primary}', ['PP']),
('open -w {clipboard}', ['wp']),
('open -w {primary}', ['wP']),
('quickmark-save', ['m']),
('set-cmd-text -s :quickmark-load', ['b']),
('set-cmd-text -s :quickmark-load -t', ['B']),
@ -1698,6 +1698,11 @@ CHANGED_KEY_COMMANDS = [
(re.compile(r'^yank-selected -p'), r'yank selection -s'),
(re.compile(r'^yank-selected'), r'yank selection'),
(re.compile(r'^paste$'), r'open {clipboard}'),
(re.compile(r'^paste -([twb])$'), r'open -\1 {clipboard}'),
(re.compile(r'^paste -([twb])s$'), r'open -\1 {primary}'),
(re.compile(r'^paste -s([twb])$'), r'open -\1 {primary}'),
(re.compile(r'^completion-item-next'), r'completion-item-focus next'),
(re.compile(r'^completion-item-prev'), r'completion-item-focus prev'),
]

View File

@ -47,7 +47,7 @@ class MinimalLineEditMixin:
if e.key() == Qt.Key_Insert and e.modifiers() == Qt.ShiftModifier:
try:
text = utils.get_clipboard(selection=True)
except utils.SelectionUnsupportedError:
except utils.ClipboardError:
pass
else:
e.accept()

View File

@ -43,11 +43,21 @@ fake_clipboard = None
log_clipboard = False
class SelectionUnsupportedError(Exception):
class ClipboardError(Exception):
"""Raised if the clipboard contents are unavailable for some reason."""
class SelectionUnsupportedError(ClipboardError):
"""Raised if [gs]et_clipboard is used and selection=True is unsupported."""
class ClipboardEmptyError(ClipboardError):
"""Raised if get_clipboard is used and the clipboard is empty."""
def elide(text, length):
"""Elide text so it uses a maximum of length chars."""
if length < 1:
@ -810,6 +820,11 @@ def get_clipboard(selection=False):
mode = QClipboard.Selection if selection else QClipboard.Clipboard
data = QApplication.clipboard().text(mode=mode)
target = "Primary selection" if selection else "Clipboard"
if not data.strip():
raise ClipboardEmptyError("{} is empty.".format(target))
log.misc.debug("{} contained: {!r}".format(target, data))
return data

View File

@ -524,3 +524,16 @@ Feature: Various utility commands.
Then the following tabs should be open:
- data/hints/link_blank.html
- data/hello.txt (active)
## Variables
Scenario: {url} as part of an argument
When I open data/hello.txt
And I run :message-info foo{url}
Then the message "foohttp://localhost:*/hello.txt" should be shown
Scenario: Multiple variables in an argument
When I open data/hello.txt
And I put "foo" into the clipboard
And I run :message-info {clipboard}bar{url}
Then the message "foobarhttp://localhost:*/hello.txt" should be shown

View File

@ -1,6 +1,6 @@
Feature: Yanking and pasting.
:yank and :paste can be used to copy/paste the URL or title from/to the
clipboard and primary selection.
:yank, {clipboard} and {primary} can be used to copy/paste the URL or title
from/to the clipboard and primary selection.
Background:
Given I run :tab-only
@ -45,11 +45,11 @@ Feature: Yanking and pasting.
Then the message "Yanked URL to clipboard: http://localhost:(port)/data/title with spaces.html" should be shown
And the clipboard should contain "http://localhost:(port)/data/title with spaces.html"
#### :paste
#### {clipboard} and {primary}
Scenario: Pasting a URL
When I put "http://localhost:(port)/data/hello.txt" into the clipboard
And I run :paste
And I run :open {clipboard}
And I wait until data/hello.txt is loaded
Then the requests should be:
data/hello.txt
@ -57,32 +57,32 @@ Feature: Yanking and pasting.
Scenario: Pasting a URL from primary selection
When selection is supported
And I put "http://localhost:(port)/data/hello2.txt" into the primary selection
And I run :paste --sel
And I run :open {primary}
And I wait until data/hello2.txt is loaded
Then the requests should be:
data/hello2.txt
Scenario: Pasting with empty clipboard
When I put "" into the clipboard
And I run :paste
And I run :open {clipboard} (invalid command)
Then the error "Clipboard is empty." should be shown
Scenario: Pasting with empty selection
When selection is supported
And I put "" into the primary selection
And I run :paste --sel
And I run :open {primary} (invalid command)
Then the error "Primary selection is empty." should be shown
Scenario: Pasting with a space in clipboard
When I put " " into the clipboard
And I run :paste
And I run :open {clipboard} (invalid command)
Then the error "Clipboard is empty." should be shown
Scenario: Pasting in a new tab
Given I open about:blank
When I run :tab-only
And I put "http://localhost:(port)/data/hello.txt" into the clipboard
And I run :paste -t
And I run :open -t {clipboard}
And I wait until data/hello.txt is loaded
Then the following tabs should be open:
- about:blank
@ -92,7 +92,7 @@ Feature: Yanking and pasting.
Given I open about:blank
When I run :tab-only
And I put "http://localhost:(port)/data/hello.txt" into the clipboard
And I run :paste -b
And I run :open -b {clipboard}
And I wait until data/hello.txt is loaded
Then the following tabs should be open:
- about:blank (active)
@ -101,7 +101,7 @@ Feature: Yanking and pasting.
Scenario: Pasting in a new window
Given I have a fresh instance
When I put "http://localhost:(port)/data/hello.txt" into the clipboard
And I run :paste -w
And I run :open -w {clipboard}
And I wait until data/hello.txt is loaded
Then the session should look like:
windows:
@ -119,7 +119,7 @@ Feature: Yanking and pasting.
Scenario: Pasting an invalid URL
When I set general -> auto-search to false
And I put "foo bar" into the clipboard
And I run :paste
And I run :open {clipboard}
Then the error "Invalid URL" should be shown
Scenario: Pasting multiple urls in a new tab
@ -128,7 +128,7 @@ Feature: Yanking and pasting.
http://localhost:(port)/data/hello.txt
http://localhost:(port)/data/hello2.txt
http://localhost:(port)/data/hello3.txt
And I run :paste -t
And I run :open -t {clipboard}
And I wait until data/hello.txt is loaded
And I wait until data/hello2.txt is loaded
And I wait until data/hello3.txt is loaded
@ -145,7 +145,7 @@ Feature: Yanking and pasting.
this url:
http://qutebrowser.org
should not open
And I run :paste -t
And I run :open -t {clipboard}
And I wait until data/hello.txt?q=this%20url%3A%0Ahttp%3A//qutebrowser.org%0Ashould%20not%20open is loaded
Then the following tabs should be open:
- about:blank
@ -159,7 +159,7 @@ Feature: Yanking and pasting.
text:
should open
as search
And I run :paste -t
And I run :open -t {clipboard}
And I wait until data/hello.txt?q=text%3A%0Ashould%20open%0Aas%20search is loaded
Then the following tabs should be open:
- about:blank
@ -172,7 +172,7 @@ Feature: Yanking and pasting.
http://localhost:(port)/data/hello.txt
http://localhost:(port)/data/hello2.txt
http://localhost:(port)/data/hello3.txt
And I run :paste -b
And I run :open -b {clipboard}
And I wait until data/hello.txt is loaded
And I wait until data/hello2.txt is loaded
And I wait until data/hello3.txt is loaded
@ -188,7 +188,7 @@ Feature: Yanking and pasting.
http://localhost:(port)/data/hello.txt
http://localhost:(port)/data/hello2.txt
http://localhost:(port)/data/hello3.txt
And I run :paste -w
And I run :open -w {clipboard}
And I wait until data/hello.txt is loaded
And I wait until data/hello2.txt is loaded
And I wait until data/hello3.txt is loaded
@ -218,13 +218,13 @@ Feature: Yanking and pasting.
Scenario: Pasting multiple urls with an empty one
When I open about:blank
And I put "http://localhost:(port)/data/hello.txt\n\nhttp://localhost:(port)/data/hello2.txt" into the clipboard
And I run :paste -t
And I run :open -t {clipboard}
Then no crash should happen
Scenario: Pasting multiple urls with an almost empty one
When I open about:blank
And I put "http://localhost:(port)/data/hello.txt\n \nhttp://localhost:(port)/data/hello2.txt" into the clipboard
And I run :paste -t
And I run :open -t {clipboard}
Then no crash should happen
#### :paste-primary

View File

@ -298,6 +298,10 @@ class TestKeyConfigParser:
('yank -ds', 'yank domain -s'),
('yank -p', 'yank pretty-url'),
('yank -ps', 'yank pretty-url -s'),
('paste', 'open {clipboard}'),
('paste -t', 'open -t {clipboard}'),
('paste -ws', 'open -w {primary}'),
]
)
def test_migrations(self, old, new_expected):