Merge branch 'tab-complete' of https://github.com/toofar/qutebrowser into toofar-tab-complete
This commit is contained in:
commit
c1cec53c0e
@ -170,11 +170,11 @@ Contributors, sorted by the number of commits in descending order:
|
|||||||
* ZDarian
|
* ZDarian
|
||||||
* Milan Svoboda
|
* Milan Svoboda
|
||||||
* John ShaggyTwoDope Jenkins
|
* John ShaggyTwoDope Jenkins
|
||||||
|
* Jimmy
|
||||||
* Peter Vilim
|
* Peter Vilim
|
||||||
* Clayton Craft
|
* Clayton Craft
|
||||||
* Oliver Caldwell
|
* Oliver Caldwell
|
||||||
* Jonas Schürmann
|
* Jonas Schürmann
|
||||||
* Jimmy
|
|
||||||
* Panagiotis Ktistakis
|
* Panagiotis Ktistakis
|
||||||
* Jakub Klinkovský
|
* Jakub Klinkovský
|
||||||
* skinnay
|
* skinnay
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|<<bookmark-add,bookmark-add>>|Save the current page as a bookmark.
|
|<<bookmark-add,bookmark-add>>|Save the current page as a bookmark.
|
||||||
|<<bookmark-del,bookmark-del>>|Delete a bookmark.
|
|<<bookmark-del,bookmark-del>>|Delete a bookmark.
|
||||||
|<<bookmark-load,bookmark-load>>|Load a bookmark.
|
|<<bookmark-load,bookmark-load>>|Load a bookmark.
|
||||||
|
|<<buffer,buffer>>|Select tab by index or url/title best match.
|
||||||
|<<close,close>>|Close the current window.
|
|<<close,close>>|Close the current window.
|
||||||
|<<download,download>>|Download a given URL, or current page if no URL given.
|
|<<download,download>>|Download a given URL, or current page if no URL given.
|
||||||
|<<download-cancel,download-cancel>>|Cancel the last/[count]th download.
|
|<<download-cancel,download-cancel>>|Cancel the last/[count]th download.
|
||||||
@ -142,6 +143,18 @@ Load a bookmark.
|
|||||||
* This command does not split arguments after the last argument and handles quotes literally.
|
* This command does not split arguments after the last argument and handles quotes literally.
|
||||||
* With this command, +;;+ is interpreted literally instead of splitting off a second command.
|
* With this command, +;;+ is interpreted literally instead of splitting off a second command.
|
||||||
|
|
||||||
|
[[buffer]]
|
||||||
|
=== buffer
|
||||||
|
Syntax: +:buffer 'index'+
|
||||||
|
|
||||||
|
Select tab by index or url/title best match.
|
||||||
|
|
||||||
|
Focuses window if necessary.
|
||||||
|
|
||||||
|
==== positional arguments
|
||||||
|
* +'index'+: The [win_id/]index of the tab to focus. Or a substring in which case the closest match will be focused.
|
||||||
|
|
||||||
|
|
||||||
[[close]]
|
[[close]]
|
||||||
=== close
|
=== close
|
||||||
Close the current window.
|
Close the current window.
|
||||||
|
@ -35,7 +35,7 @@ from PyQt5.QtWidgets import QApplication
|
|||||||
from PyQt5.QtWebKit import QWebSettings
|
from PyQt5.QtWebKit import QWebSettings
|
||||||
from PyQt5.QtGui import QDesktopServices, QPixmap, QIcon, QCursor, QWindow
|
from PyQt5.QtGui import QDesktopServices, QPixmap, QIcon, QCursor, QWindow
|
||||||
from PyQt5.QtCore import (pyqtSlot, qInstallMessageHandler, QTimer, QUrl,
|
from PyQt5.QtCore import (pyqtSlot, qInstallMessageHandler, QTimer, QUrl,
|
||||||
QObject, Qt, QEvent)
|
QObject, Qt, QEvent, pyqtSignal)
|
||||||
try:
|
try:
|
||||||
import hunter
|
import hunter
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@ -742,6 +742,8 @@ class Application(QApplication):
|
|||||||
_args: ArgumentParser instance.
|
_args: ArgumentParser instance.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
new_window = pyqtSignal(mainwindow.MainWindow)
|
||||||
|
|
||||||
def __init__(self, args):
|
def __init__(self, args):
|
||||||
"""Constructor.
|
"""Constructor.
|
||||||
|
|
||||||
|
@ -45,6 +45,7 @@ from qutebrowser.utils import (message, usertypes, log, qtutils, urlutils,
|
|||||||
objreg, utils)
|
objreg, utils)
|
||||||
from qutebrowser.utils.usertypes import KeyMode
|
from qutebrowser.utils.usertypes import KeyMode
|
||||||
from qutebrowser.misc import editor, guiprocess
|
from qutebrowser.misc import editor, guiprocess
|
||||||
|
from qutebrowser.completion.models import instances, sortfilter
|
||||||
|
|
||||||
|
|
||||||
class CommandDispatcher:
|
class CommandDispatcher:
|
||||||
@ -834,6 +835,60 @@ class CommandDispatcher:
|
|||||||
raise cmdexc.CommandError(e)
|
raise cmdexc.CommandError(e)
|
||||||
self._open(url, tab, bg, window)
|
self._open(url, tab, bg, window)
|
||||||
|
|
||||||
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
||||||
|
completion=[usertypes.Completion.tab])
|
||||||
|
def buffer(self, index):
|
||||||
|
"""Select tab by index or url/title best match.
|
||||||
|
|
||||||
|
Focuses window if necessary.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
index: The [win_id/]index of the tab to focus. Or a substring
|
||||||
|
in which case the closest match will be focused.
|
||||||
|
"""
|
||||||
|
index_parts = index.split('/', 1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
for part in index_parts:
|
||||||
|
int(part)
|
||||||
|
except ValueError:
|
||||||
|
model = instances.get(usertypes.Completion.tab)
|
||||||
|
sf = sortfilter.CompletionFilterModel(source=model)
|
||||||
|
sf.set_pattern(index)
|
||||||
|
if sf.count() > 0:
|
||||||
|
index = sf.data(sf.first_item())
|
||||||
|
index_parts = index.split('/', 1)
|
||||||
|
else:
|
||||||
|
raise cmdexc.CommandError(
|
||||||
|
"No matching tab for: {}".format(index))
|
||||||
|
|
||||||
|
if len(index_parts) == 2:
|
||||||
|
win_id = int(index_parts[0])
|
||||||
|
idx = int(index_parts[1])
|
||||||
|
elif len(index_parts) == 1:
|
||||||
|
idx = int(index_parts[0])
|
||||||
|
active_win = objreg.get('app').activeWindow()
|
||||||
|
if active_win is None:
|
||||||
|
# Not sure how you enter a command without an active window...
|
||||||
|
raise cmdexc.CommandError(
|
||||||
|
"No window specified and couldn't find active window!")
|
||||||
|
win_id = active_win.win_id
|
||||||
|
|
||||||
|
if win_id not in objreg.window_registry:
|
||||||
|
raise cmdexc.CommandError(
|
||||||
|
"There's no window with id {}!".format(win_id))
|
||||||
|
|
||||||
|
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
||||||
|
window=win_id)
|
||||||
|
if not 0 < idx <= tabbed_browser.count():
|
||||||
|
raise cmdexc.CommandError(
|
||||||
|
"There's no tab with index {}!".format(idx))
|
||||||
|
|
||||||
|
window = objreg.window_registry[win_id]
|
||||||
|
window.activateWindow()
|
||||||
|
window.raise_()
|
||||||
|
tabbed_browser.setCurrentIndex(idx-1)
|
||||||
|
|
||||||
@cmdutils.register(instance='command-dispatcher', scope='window',
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
||||||
count='count')
|
count='count')
|
||||||
def tab_focus(self, index: {'type': (int, 'last')}=None, count=None):
|
def tab_focus(self, index: {'type': (int, 'last')}=None, count=None):
|
||||||
|
@ -59,6 +59,14 @@ def _init_url_completion():
|
|||||||
_instances[usertypes.Completion.url] = model
|
_instances[usertypes.Completion.url] = model
|
||||||
|
|
||||||
|
|
||||||
|
def _init_tab_completion():
|
||||||
|
"""Initialize the tab completion model."""
|
||||||
|
log.completion.debug("Initializing tab completion.")
|
||||||
|
with debug.log_time(log.completion, 'tab completion init'):
|
||||||
|
model = miscmodels.TabCompletionModel()
|
||||||
|
_instances[usertypes.Completion.tab] = model
|
||||||
|
|
||||||
|
|
||||||
def _init_setting_completions():
|
def _init_setting_completions():
|
||||||
"""Initialize setting completion models."""
|
"""Initialize setting completion models."""
|
||||||
log.completion.debug("Initializing setting completion.")
|
log.completion.debug("Initializing setting completion.")
|
||||||
@ -115,6 +123,7 @@ INITIALIZERS = {
|
|||||||
usertypes.Completion.command: _init_command_completion,
|
usertypes.Completion.command: _init_command_completion,
|
||||||
usertypes.Completion.helptopic: _init_helptopic_completion,
|
usertypes.Completion.helptopic: _init_helptopic_completion,
|
||||||
usertypes.Completion.url: _init_url_completion,
|
usertypes.Completion.url: _init_url_completion,
|
||||||
|
usertypes.Completion.tab: _init_tab_completion,
|
||||||
usertypes.Completion.section: _init_setting_completions,
|
usertypes.Completion.section: _init_setting_completions,
|
||||||
usertypes.Completion.option: _init_setting_completions,
|
usertypes.Completion.option: _init_setting_completions,
|
||||||
usertypes.Completion.value: _init_setting_completions,
|
usertypes.Completion.value: _init_setting_completions,
|
||||||
|
@ -19,6 +19,9 @@
|
|||||||
|
|
||||||
"""Misc. CompletionModels."""
|
"""Misc. CompletionModels."""
|
||||||
|
|
||||||
|
from PyQt5.QtCore import Qt, QTimer, pyqtSlot
|
||||||
|
|
||||||
|
from qutebrowser.browser import webview
|
||||||
from qutebrowser.config import config, configdata
|
from qutebrowser.config import config, configdata
|
||||||
from qutebrowser.utils import objreg, log
|
from qutebrowser.utils import objreg, log
|
||||||
from qutebrowser.commands import cmdutils
|
from qutebrowser.commands import cmdutils
|
||||||
@ -138,3 +141,77 @@ class SessionCompletionModel(base.BaseCompletionModel):
|
|||||||
self.new_item(cat, name)
|
self.new_item(cat, name)
|
||||||
except OSError:
|
except OSError:
|
||||||
log.completion.exception("Failed to list sessions!")
|
log.completion.exception("Failed to list sessions!")
|
||||||
|
|
||||||
|
|
||||||
|
class TabCompletionModel(base.BaseCompletionModel):
|
||||||
|
|
||||||
|
"""A model to complete on open tabs across all windows.
|
||||||
|
|
||||||
|
Used for switching the buffer command."""
|
||||||
|
|
||||||
|
# https://github.com/The-Compiler/qutebrowser/issues/545
|
||||||
|
# pylint: disable=abstract-method
|
||||||
|
|
||||||
|
#IDX_COLUMN = 0
|
||||||
|
URL_COLUMN = 1
|
||||||
|
TEXT_COLUMN = 2
|
||||||
|
|
||||||
|
COLUMN_WIDTHS = (6, 40, 54)
|
||||||
|
DUMB_SORT = Qt.DescendingOrder
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self.columns_to_filter = [self.URL_COLUMN, self.TEXT_COLUMN]
|
||||||
|
|
||||||
|
for win_id in objreg.window_registry:
|
||||||
|
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
||||||
|
window=win_id)
|
||||||
|
for i in range(tabbed_browser.count()):
|
||||||
|
tab = tabbed_browser.widget(i)
|
||||||
|
tab.url_text_changed.connect(self.rebuild)
|
||||||
|
tab.shutting_down.connect(self.delayed_rebuild)
|
||||||
|
tabbed_browser.new_tab.connect(self.on_new_tab)
|
||||||
|
objreg.get("app").new_window.connect(self.on_new_window)
|
||||||
|
self.rebuild()
|
||||||
|
|
||||||
|
# slot argument should be mainwindow.MainWindow but can't import
|
||||||
|
# that at module level because of import loops.
|
||||||
|
@pyqtSlot(object)
|
||||||
|
def on_new_window(self, window):
|
||||||
|
"""Add hooks to new windows."""
|
||||||
|
window.tabbed_browser.new_tab.connect(self.on_new_tab)
|
||||||
|
|
||||||
|
@pyqtSlot(webview.WebView)
|
||||||
|
def on_new_tab(self, tab):
|
||||||
|
"""Add hooks to new tabs."""
|
||||||
|
tab.url_text_changed.connect(self.rebuild)
|
||||||
|
tab.shutting_down.connect(self.delayed_rebuild)
|
||||||
|
self.rebuild()
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def delayed_rebuild(self):
|
||||||
|
"""Fire a rebuild indirectly so widgets get a chance to update."""
|
||||||
|
QTimer.singleShot(0, self.rebuild)
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def rebuild(self):
|
||||||
|
"""Rebuild completion model from current tabs.
|
||||||
|
|
||||||
|
Very lazy method of keeping the model up to date. We could connect to
|
||||||
|
signals for new tab, tab url/title changed, tab close, tab moved and
|
||||||
|
make sure we handled background loads too ... but iterating over a
|
||||||
|
few/few dozen/few hundred tabs doesn't take very long at all.
|
||||||
|
"""
|
||||||
|
self.removeRows(0, self.rowCount())
|
||||||
|
for win_id in objreg.window_registry:
|
||||||
|
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
||||||
|
window=win_id)
|
||||||
|
if tabbed_browser.shutting_down:
|
||||||
|
continue
|
||||||
|
c = self.new_category("{}".format(win_id))
|
||||||
|
for i in range(tabbed_browser.count()):
|
||||||
|
tab = tabbed_browser.widget(i)
|
||||||
|
self.new_item(c, "{}/{}".format(win_id, i+1),
|
||||||
|
tab.url().toDisplayString(),
|
||||||
|
tabbed_browser.page_title(i))
|
||||||
|
@ -187,6 +187,8 @@ class MainWindow(QWidget):
|
|||||||
#self.tabWidget.setCurrentIndex(0)
|
#self.tabWidget.setCurrentIndex(0)
|
||||||
#QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
#QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
||||||
|
|
||||||
|
objreg.get("app").new_window.emit(self)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return utils.get_repr(self)
|
return utils.get_repr(self)
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ class TabbedBrowser(tabwidget.TabWidget):
|
|||||||
tabbar -> new-tab-position set to 'left'.
|
tabbar -> new-tab-position set to 'left'.
|
||||||
_tab_insert_idx_right: Same as above, for 'right'.
|
_tab_insert_idx_right: Same as above, for 'right'.
|
||||||
_undo_stack: List of UndoEntry namedtuples of closed tabs.
|
_undo_stack: List of UndoEntry namedtuples of closed tabs.
|
||||||
_shutting_down: Whether we're currently shutting down.
|
shutting_down: Whether we're currently shutting down.
|
||||||
|
|
||||||
Signals:
|
Signals:
|
||||||
cur_progress: Progress of the current tab changed (loadProgress).
|
cur_progress: Progress of the current tab changed (loadProgress).
|
||||||
@ -82,6 +82,7 @@ class TabbedBrowser(tabwidget.TabWidget):
|
|||||||
widget can adjust its size to it.
|
widget can adjust its size to it.
|
||||||
arg: The new size.
|
arg: The new size.
|
||||||
current_tab_changed: The current tab changed to the emitted WebView.
|
current_tab_changed: The current tab changed to the emitted WebView.
|
||||||
|
new_tab: Emits the new WebView and its index when a new tab is opened.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
cur_progress = pyqtSignal(int)
|
cur_progress = pyqtSignal(int)
|
||||||
@ -96,13 +97,14 @@ class TabbedBrowser(tabwidget.TabWidget):
|
|||||||
resized = pyqtSignal('QRect')
|
resized = pyqtSignal('QRect')
|
||||||
got_cmd = pyqtSignal(str)
|
got_cmd = pyqtSignal(str)
|
||||||
current_tab_changed = pyqtSignal(webview.WebView)
|
current_tab_changed = pyqtSignal(webview.WebView)
|
||||||
|
new_tab = pyqtSignal(webview.WebView, int)
|
||||||
|
|
||||||
def __init__(self, win_id, parent=None):
|
def __init__(self, win_id, parent=None):
|
||||||
super().__init__(win_id, parent)
|
super().__init__(win_id, parent)
|
||||||
self._win_id = win_id
|
self._win_id = win_id
|
||||||
self._tab_insert_idx_left = 0
|
self._tab_insert_idx_left = 0
|
||||||
self._tab_insert_idx_right = -1
|
self._tab_insert_idx_right = -1
|
||||||
self._shutting_down = False
|
self.shutting_down = False
|
||||||
self.tabCloseRequested.connect(self.on_tab_close_requested)
|
self.tabCloseRequested.connect(self.on_tab_close_requested)
|
||||||
self.currentChanged.connect(self.on_current_changed)
|
self.currentChanged.connect(self.on_current_changed)
|
||||||
self.cur_load_started.connect(self.on_cur_load_started)
|
self.cur_load_started.connect(self.on_cur_load_started)
|
||||||
@ -234,7 +236,7 @@ class TabbedBrowser(tabwidget.TabWidget):
|
|||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
"""Try to shut down all tabs cleanly."""
|
"""Try to shut down all tabs cleanly."""
|
||||||
self._shutting_down = True
|
self.shutting_down = True
|
||||||
for tab in self.widgets():
|
for tab in self.widgets():
|
||||||
self._remove_tab(tab)
|
self._remove_tab(tab)
|
||||||
|
|
||||||
@ -398,6 +400,7 @@ class TabbedBrowser(tabwidget.TabWidget):
|
|||||||
if not background:
|
if not background:
|
||||||
self.setCurrentWidget(tab)
|
self.setCurrentWidget(tab)
|
||||||
tab.show()
|
tab.show()
|
||||||
|
self.new_tab.emit(tab, idx)
|
||||||
return tab
|
return tab
|
||||||
|
|
||||||
def _get_new_tab_idx(self, explicit):
|
def _get_new_tab_idx(self, explicit):
|
||||||
@ -546,7 +549,7 @@ class TabbedBrowser(tabwidget.TabWidget):
|
|||||||
@pyqtSlot(int)
|
@pyqtSlot(int)
|
||||||
def on_current_changed(self, idx):
|
def on_current_changed(self, idx):
|
||||||
"""Set last-focused-tab and leave hinting mode when focus changed."""
|
"""Set last-focused-tab and leave hinting mode when focus changed."""
|
||||||
if idx == -1 or self._shutting_down:
|
if idx == -1 or self.shutting_down:
|
||||||
# closing the last tab (before quitting) or shutting down
|
# closing the last tab (before quitting) or shutting down
|
||||||
return
|
return
|
||||||
tab = self.widget(idx)
|
tab = self.widget(idx)
|
||||||
|
@ -237,7 +237,7 @@ KeyMode = enum('KeyMode', ['normal', 'hint', 'command', 'yesno', 'prompt',
|
|||||||
# Available command completions
|
# Available command completions
|
||||||
Completion = enum('Completion', ['command', 'section', 'option', 'value',
|
Completion = enum('Completion', ['command', 'section', 'option', 'value',
|
||||||
'helptopic', 'quickmark_by_name',
|
'helptopic', 'quickmark_by_name',
|
||||||
'bookmark_by_url', 'url', 'sessions'])
|
'bookmark_by_url', 'url', 'tab', 'sessions'])
|
||||||
|
|
||||||
|
|
||||||
# Exit statuses for errors. Needs to be an int for sys.exit.
|
# Exit statuses for errors. Needs to be an int for sys.exit.
|
||||||
|
@ -709,3 +709,116 @@ Feature: Tab management
|
|||||||
- data/hints/link.html
|
- data/hints/link.html
|
||||||
- about:blank
|
- about:blank
|
||||||
- data/hello.txt (active)
|
- data/hello.txt (active)
|
||||||
|
|
||||||
|
# :buffer
|
||||||
|
|
||||||
|
Scenario: buffer without args
|
||||||
|
Given I have a fresh instance
|
||||||
|
When I run :buffer
|
||||||
|
Then the error "buffer: The following arguments are required: index" should be shown
|
||||||
|
|
||||||
|
Scenario: buffer one window title present
|
||||||
|
When I open data/title.html
|
||||||
|
And I open data/search.html in a new tab
|
||||||
|
And I open data/scroll.html in a new tab
|
||||||
|
And I run :buffer "Searching text"
|
||||||
|
Then the following tabs should be open:
|
||||||
|
- data/title.html
|
||||||
|
- data/search.html (active)
|
||||||
|
- data/scroll.html
|
||||||
|
|
||||||
|
Scenario: buffer one window title not present
|
||||||
|
When I run :buffer "invalid title"
|
||||||
|
Then the error "No matching tab for: invalid title" should be shown
|
||||||
|
|
||||||
|
Scenario: buffer two window title present
|
||||||
|
When I open data/title.html
|
||||||
|
And I open data/search.html in a new tab
|
||||||
|
And I open data/scroll.html in a new tab
|
||||||
|
And I open data/caret.html in a new window
|
||||||
|
And I open data/paste_primary.html in a new tab
|
||||||
|
And I run :buffer "Scrolling"
|
||||||
|
Then the session should look like:
|
||||||
|
windows:
|
||||||
|
- active: true
|
||||||
|
tabs:
|
||||||
|
- history:
|
||||||
|
- url: about:blank
|
||||||
|
- url: http://localhost:*/data/title.html
|
||||||
|
- history:
|
||||||
|
- url: http://localhost:*/data/search.html
|
||||||
|
- active: true
|
||||||
|
history:
|
||||||
|
- url: http://localhost:*/data/scroll.html
|
||||||
|
- tabs:
|
||||||
|
- history:
|
||||||
|
- url: http://localhost:*/data/caret.html
|
||||||
|
- active: true
|
||||||
|
history:
|
||||||
|
- url: http://localhost:*/data/paste_primary.html
|
||||||
|
|
||||||
|
Scenario: buffer one window index not present
|
||||||
|
When I open data/title.html
|
||||||
|
And I run :buffer "666"
|
||||||
|
Then the error "There's no tab with index 666!" should be shown
|
||||||
|
|
||||||
|
Scenario: buffer one window win not present
|
||||||
|
When I open data/title.html
|
||||||
|
And I run :buffer "2/1"
|
||||||
|
Then the error "There's no window with id 2!" should be shown
|
||||||
|
|
||||||
|
Scenario: buffer two window index present
|
||||||
|
Given I have a fresh instance
|
||||||
|
When I open data/title.html
|
||||||
|
And I open data/search.html in a new tab
|
||||||
|
And I open data/scroll.html in a new tab
|
||||||
|
And I run :open -w http://localhost:(port)/data/caret.html
|
||||||
|
And I open data/paste_primary.html in a new tab
|
||||||
|
And I wait until data/caret.html is loaded
|
||||||
|
And I run :buffer "0/2"
|
||||||
|
Then the session should look like:
|
||||||
|
windows:
|
||||||
|
- active: true
|
||||||
|
tabs:
|
||||||
|
- history:
|
||||||
|
- url: about:blank
|
||||||
|
- url: http://localhost:*/data/title.html
|
||||||
|
- active: true
|
||||||
|
history:
|
||||||
|
- url: http://localhost:*/data/search.html
|
||||||
|
- history:
|
||||||
|
- url: http://localhost:*/data/scroll.html
|
||||||
|
- tabs:
|
||||||
|
- history:
|
||||||
|
- url: http://localhost:*/data/caret.html
|
||||||
|
- active: true
|
||||||
|
history:
|
||||||
|
- url: http://localhost:*/data/paste_primary.html
|
||||||
|
|
||||||
|
Scenario: buffer troubling args 01
|
||||||
|
Given I have a fresh instance
|
||||||
|
When I open data/title.html
|
||||||
|
And I run :buffer "-1"
|
||||||
|
Then the error "There's no tab with index -1!" should be shown
|
||||||
|
|
||||||
|
Scenario: buffer troubling args 02
|
||||||
|
When I open data/title.html
|
||||||
|
And I run :buffer "/"
|
||||||
|
Then the following tabs should be open:
|
||||||
|
- data/title.html (active)
|
||||||
|
|
||||||
|
Scenario: buffer troubling args 03
|
||||||
|
When I open data/title.html
|
||||||
|
And I run :buffer "//"
|
||||||
|
Then the following tabs should be open:
|
||||||
|
- data/title.html (active)
|
||||||
|
|
||||||
|
Scenario: buffer troubling args 04
|
||||||
|
When I open data/title.html
|
||||||
|
And I run :buffer "0/x"
|
||||||
|
Then the error "No matching tab for: 0/x" should be shown
|
||||||
|
|
||||||
|
Scenario: buffer troubling args 05
|
||||||
|
When I open data/title.html
|
||||||
|
And I run :buffer "1/2/3"
|
||||||
|
Then the error "No matching tab for: 1/2/3" should be shown
|
||||||
|
Loading…
Reference in New Issue
Block a user