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

View File

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

View File

@ -78,8 +78,10 @@ def init(win_id, parent):
warn=False), warn=False),
KM.yesno: modeparsers.PromptKeyParser(win_id, modeman), KM.yesno: modeparsers.PromptKeyParser(win_id, modeman),
KM.caret: modeparsers.CaretKeyParser(win_id, modeman), KM.caret: modeparsers.CaretKeyParser(win_id, modeman),
KM.set_mark: modeparsers.MarkKeyParser(win_id, KM.set_mark, modeman), KM.set_mark: modeparsers.RegisterKeyParser(win_id, KM.set_mark, modeman),
KM.jump_mark: modeparsers.MarkKeyParser(win_id, KM.jump_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) objreg.register('keyparsers', keyparsers, scope='window', window=win_id)
modeman.destroyed.connect( modeman.destroyed.connect(

View File

@ -27,6 +27,7 @@ from PyQt5.QtCore import pyqtSlot, Qt
from qutebrowser.config import config from qutebrowser.config import config
from qutebrowser.keyinput import keyparser from qutebrowser.keyinput import keyparser
from qutebrowser.misc import utilcmds
from qutebrowser.utils import usertypes, log, objreg, utils from qutebrowser.utils import usertypes, log, objreg, utils
@ -264,12 +265,13 @@ class CaretKeyParser(keyparser.CommandKeyParser):
self.read_config('caret') 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: 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): def __init__(self, win_id, mode, parent=None):
@ -278,7 +280,7 @@ class MarkKeyParser(keyparser.BaseKeyParser):
self._mode = mode self._mode = mode
def handle(self, e): 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: Args:
e: the KeyPressEvent from Qt. e: the KeyPressEvent from Qt.
@ -299,18 +301,22 @@ class MarkKeyParser(keyparser.BaseKeyParser):
tabbed_browser.set_mark(key) tabbed_browser.set_mark(key)
elif self._mode == usertypes.KeyMode.jump_mark: elif self._mode == usertypes.KeyMode.jump_mark:
tabbed_browser.jump_mark(key) 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: 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 return True
@pyqtSlot(str) @pyqtSlot(str)
def on_keyconfig_changed(self, mode): def on_keyconfig_changed(self, mode):
"""MarkKeyParser has no config section (no bindable keys).""" """RegisterKeyParser has no config section (no bindable keys)."""
pass pass
def execute(self, cmdstr, _keytype, count=None): def execute(self, cmdstr, _keytype, count=None):
"""Should never be called on MarkKeyParser.""" """Should never be called on RegisterKeyParser."""
assert False 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]) commandrunner.run(cmd[0], count if count is not None else cmd[1])
@cmdutils.register(maxsplit=0) @cmdutils.register(instance='mode-manager', name='record-macro', scope='window')
@cmdutils.argument('name') @cmdutils.argument('register')
def record_macro(name=""): def record_macro_command(self, register=None):
"""Start or stop recording a macro. """Start or stop recording a macro.
Args: Args:
name: Which name to give the macro. register: Which register to store the macro in.
""" """
if runners.recording_macro is None: if runners.recording_macro is None:
message.info("Defining macro...") if register is None:
runners.macro[name] = [] self.enter(usertypes.KeyMode.record_macro, 'record_macro')
runners.recording_macro = name
elif runners.recording_macro == name:
message.info("Macro defined.")
runners.recording_macro = None
else: else:
raise cmdexc.CommandError( record_macro(register)
"Already recording macro '{}'".format(runners.recording_macro)) else:
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('win_id', win_id=True)
@cmdutils.argument('count', count=True) @cmdutils.argument('count', count=True)
@cmdutils.argument('name') @cmdutils.argument('register')
def run_macro(win_id, count=1, name=""): def run_macro_command(self, win_id, count=1, register=None):
"""Run a recorded macro. """Run a recorded macro.
Args: Args:
count: How many times to run the macro. count: How many times to run the macro.
name: Which macro to run. register: Which macro to run.
""" """
if name not in runners.macro: runners.macro_count[win_id] = count
raise cmdexc.CommandError("No macro defined!") 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) commandrunner = runners.CommandRunner(win_id)
for _ in range(count): for _ in range(runners.macro_count[win_id]):
for cmd in runners.macro[name]: for cmd in runners.macro[register]:
commandrunner.run(*cmd) commandrunner.run(*cmd)

View File

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

View File

@ -121,11 +121,11 @@ Feature: Keyboard input
When I open data/keyinput/log.html When I open data/keyinput/log.html
And I set general -> log-javascript-console to info And I set general -> log-javascript-console to info
And I set input -> forward-unbound-keys to all 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>" And I press the key "<F1>"
# q # ,
Then the javascript message "key press: 81" should be logged Then the javascript message "key press: 188" should be logged
And the javascript message "key release: 81" should be logged And the javascript message "key release: 188" should be logged
# <F1> # <F1>
And the javascript message "key press: 112" should be logged And the javascript message "key press: 112" should be logged
And the javascript message "key release: 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 Scenario: Recording a simple macro
Given I open data/scroll/simple.html Given I open data/scroll/simple.html
And I run :tab-only 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 run :record-macro
And I press the key "a"
And I run :scroll up And I run :scroll up
And I run :scroll up And I run :scroll up
And I run :record-macro And I run :record-macro
And I run :run-macro with count 2 And I run :run-macro with count 2
And I press the key "a"
Then the page should not be scrolled Then the page should not be scrolled
Scenario: Recording a named macro Scenario: Recording a named macro
Given I open data/scroll/simple.html Given I open data/scroll/simple.html
And I run :tab-only 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 :record-macro foo
And I run :scroll up And I run :scroll up
And I run :scroll up And I run :scroll up