Interactively get macro register

This commit is contained in:
Jan Verbeek 2016-10-08 21:17:47 +02:00
parent 7aaaadac1a
commit 33ff0ba715
8 changed files with 68 additions and 47 deletions

View File

@ -606,15 +606,12 @@ Quit qutebrowser.
[[record-macro]]
=== record-macro
Syntax: +:record-macro ['name']+
Syntax: +:record-macro ['register']+
Start or stop recording a macro.
==== positional arguments
* +'name'+: Which name to give the macro.
==== note
* This command does not split arguments after the last argument and handles quotes literally.
* +'register'+: Which register to store the macro in.
[[reload]]
=== reload
@ -653,19 +650,16 @@ Restart qutebrowser while keeping existing tabs open.
[[run-macro]]
=== run-macro
Syntax: +:run-macro ['name']+
Syntax: +:run-macro ['register']+
Run a recorded macro.
==== positional arguments
* +'name'+: Which macro to run.
* +'register'+: Which macro to run.
==== count
How many times to run the macro.
==== note
* This command does not split arguments after the last argument and handles quotes literally.
[[save]]
=== save
Syntax: +:save ['what' ['what' ...]]+

View File

@ -34,8 +34,10 @@ from qutebrowser.misc import split
ParseResult = collections.namedtuple('ParseResult', ['cmd', 'args', 'cmdline',
'count'])
last_command = {}
macro = {}
recording_macro = None
macro_count = {}
def _current_url(tabbed_browser):

View File

@ -78,8 +78,10 @@ def init(win_id, parent):
warn=False),
KM.yesno: modeparsers.PromptKeyParser(win_id, modeman),
KM.caret: modeparsers.CaretKeyParser(win_id, modeman),
KM.set_mark: modeparsers.MarkKeyParser(win_id, KM.set_mark, modeman),
KM.jump_mark: modeparsers.MarkKeyParser(win_id, KM.jump_mark, modeman),
KM.set_mark: modeparsers.RegisterKeyParser(win_id, KM.set_mark, modeman),
KM.jump_mark: modeparsers.RegisterKeyParser(win_id, KM.jump_mark, modeman),
KM.record_macro: modeparsers.RegisterKeyParser(win_id, KM.record_macro, modeman),
KM.run_macro: modeparsers.RegisterKeyParser(win_id, KM.run_macro, modeman),
}
objreg.register('keyparsers', keyparsers, scope='window', window=win_id)
modeman.destroyed.connect(

View File

@ -27,6 +27,7 @@ from PyQt5.QtCore import pyqtSlot, Qt
from qutebrowser.config import config
from qutebrowser.keyinput import keyparser
from qutebrowser.misc import utilcmds
from qutebrowser.utils import usertypes, log, objreg, utils
@ -264,12 +265,13 @@ class CaretKeyParser(keyparser.CommandKeyParser):
self.read_config('caret')
class MarkKeyParser(keyparser.BaseKeyParser):
class RegisterKeyParser(keyparser.BaseKeyParser):
"""KeyParser for set_mark and jump_mark mode.
"""KeyParser for modes that record a register key.
Attributes:
_mode: Either KeyMode.set_mark or KeyMode.jump_mark.
_mode: One of KeyMode.set_mark, KeyMode.jump_mark, KeyMode.record_macro
and KeyMode.run_macro.
"""
def __init__(self, win_id, mode, parent=None):
@ -278,7 +280,7 @@ class MarkKeyParser(keyparser.BaseKeyParser):
self._mode = mode
def handle(self, e):
"""Override handle to always match the next key and create a mark.
"""Override handle to always match the next key and use the register.
Args:
e: the KeyPressEvent from Qt.
@ -299,18 +301,22 @@ class MarkKeyParser(keyparser.BaseKeyParser):
tabbed_browser.set_mark(key)
elif self._mode == usertypes.KeyMode.jump_mark:
tabbed_browser.jump_mark(key)
elif self._mode == usertypes.KeyMode.record_macro:
utilcmds.record_macro(key)
elif self._mode == usertypes.KeyMode.run_macro:
utilcmds.run_macro(self._win_id, key)
else:
raise ValueError("{} is not a valid mark mode".format(self._mode))
raise ValueError("{} is not a valid register key".format(self._mode))
self.request_leave.emit(self._mode, "valid mark key")
self.request_leave.emit(self._mode, "valid register key")
return True
@pyqtSlot(str)
def on_keyconfig_changed(self, mode):
"""MarkKeyParser has no config section (no bindable keys)."""
"""RegisterKeyParser has no config section (no bindable keys)."""
pass
def execute(self, cmdstr, _keytype, count=None):
"""Should never be called on MarkKeyParser."""
"""Should never be called on RegisterKeyParser."""
assert False

View File

@ -234,42 +234,57 @@ def repeat_command(win_id, count=None):
commandrunner.run(cmd[0], count if count is not None else cmd[1])
@cmdutils.register(maxsplit=0)
@cmdutils.argument('name')
def record_macro(name=""):
@cmdutils.register(instance='mode-manager', name='record-macro', scope='window')
@cmdutils.argument('register')
def record_macro_command(self, register=None):
"""Start or stop recording a macro.
Args:
name: Which name to give the macro.
register: Which register to store the macro in.
"""
if runners.recording_macro is None:
message.info("Defining macro...")
runners.macro[name] = []
runners.recording_macro = name
elif runners.recording_macro == name:
message.info("Macro defined.")
runners.recording_macro = None
if register is None:
self.enter(usertypes.KeyMode.record_macro, 'record_macro')
else:
record_macro(register)
else:
raise cmdexc.CommandError(
"Already recording macro '{}'".format(runners.recording_macro))
message.info("Macro recorded.")
runners.recording_macro = None
@cmdutils.register(maxsplit=0)
def record_macro(register):
"""Start recording a macro."""
message.info("Recording macro...")
runners.macro[register] = []
runners.recording_macro = register
@cmdutils.register(instance='mode-manager', name='run-macro', scope='window')
@cmdutils.argument('win_id', win_id=True)
@cmdutils.argument('count', count=True)
@cmdutils.argument('name')
def run_macro(win_id, count=1, name=""):
@cmdutils.argument('register')
def run_macro_command(self, win_id, count=1, register=None):
"""Run a recorded macro.
Args:
count: How many times to run the macro.
name: Which macro to run.
register: Which macro to run.
"""
if name not in runners.macro:
raise cmdexc.CommandError("No macro defined!")
runners.macro_count[win_id] = count
if register is None:
self.enter(usertypes.KeyMode.run_macro, 'run_macro')
else:
run_macro(win_id, register)
def run_macro(win_id, register):
"""Run a recorded macro."""
if register not in runners.macro:
raise cmdexc.CommandError(
"No macro recorded in '{}'!".format(register))
commandrunner = runners.CommandRunner(win_id)
for _ in range(count):
for cmd in runners.macro[name]:
for _ in range(runners.macro_count[win_id]):
for cmd in runners.macro[register]:
commandrunner.run(*cmd)

View File

@ -233,7 +233,7 @@ ClickTarget = enum('ClickTarget', ['normal', 'tab', 'tab_bg', 'window',
# Key input modes
KeyMode = enum('KeyMode', ['normal', 'hint', 'command', 'yesno', 'prompt',
'insert', 'passthrough', 'caret', 'set_mark',
'jump_mark'])
'jump_mark', 'record_macro', 'run_macro'])
# Available command completions

View File

@ -121,11 +121,11 @@ Feature: Keyboard input
When I open data/keyinput/log.html
And I set general -> log-javascript-console to info
And I set input -> forward-unbound-keys to all
And I press the key "q"
And I press the key ","
And I press the key "<F1>"
# q
Then the javascript message "key press: 81" should be logged
And the javascript message "key release: 81" should be logged
# ,
Then the javascript message "key press: 188" should be logged
And the javascript message "key release: 188" should be logged
# <F1>
And the javascript message "key press: 112" should be logged
And the javascript message "key release: 112" should be logged

View File

@ -625,18 +625,20 @@ Feature: Various utility commands.
Scenario: Recording a simple macro
Given I open data/scroll/simple.html
And I run :tab-only
When I run :scroll down with count 5
When I run :scroll down with count 6
And I run :record-macro
And I press the key "a"
And I run :scroll up
And I run :scroll up
And I run :record-macro
And I run :run-macro with count 2
And I press the key "a"
Then the page should not be scrolled
Scenario: Recording a named macro
Given I open data/scroll/simple.html
And I run :tab-only
When I run :scroll down with count 5
When I run :scroll down with count 6
And I run :record-macro foo
And I run :scroll up
And I run :scroll up