Lots of fixes for new command system.
Squashed commit: - Fix getting current URL - Get rid of *args for hints. - Make enums work. - Fix moving commands to utilcmds. - Fix enums in argparse - Fix arg splitting for hints. - Fix default enum args. - Fix argument splitting for hints if None is given. - Fix set_cmd_text with flags and fix {url}. - Fix unittests - Fix tuple types for arguments. - Fix scroll-page. - Fix lint - Fix open_target. - Others
This commit is contained in:
parent
d836e26107
commit
57d51ad9bb
3
.flake8
3
.flake8
@ -11,7 +11,8 @@
|
|||||||
# E222: Multiple spaces after operator
|
# E222: Multiple spaces after operator
|
||||||
# F811: Redifiniton
|
# F811: Redifiniton
|
||||||
# W292: No newline at end of file
|
# W292: No newline at end of file
|
||||||
|
# E701: multiple statements on one line
|
||||||
# E702: multiple statements on one line
|
# E702: multiple statements on one line
|
||||||
ignore=E241,E265,F401,E501,F821,F841,E222,F811,W292,E702
|
ignore=E241,E265,F401,E501,F821,F841,E222,F811,W292,E701,E702
|
||||||
max_complexity = 12
|
max_complexity = 12
|
||||||
exclude = ez_setup.py
|
exclude = ez_setup.py
|
||||||
|
@ -233,7 +233,7 @@ class CommandDispatcher:
|
|||||||
else:
|
else:
|
||||||
diag = QPrintDialog()
|
diag = QPrintDialog()
|
||||||
diag.setAttribute(Qt.WA_DeleteOnClose)
|
diag.setAttribute(Qt.WA_DeleteOnClose)
|
||||||
diag.open(lambda: tab.print(printdiag.printer()))
|
diag.open(lambda: tab.print(diag.printer()))
|
||||||
|
|
||||||
@cmdutils.register(instance='mainwindow.tabs.cmd')
|
@cmdutils.register(instance='mainwindow.tabs.cmd')
|
||||||
def back(self, count=1):
|
def back(self, count=1):
|
||||||
@ -257,7 +257,7 @@ class CommandDispatcher:
|
|||||||
|
|
||||||
@cmdutils.register(instance='mainwindow.tabs.cmd')
|
@cmdutils.register(instance='mainwindow.tabs.cmd')
|
||||||
def hint(self, group=webelem.Group.all, target=hints.Target.normal,
|
def hint(self, group=webelem.Group.all, target=hints.Target.normal,
|
||||||
*args : {'nargs': '*'}):
|
args=None):
|
||||||
"""Start hinting.
|
"""Start hinting.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -286,7 +286,7 @@ class CommandDispatcher:
|
|||||||
link.
|
link.
|
||||||
- `spawn`: Spawn a command.
|
- `spawn`: Spawn a command.
|
||||||
|
|
||||||
*args: Arguments for spawn/userscript/fill.
|
args: Arguments for spawn/userscript/fill.
|
||||||
|
|
||||||
- With `spawn`: The executable and arguments to spawn.
|
- With `spawn`: The executable and arguments to spawn.
|
||||||
`{hint-url}` will get replaced by the selected
|
`{hint-url}` will get replaced by the selected
|
||||||
@ -301,7 +301,7 @@ class CommandDispatcher:
|
|||||||
if frame is None:
|
if frame is None:
|
||||||
raise cmdexc.CommandError("No frame focused!")
|
raise cmdexc.CommandError("No frame focused!")
|
||||||
widget.hintmanager.start(frame, self._tabs.current_url(), group,
|
widget.hintmanager.start(frame, self._tabs.current_url(), group,
|
||||||
target, *args)
|
target, args)
|
||||||
|
|
||||||
@cmdutils.register(instance='mainwindow.tabs.cmd', hide=True)
|
@cmdutils.register(instance='mainwindow.tabs.cmd', hide=True)
|
||||||
def follow_hint(self):
|
def follow_hint(self):
|
||||||
@ -364,7 +364,7 @@ class CommandDispatcher:
|
|||||||
Qt.Horizontal if horizontal else Qt.Vertical)
|
Qt.Horizontal if horizontal else Qt.Vertical)
|
||||||
|
|
||||||
@cmdutils.register(instance='mainwindow.tabs.cmd', hide=True)
|
@cmdutils.register(instance='mainwindow.tabs.cmd', hide=True)
|
||||||
def scroll_page(self, x : int, y : int, count=1):
|
def scroll_page(self, x: float, y: float, count=1):
|
||||||
"""Scroll the frame page-wise.
|
"""Scroll the frame page-wise.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -402,7 +402,8 @@ class CommandDispatcher:
|
|||||||
target = "clipboard"
|
target = "clipboard"
|
||||||
log.misc.debug("Yanking to {}: '{}'".format(target, s))
|
log.misc.debug("Yanking to {}: '{}'".format(target, s))
|
||||||
clipboard.setText(s, mode)
|
clipboard.setText(s, mode)
|
||||||
message.info("URL yanked to {}".format(target))
|
what = 'Title' if title else 'URL'
|
||||||
|
message.info("{} yanked to {}".format(what, target))
|
||||||
|
|
||||||
@cmdutils.register(instance='mainwindow.tabs.cmd')
|
@cmdutils.register(instance='mainwindow.tabs.cmd')
|
||||||
def zoom_in(self, count=1):
|
def zoom_in(self, count=1):
|
||||||
@ -518,7 +519,7 @@ class CommandDispatcher:
|
|||||||
widget.openurl(url)
|
widget.openurl(url)
|
||||||
|
|
||||||
@cmdutils.register(instance='mainwindow.tabs.cmd')
|
@cmdutils.register(instance='mainwindow.tabs.cmd')
|
||||||
def tab_focus(self, index : int = None, count=None):
|
def tab_focus(self, index: (int, 'last')=None, count=None):
|
||||||
"""Select the tab given as argument/[count].
|
"""Select the tab given as argument/[count].
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
"""A HintManager to draw hints over links."""
|
"""A HintManager to draw hints over links."""
|
||||||
|
|
||||||
import math
|
import math
|
||||||
|
import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
import collections
|
import collections
|
||||||
|
|
||||||
@ -472,7 +473,7 @@ class HintManager(QObject):
|
|||||||
self.openurl.emit(url, newtab)
|
self.openurl.emit(url, newtab)
|
||||||
|
|
||||||
def start(self, mainframe, baseurl, group=webelem.Group.all,
|
def start(self, mainframe, baseurl, group=webelem.Group.all,
|
||||||
target=Target.normal, *args):
|
target=Target.normal, args=None):
|
||||||
"""Start hinting.
|
"""Start hinting.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -480,7 +481,7 @@ class HintManager(QObject):
|
|||||||
baseurl: URL of the current page.
|
baseurl: URL of the current page.
|
||||||
group: Which group of elements to hint.
|
group: Which group of elements to hint.
|
||||||
target: What to do with the link. See attribute docstring.
|
target: What to do with the link. See attribute docstring.
|
||||||
*args: Arguments for userscript/download
|
args: Arguments for userscript/download
|
||||||
|
|
||||||
Emit:
|
Emit:
|
||||||
hint_strings_updated: Emitted to update keypraser.
|
hint_strings_updated: Emitted to update keypraser.
|
||||||
@ -493,14 +494,13 @@ class HintManager(QObject):
|
|||||||
# on_mode_left, we are extra careful here.
|
# on_mode_left, we are extra careful here.
|
||||||
raise ValueError("start() was called with frame=None")
|
raise ValueError("start() was called with frame=None")
|
||||||
if target in (Target.userscript, Target.spawn, Target.fill):
|
if target in (Target.userscript, Target.spawn, Target.fill):
|
||||||
if not args:
|
if args is None:
|
||||||
raise cmdexc.CommandError(
|
raise cmdexc.CommandError(
|
||||||
"Additional arguments are required with target "
|
"'args' is required with target userscript/spawn/fill.")
|
||||||
"userscript/spawn/fill.")
|
|
||||||
else:
|
else:
|
||||||
if args:
|
if args is not None:
|
||||||
raise cmdexc.CommandError(
|
raise cmdexc.CommandError(
|
||||||
"Arguments are only allowed with target userscript/spawn.")
|
"'args' is only allowed with target userscript/spawn.")
|
||||||
elems = []
|
elems = []
|
||||||
ctx = HintContext()
|
ctx = HintContext()
|
||||||
ctx.frames = webelem.get_child_frames(mainframe)
|
ctx.frames = webelem.get_child_frames(mainframe)
|
||||||
@ -514,7 +514,13 @@ class HintManager(QObject):
|
|||||||
raise cmdexc.CommandError("No elements found.")
|
raise cmdexc.CommandError("No elements found.")
|
||||||
ctx.target = target
|
ctx.target = target
|
||||||
ctx.baseurl = baseurl
|
ctx.baseurl = baseurl
|
||||||
ctx.args = args
|
if args is None:
|
||||||
|
ctx.args = None
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
ctx.args = shlex.split(args)
|
||||||
|
except ValueError as e:
|
||||||
|
raise cmdexc.CommandError("Could not split args: {}".format(e))
|
||||||
message.instance().set_text(self.HINT_TEXTS[target])
|
message.instance().set_text(self.HINT_TEXTS[target])
|
||||||
strings = self._hint_strings(visible_elems)
|
strings = self._hint_strings(visible_elems)
|
||||||
for e, string in zip(visible_elems, strings):
|
for e, string in zip(visible_elems, strings):
|
||||||
|
@ -19,8 +19,12 @@
|
|||||||
|
|
||||||
"""argparse.ArgumentParser subclass to parse qutebrowser commands."""
|
"""argparse.ArgumentParser subclass to parse qutebrowser commands."""
|
||||||
|
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
|
from qutebrowser.commands import cmdexc
|
||||||
|
from qutebrowser.utils import utils
|
||||||
|
|
||||||
|
|
||||||
class ArgumentParserError(Exception):
|
class ArgumentParserError(Exception):
|
||||||
|
|
||||||
@ -29,6 +33,8 @@ class ArgumentParserError(Exception):
|
|||||||
|
|
||||||
class ArgumentParser(argparse.ArgumentParser):
|
class ArgumentParser(argparse.ArgumentParser):
|
||||||
|
|
||||||
|
"""Subclass ArgumentParser to be more suitable for runtime parsing."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__(add_help=False)
|
super().__init__(add_help=False)
|
||||||
|
|
||||||
@ -37,4 +43,46 @@ class ArgumentParser(argparse.ArgumentParser):
|
|||||||
'Status: {}, message: {}'.format(status, msg))
|
'Status: {}, message: {}'.format(status, msg))
|
||||||
|
|
||||||
def error(self, msg):
|
def error(self, msg):
|
||||||
raise ArgumentParserError(msg)
|
raise ArgumentParserError(msg[0].upper() + msg[1:])
|
||||||
|
|
||||||
|
|
||||||
|
def enum_getter(enum):
|
||||||
|
"""Function factory to get an enum getter."""
|
||||||
|
|
||||||
|
def _get_enum_item(key):
|
||||||
|
"""Helper function to get an enum item.
|
||||||
|
|
||||||
|
Passes through existing items unmodified.
|
||||||
|
"""
|
||||||
|
if isinstance(key, enum):
|
||||||
|
return key
|
||||||
|
try:
|
||||||
|
return enum[key.replace('-', '_')]
|
||||||
|
except KeyError:
|
||||||
|
raise cmdexc.ArgumentTypeError("Invalid value {}.".format(key))
|
||||||
|
|
||||||
|
return _get_enum_item
|
||||||
|
|
||||||
|
|
||||||
|
def multitype_conv(tpl):
|
||||||
|
"""Function factory to get a type converter for a choice of types."""
|
||||||
|
|
||||||
|
def _convert(value):
|
||||||
|
"""Convert a value according to an iterable of possible arg types."""
|
||||||
|
for typ in tpl:
|
||||||
|
if isinstance(typ, str):
|
||||||
|
if value == typ:
|
||||||
|
return value
|
||||||
|
elif utils.is_enum(typ):
|
||||||
|
return enum_getter(typ)(value)
|
||||||
|
elif callable(typ):
|
||||||
|
# int, float, etc.
|
||||||
|
if isinstance(value, typ):
|
||||||
|
return value
|
||||||
|
try:
|
||||||
|
return typ(value)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
raise cmdexc.ArgumentTypeError('Invalid value {}.'.format(value))
|
||||||
|
|
||||||
|
return _convert
|
||||||
|
@ -49,6 +49,13 @@ class ArgumentCountError(CommandMetaError):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ArgumentTypeError(CommandMetaError):
|
||||||
|
|
||||||
|
"""Raised when an argument had an invalid type."""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class PrerequisitesError(CommandMetaError):
|
class PrerequisitesError(CommandMetaError):
|
||||||
|
|
||||||
"""Raised when a cmd can't be used because some prerequisites aren't met.
|
"""Raised when a cmd can't be used because some prerequisites aren't met.
|
||||||
|
@ -23,11 +23,11 @@ Module attributes:
|
|||||||
cmd_dict: A mapping from command-strings to command objects.
|
cmd_dict: A mapping from command-strings to command objects.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import enum
|
|
||||||
import inspect
|
import inspect
|
||||||
import collections
|
import collections
|
||||||
|
|
||||||
from qutebrowser.utils import usertypes, qtutils, log, debug
|
from qutebrowser.utils import usertypes, qtutils, log, utils
|
||||||
|
from qutebrowser.utils import debug as debugutils
|
||||||
from qutebrowser.commands import command, cmdexc, argparser
|
from qutebrowser.commands import command, cmdexc, argparser
|
||||||
|
|
||||||
cmd_dict = {}
|
cmd_dict = {}
|
||||||
@ -160,15 +160,16 @@ class register: # pylint: disable=invalid-name
|
|||||||
"""
|
"""
|
||||||
names = self._get_names(func)
|
names = self._get_names(func)
|
||||||
log.commands.vdebug("Registering command {}".format(names[0]))
|
log.commands.vdebug("Registering command {}".format(names[0]))
|
||||||
if any(name in cmd_dict for name in names):
|
for name in names:
|
||||||
|
if name in cmd_dict:
|
||||||
raise ValueError("{} is already registered!".format(name))
|
raise ValueError("{} is already registered!".format(name))
|
||||||
has_count, desc, parser = self._inspect_func(func)
|
has_count, desc, parser, type_conv = self._inspect_func(func)
|
||||||
cmd = command.Command(
|
cmd = command.Command(
|
||||||
name=names[0], split=self.split, hide=self.hide, count=has_count,
|
name=names[0], split=self.split, hide=self.hide, count=has_count,
|
||||||
desc=desc, instance=self.instance, handler=func,
|
desc=desc, instance=self.instance, handler=func,
|
||||||
completion=self.completion, modes=self.modes,
|
completion=self.completion, modes=self.modes,
|
||||||
not_modes=self.not_modes, needs_js=self.needs_js, debug=self.debug,
|
not_modes=self.not_modes, needs_js=self.needs_js,
|
||||||
parser=parser)
|
is_debug=self.debug, parser=parser, type_conv=type_conv)
|
||||||
for name in names:
|
for name in names:
|
||||||
cmd_dict[name] = cmd
|
cmd_dict[name] = cmd
|
||||||
return func
|
return func
|
||||||
@ -202,15 +203,17 @@ class register: # pylint: disable=invalid-name
|
|||||||
func: The function to look at.
|
func: The function to look at.
|
||||||
|
|
||||||
Return:
|
Return:
|
||||||
A (has_count, desc, parser) tuple.
|
A (has_count, desc, parser, type_conv) tuple.
|
||||||
has_count: Whether the command supports a count.
|
has_count: Whether the command supports a count.
|
||||||
desc: The description of the command.
|
desc: The description of the command.
|
||||||
parser: The ArgumentParser to use when parsing the commandline.
|
parser: The ArgumentParser to use when parsing the commandline.
|
||||||
|
type_conv: A mapping of args to type converter callables.
|
||||||
"""
|
"""
|
||||||
|
type_conv = {}
|
||||||
signature = inspect.signature(func)
|
signature = inspect.signature(func)
|
||||||
if 'self' in signature.parameters and self.instance is None:
|
if 'self' in signature.parameters and self.instance is None:
|
||||||
raise ValueError("{} is a class method, but instance was not "
|
raise ValueError("{} is a class method, but instance was not "
|
||||||
"given!".format(mainname))
|
"given!".format(self.name[0]))
|
||||||
has_count = 'count' in signature.parameters
|
has_count = 'count' in signature.parameters
|
||||||
parser = argparser.ArgumentParser()
|
parser = argparser.ArgumentParser()
|
||||||
if func.__doc__ is not None:
|
if func.__doc__ is not None:
|
||||||
@ -224,43 +227,64 @@ class register: # pylint: disable=invalid-name
|
|||||||
args = []
|
args = []
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
annotation_info = self._parse_annotation(param)
|
annotation_info = self._parse_annotation(param)
|
||||||
if annotation_info.typ is not None:
|
kwargs.update(self._param_to_argparse_kw(
|
||||||
typ = annotation_info.typ
|
param, annotation_info))
|
||||||
else:
|
|
||||||
typ = self._infer_type(param)
|
|
||||||
kwargs.update(self._type_to_argparse(typ))
|
|
||||||
kwargs.update(annotation_info.kwargs)
|
kwargs.update(annotation_info.kwargs)
|
||||||
if (param.kind == inspect.Parameter.VAR_POSITIONAL and
|
args += self._param_to_argparse_pos(param, annotation_info)
|
||||||
'nargs' not in kwargs): # annotation_info overrides it
|
typ = self._get_type(param, annotation_info)
|
||||||
kwargs['nargs'] = '*'
|
if utils.is_enum(typ):
|
||||||
is_flag = typ == bool
|
type_conv[param.name] = argparser.enum_getter(typ)
|
||||||
args += self._get_argparse_args(param, annotation_info,
|
elif isinstance(typ, tuple):
|
||||||
is_flag)
|
type_conv[param.name] = argparser.multitype_conv(typ)
|
||||||
callsig = debug.format_call(parser.add_argument, args,
|
callsig = debugutils.format_call(parser.add_argument, args,
|
||||||
kwargs, full=False)
|
kwargs, full=False)
|
||||||
log.commands.vdebug('Adding arg {} of type {} -> {}'.format(
|
log.commands.vdebug('Adding arg {} of type {} -> {}'.format(
|
||||||
param.name, typ, callsig))
|
param.name, typ, callsig))
|
||||||
parser.add_argument(*args, **kwargs)
|
parser.add_argument(*args, **kwargs)
|
||||||
return has_count, desc, parser
|
return has_count, desc, parser, type_conv
|
||||||
|
|
||||||
def _get_argparse_args(self, param, annotation_info, is_flag):
|
def _param_to_argparse_pos(self, param, annotation_info):
|
||||||
"""Get a list of positional argparse arguments.
|
"""Get a list of positional argparse arguments.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
param: The inspect.Parameter instance for the current parameter.
|
param: The inspect.Parameter instance for the current parameter.
|
||||||
annotation_info: An AnnotationInfo tuple for the parameter.
|
annotation_info: An AnnotationInfo tuple for the parameter.
|
||||||
is_flag: Whether the option is a flag or not.
|
|
||||||
"""
|
"""
|
||||||
args = []
|
args = []
|
||||||
name = annotation_info.name or param.name
|
name = annotation_info.name or param.name
|
||||||
shortname = annotation_info.flag or param.name[0]
|
shortname = annotation_info.flag or param.name[0]
|
||||||
if is_flag:
|
if self._get_type(param, annotation_info) == bool:
|
||||||
args.append('--{}'.format(name))
|
args.append('--{}'.format(name))
|
||||||
args.append('-{}'.format(shortname))
|
args.append('-{}'.format(shortname))
|
||||||
else:
|
else:
|
||||||
args.append(name)
|
args.append(name)
|
||||||
return args
|
return args
|
||||||
|
|
||||||
|
def _param_to_argparse_kw(self, param, annotation_info):
|
||||||
|
"""Get argparse keyword arguments for a parameter.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
param: The inspect.Parameter object to get the args for.
|
||||||
|
annotation_info: An AnnotationInfo tuple for the parameter.
|
||||||
|
"""
|
||||||
|
kwargs = {}
|
||||||
|
typ = self._get_type(param, annotation_info)
|
||||||
|
if isinstance(typ, tuple):
|
||||||
|
pass
|
||||||
|
elif utils.is_enum(typ):
|
||||||
|
kwargs['choices'] = [e.name.replace('_', '-') for e in typ]
|
||||||
|
elif typ is bool:
|
||||||
|
kwargs['action'] = 'store_true'
|
||||||
|
elif typ is not None:
|
||||||
|
kwargs['type'] = typ
|
||||||
|
|
||||||
|
if param.kind == inspect.Parameter.VAR_POSITIONAL:
|
||||||
|
kwargs['nargs'] = '*'
|
||||||
|
elif typ is not bool and param.default is not inspect.Parameter.empty:
|
||||||
|
kwargs['default'] = param.default
|
||||||
|
kwargs['nargs'] = '?'
|
||||||
|
return kwargs
|
||||||
|
|
||||||
def _parse_annotation(self, param):
|
def _parse_annotation(self, param):
|
||||||
"""Get argparse arguments and type from a parameter annotation.
|
"""Get argparse arguments and type from a parameter annotation.
|
||||||
|
|
||||||
@ -276,8 +300,9 @@ class register: # pylint: disable=invalid-name
|
|||||||
name: The long name if overridden.
|
name: The long name if overridden.
|
||||||
"""
|
"""
|
||||||
info = {'kwargs': {}, 'typ': None, 'flag': None, 'name': None}
|
info = {'kwargs': {}, 'typ': None, 'flag': None, 'name': None}
|
||||||
log.commands.vdebug("Parsing annotation {}".format(param.annotation))
|
|
||||||
if param.annotation is not inspect.Parameter.empty:
|
if param.annotation is not inspect.Parameter.empty:
|
||||||
|
log.commands.vdebug("Parsing annotation {}".format(
|
||||||
|
param.annotation))
|
||||||
if isinstance(param.annotation, dict):
|
if isinstance(param.annotation, dict):
|
||||||
for field in ('type', 'flag', 'name'):
|
for field in ('type', 'flag', 'name'):
|
||||||
if field in param.annotation:
|
if field in param.annotation:
|
||||||
@ -288,27 +313,16 @@ class register: # pylint: disable=invalid-name
|
|||||||
info['typ'] = param.annotation
|
info['typ'] = param.annotation
|
||||||
return self.AnnotationInfo(**info)
|
return self.AnnotationInfo(**info)
|
||||||
|
|
||||||
def _infer_type(self, param):
|
def _get_type(self, param, annotation_info):
|
||||||
"""Get the type of an argument from its default value."""
|
"""Get the type of an argument from its default value or annotation.
|
||||||
if param.default is None or param.default is inspect.Parameter.empty:
|
|
||||||
|
Args:
|
||||||
|
param: The inspect.Parameter to look at.
|
||||||
|
annotation_info: An AnnotationInfo tuple which overrides the type.
|
||||||
|
"""
|
||||||
|
if annotation_info.typ is not None:
|
||||||
|
return annotation_info.typ
|
||||||
|
elif param.default is None or param.default is inspect.Parameter.empty:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
return type(param.default)
|
return type(param.default)
|
||||||
|
|
||||||
def _type_to_argparse(self, typ):
|
|
||||||
"""Get argparse keyword arguments based on a type."""
|
|
||||||
kwargs = {}
|
|
||||||
try:
|
|
||||||
is_enum = issubclass(typ, enum.Enum)
|
|
||||||
except TypeError:
|
|
||||||
is_enum = False
|
|
||||||
if isinstance(typ, tuple):
|
|
||||||
kwargs['choices'] = typ
|
|
||||||
elif is_enum:
|
|
||||||
kwargs['choices'] = [e.name.replace('_', '-') for e in typ]
|
|
||||||
elif typ is bool:
|
|
||||||
kwargs['action'] = 'store_true'
|
|
||||||
elif typ is not None:
|
|
||||||
kwargs['type'] = typ
|
|
||||||
|
|
||||||
return kwargs
|
|
||||||
|
@ -43,6 +43,7 @@ class Command:
|
|||||||
needs_js: Whether the command needs javascript enabled
|
needs_js: Whether the command needs javascript enabled
|
||||||
debug: Whether this is a debugging command (only shown with --debug).
|
debug: Whether this is a debugging command (only shown with --debug).
|
||||||
parser: The ArgumentParser to use to parse this command.
|
parser: The ArgumentParser to use to parse this command.
|
||||||
|
type_conv: A mapping of conversion functions for arguments.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# TODO:
|
# TODO:
|
||||||
@ -50,7 +51,8 @@ class Command:
|
|||||||
# this might be combined with help texts or so as well
|
# this might be combined with help texts or so as well
|
||||||
|
|
||||||
def __init__(self, name, split, hide, count, desc, instance, handler,
|
def __init__(self, name, split, hide, count, desc, instance, handler,
|
||||||
completion, modes, not_modes, needs_js, debug, parser):
|
completion, modes, not_modes, needs_js, is_debug, parser,
|
||||||
|
type_conv):
|
||||||
# I really don't know how to solve this in a better way, I tried.
|
# I really don't know how to solve this in a better way, I tried.
|
||||||
# pylint: disable=too-many-arguments
|
# pylint: disable=too-many-arguments
|
||||||
self.name = name
|
self.name = name
|
||||||
@ -64,8 +66,9 @@ class Command:
|
|||||||
self.modes = modes
|
self.modes = modes
|
||||||
self.not_modes = not_modes
|
self.not_modes = not_modes
|
||||||
self.needs_js = needs_js
|
self.needs_js = needs_js
|
||||||
self.debug = debug
|
self.debug = is_debug
|
||||||
self.parser = parser
|
self.parser = parser
|
||||||
|
self.type_conv = type_conv
|
||||||
|
|
||||||
def _check_prerequisites(self):
|
def _check_prerequisites(self):
|
||||||
"""Check if the command is permitted to run currently.
|
"""Check if the command is permitted to run currently.
|
||||||
@ -114,7 +117,7 @@ class Command:
|
|||||||
try:
|
try:
|
||||||
namespace = self.parser.parse_args(args)
|
namespace = self.parser.parse_args(args)
|
||||||
except argparser.ArgumentParserError as e:
|
except argparser.ArgumentParserError as e:
|
||||||
message.error(str(e))
|
message.error('{}: {}'.format(self.name, e))
|
||||||
return
|
return
|
||||||
|
|
||||||
for name, arg in vars(namespace).items():
|
for name, arg in vars(namespace).items():
|
||||||
@ -124,6 +127,12 @@ class Command:
|
|||||||
# FIXME: This approach is rather naive, but for now it works.
|
# FIXME: This approach is rather naive, but for now it works.
|
||||||
posargs += arg
|
posargs += arg
|
||||||
else:
|
else:
|
||||||
|
if name in self.type_conv:
|
||||||
|
# We convert enum types after getting the values from
|
||||||
|
# argparse, because argparse's choices argument is
|
||||||
|
# processed after type conversation, which is not what we
|
||||||
|
# want.
|
||||||
|
arg = self.type_conv[name](arg)
|
||||||
kwargs[name] = arg
|
kwargs[name] = arg
|
||||||
|
|
||||||
if self.instance is not None:
|
if self.instance is not None:
|
||||||
@ -140,4 +149,7 @@ class Command:
|
|||||||
self._check_prerequisites()
|
self._check_prerequisites()
|
||||||
log.commands.debug('Calling {}'.format(
|
log.commands.debug('Calling {}'.format(
|
||||||
debug.format_call(self.handler, posargs, kwargs)))
|
debug.format_call(self.handler, posargs, kwargs)))
|
||||||
|
# FIXME this won't work properly if some arguments are required to be
|
||||||
|
# positional, e.g.:
|
||||||
|
# def fun(one=True, two=False, *args)
|
||||||
self.handler(*posargs, **kwargs)
|
self.handler(*posargs, **kwargs)
|
||||||
|
@ -27,6 +27,20 @@ from qutebrowser.commands import cmdexc, cmdutils
|
|||||||
from qutebrowser.utils import message, log, utils
|
from qutebrowser.utils import message, log, utils
|
||||||
|
|
||||||
|
|
||||||
|
def replace_variables(arglist):
|
||||||
|
"""Utility function to replace variables like {url} in a list of args."""
|
||||||
|
args = []
|
||||||
|
for arg in arglist:
|
||||||
|
if arg == '{url}':
|
||||||
|
app = QCoreApplication.instance()
|
||||||
|
url = app.mainwindow.tabs.current_url().toString(
|
||||||
|
QUrl.FullyEncoded | QUrl.RemovePassword)
|
||||||
|
args.append(url)
|
||||||
|
else:
|
||||||
|
args.append(arg)
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
class SearchRunner(QObject):
|
class SearchRunner(QObject):
|
||||||
|
|
||||||
"""Run searches on webpages.
|
"""Run searches on webpages.
|
||||||
@ -219,6 +233,14 @@ class CommandRunner:
|
|||||||
else:
|
else:
|
||||||
raise cmdexc.NoSuchCommandError(
|
raise cmdexc.NoSuchCommandError(
|
||||||
'{}: no such command'.format(cmdstr))
|
'{}: no such command'.format(cmdstr))
|
||||||
|
self._split_args(argstr)
|
||||||
|
retargs = self._args[:]
|
||||||
|
if text.endswith(' '):
|
||||||
|
retargs.append('')
|
||||||
|
return [cmdstr] + retargs
|
||||||
|
|
||||||
|
def _split_args(self, argstr):
|
||||||
|
"""Split the arguments from an arg string."""
|
||||||
if argstr is None:
|
if argstr is None:
|
||||||
self._args = []
|
self._args = []
|
||||||
elif self._cmd.split:
|
elif self._cmd.split:
|
||||||
@ -244,10 +266,6 @@ class CommandRunner:
|
|||||||
# If there are only flags, we got it right on the first try
|
# If there are only flags, we got it right on the first try
|
||||||
# already.
|
# already.
|
||||||
self._args = split_args
|
self._args = split_args
|
||||||
retargs = self._args[:]
|
|
||||||
if text.endswith(' '):
|
|
||||||
retargs.append('')
|
|
||||||
return [cmdstr] + retargs
|
|
||||||
|
|
||||||
def run(self, text, count=None):
|
def run(self, text, count=None):
|
||||||
"""Parse a command from a line of text and run it.
|
"""Parse a command from a line of text and run it.
|
||||||
@ -261,15 +279,11 @@ class CommandRunner:
|
|||||||
self.run(sub, count)
|
self.run(sub, count)
|
||||||
return
|
return
|
||||||
self.parse(text)
|
self.parse(text)
|
||||||
app = QCoreApplication.instance()
|
args = replace_variables(self._args)
|
||||||
cur_url = app.mainwindow.tabs.current_url().toString(
|
|
||||||
QUrl.FullyEncoded | QUrl.RemovePassword)
|
|
||||||
self._args = [cur_url if e == '{url}' else e for e in self._args]
|
|
||||||
if count is not None:
|
if count is not None:
|
||||||
self._cmd.run(self._args, count=count)
|
self._cmd.run(args, count=count)
|
||||||
else:
|
else:
|
||||||
self._cmd.run(self._args)
|
self._cmd.run(args)
|
||||||
|
|
||||||
|
|
||||||
@pyqtSlot(str, int)
|
@pyqtSlot(str, int)
|
||||||
def run_safely(self, text, count=None):
|
def run_safely(self, text, count=None):
|
||||||
|
@ -584,11 +584,11 @@ DATA = collections.OrderedDict([
|
|||||||
('keybind', sect.ValueList(
|
('keybind', sect.ValueList(
|
||||||
typ.KeyBindingName(), typ.KeyBinding(),
|
typ.KeyBindingName(), typ.KeyBinding(),
|
||||||
('o', 'set-cmd-text ":open "'),
|
('o', 'set-cmd-text ":open "'),
|
||||||
('go', 'set-cmd-text :open {url}'),
|
('go', 'set-cmd-text ":open {url}"'),
|
||||||
('O', 'set-cmd-text ":open -t "'),
|
('O', 'set-cmd-text ":open -t "'),
|
||||||
('gO', 'set-cmd-text :open -t {url}'),
|
('gO', 'set-cmd-text ":open -t {url}"'),
|
||||||
('xo', 'set-cmd-text ":open -b "'),
|
('xo', 'set-cmd-text ":open -b "'),
|
||||||
('xO', 'set-cmd-text :open -b {url}'),
|
('xO', 'set-cmd-text ":open -b {url}"'),
|
||||||
('ga', 'open -t about:blank'),
|
('ga', 'open -t about:blank'),
|
||||||
('d', 'tab-close'),
|
('d', 'tab-close'),
|
||||||
('co', 'tab-only'),
|
('co', 'tab-only'),
|
||||||
@ -607,9 +607,9 @@ DATA = collections.OrderedDict([
|
|||||||
(';i', 'hint images'),
|
(';i', 'hint images'),
|
||||||
(';I', 'hint images tab'),
|
(';I', 'hint images tab'),
|
||||||
('.i', 'hint images tab-bg'),
|
('.i', 'hint images tab-bg'),
|
||||||
(';o', 'hint links fill :open {hint-url}'),
|
(';o', 'hint links fill ":open {hint-url}"'),
|
||||||
(';O', 'hint links fill :open -t {hint-url}'),
|
(';O', 'hint links fill ":open -t {hint-url}"'),
|
||||||
('.o', 'hint links fill :open -b {hint-url}'),
|
('.o', 'hint links fill ":open -b {hint-url}"'),
|
||||||
(';y', 'hint links yank'),
|
(';y', 'hint links yank'),
|
||||||
(';Y', 'hint links yank-primary'),
|
(';Y', 'hint links yank-primary'),
|
||||||
(';r', 'hint links rapid'),
|
(';r', 'hint links rapid'),
|
||||||
|
@ -137,13 +137,13 @@ class TestDebug(unittest.TestCase):
|
|||||||
def test_dbg_signal_eliding(self):
|
def test_dbg_signal_eliding(self):
|
||||||
"""Test eliding in dbg_signal()."""
|
"""Test eliding in dbg_signal()."""
|
||||||
self.assertEqual(debug.dbg_signal(self.signal,
|
self.assertEqual(debug.dbg_signal(self.signal,
|
||||||
[12345678901234567890123]),
|
['x' * 201]),
|
||||||
'fake(1234567890123456789\u2026)')
|
"fake('{}\u2026)".format('x' * 198))
|
||||||
|
|
||||||
def test_dbg_signal_newline(self):
|
def test_dbg_signal_newline(self):
|
||||||
"""Test dbg_signal() with a newline."""
|
"""Test dbg_signal() with a newline."""
|
||||||
self.assertEqual(debug.dbg_signal(self.signal, ['foo\nbar']),
|
self.assertEqual(debug.dbg_signal(self.signal, ['foo\nbar']),
|
||||||
'fake(foo bar)')
|
r"fake('foo\nbar')")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import enum
|
||||||
import shutil
|
import shutil
|
||||||
import unittest
|
import unittest
|
||||||
import os.path
|
import os.path
|
||||||
@ -511,5 +512,25 @@ class NormalizeTests(unittest.TestCase):
|
|||||||
self.assertEqual(utils.normalize_keystr(orig), repl)
|
self.assertEqual(utils.normalize_keystr(orig), repl)
|
||||||
|
|
||||||
|
|
||||||
|
class IsEnumTests(unittest.TestCase):
|
||||||
|
|
||||||
|
"""Test is_enum."""
|
||||||
|
|
||||||
|
def test_enum(self):
|
||||||
|
"""Test is_enum with an enum."""
|
||||||
|
e = enum.Enum('Foo', 'bar, baz')
|
||||||
|
self.assertTrue(utils.is_enum(e))
|
||||||
|
|
||||||
|
def test_class(self):
|
||||||
|
"""Test is_enum with a non-enum class."""
|
||||||
|
# pylint: disable=multiple-statements,missing-docstring
|
||||||
|
class Test: pass
|
||||||
|
self.assertFalse(utils.is_enum(Test))
|
||||||
|
|
||||||
|
def test_object(self):
|
||||||
|
"""Test is_enum with a non-enum object."""
|
||||||
|
self.assertFalse(utils.is_enum(23))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -21,58 +21,11 @@
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import types
|
|
||||||
import functools
|
import functools
|
||||||
|
|
||||||
from PyQt5.QtCore import QEvent, QCoreApplication
|
from PyQt5.QtCore import QEvent, QCoreApplication
|
||||||
|
|
||||||
from qutebrowser.utils import log, utils
|
from qutebrowser.utils import log, utils
|
||||||
from qutebrowser.commands import cmdutils
|
|
||||||
from qutebrowser.config import config, style
|
|
||||||
|
|
||||||
|
|
||||||
@cmdutils.register(debug=True)
|
|
||||||
def debug_crash(typ : ('exception', 'segfault') = 'exception'):
|
|
||||||
"""Crash for debugging purposes.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
typ: either 'exception' or 'segfault'.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
raises Exception when typ is not segfault.
|
|
||||||
segfaults when typ is (you don't say...)
|
|
||||||
"""
|
|
||||||
if typ == 'segfault':
|
|
||||||
# From python's Lib/test/crashers/bogus_code_obj.py
|
|
||||||
co = types.CodeType(0, 0, 0, 0, 0, b'\x04\x71\x00\x00', (), (), (),
|
|
||||||
'', '', 1, b'')
|
|
||||||
exec(co) # pylint: disable=exec-used
|
|
||||||
raise Exception("Segfault failed (wat.)")
|
|
||||||
else:
|
|
||||||
raise Exception("Forced crash")
|
|
||||||
|
|
||||||
|
|
||||||
@cmdutils.register(debug=True)
|
|
||||||
def debug_all_widgets():
|
|
||||||
"""Print a list of all widgets to debug log."""
|
|
||||||
s = QCoreApplication.instance().get_all_widgets()
|
|
||||||
log.misc.debug(s)
|
|
||||||
|
|
||||||
|
|
||||||
@cmdutils.register(debug=True)
|
|
||||||
def debug_all_objects():
|
|
||||||
"""Print a list of all objects to the debug log."""
|
|
||||||
s = QCoreApplication.instance().get_all_objects()
|
|
||||||
log.misc.debug(s)
|
|
||||||
|
|
||||||
|
|
||||||
@cmdutils.register(debug=True)
|
|
||||||
def debug_cache_stats():
|
|
||||||
"""Print LRU cache stats."""
|
|
||||||
config_info = config.instance().get.cache_info()
|
|
||||||
style_info = style.get_stylesheet.cache_info()
|
|
||||||
log.misc.debug('config: {}'.format(config_info))
|
|
||||||
log.misc.debug('style: {}'.format(style_info))
|
|
||||||
|
|
||||||
|
|
||||||
def log_events(klass):
|
def log_events(klass):
|
||||||
@ -211,12 +164,12 @@ def signal_name(sig):
|
|||||||
def _format_args(args=None, kwargs=None):
|
def _format_args(args=None, kwargs=None):
|
||||||
"""Format a list of arguments/kwargs to a function-call like string."""
|
"""Format a list of arguments/kwargs to a function-call like string."""
|
||||||
if args is not None:
|
if args is not None:
|
||||||
arglist = [utils.compact_text(repr(arg), 50) for arg in args]
|
arglist = [utils.compact_text(repr(arg), 200) for arg in args]
|
||||||
else:
|
else:
|
||||||
arglist = []
|
arglist = []
|
||||||
if kwargs is not None:
|
if kwargs is not None:
|
||||||
for k, v in kwargs.items():
|
for k, v in kwargs.items():
|
||||||
arglist.append('{}={}'.format(k, utils.compact_text(repr(v), 50)))
|
arglist.append('{}={}'.format(k, utils.compact_text(repr(v), 200)))
|
||||||
return ', '.join(arglist)
|
return ', '.join(arglist)
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,10 +19,11 @@
|
|||||||
|
|
||||||
"""Misc. utility commands exposed to the user."""
|
"""Misc. utility commands exposed to the user."""
|
||||||
|
|
||||||
|
import types
|
||||||
|
import functools
|
||||||
|
|
||||||
from PyQt5.QtCore import pyqtRemoveInputHook, QCoreApplication
|
|
||||||
|
|
||||||
from functools import partial
|
from PyQt5.QtCore import QCoreApplication
|
||||||
|
|
||||||
from qutebrowser.utils import usertypes, log
|
from qutebrowser.utils import usertypes, log
|
||||||
from qutebrowser.commands import runners, cmdexc, cmdutils
|
from qutebrowser.commands import runners, cmdexc, cmdutils
|
||||||
@ -58,29 +59,12 @@ def later(ms : int, *command : {'nargs': '+'}):
|
|||||||
"int representation.")
|
"int representation.")
|
||||||
_timers.append(timer)
|
_timers.append(timer)
|
||||||
cmdline = ' '.join(command)
|
cmdline = ' '.join(command)
|
||||||
timer.timeout.connect(partial(_commandrunner.run_safely, cmdline))
|
timer.timeout.connect(functools.partial(
|
||||||
|
_commandrunner.run_safely, cmdline))
|
||||||
timer.timeout.connect(lambda: _timers.remove(timer))
|
timer.timeout.connect(lambda: _timers.remove(timer))
|
||||||
timer.start()
|
timer.start()
|
||||||
|
|
||||||
|
|
||||||
@cmdutils.register(debug=True, name='debug-set-trace')
|
|
||||||
def set_trace():
|
|
||||||
"""Break into the debugger in the shell.
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
Based on http://stackoverflow.com/a/1745965/2085149
|
|
||||||
"""
|
|
||||||
if sys.stdout is not None:
|
|
||||||
sys.stdout.flush()
|
|
||||||
print()
|
|
||||||
print("When done debugging, remember to execute:")
|
|
||||||
print(" from PyQt5 import QtCore; QtCore.pyqtRestoreInputHook()")
|
|
||||||
print("before executing c(ontinue).")
|
|
||||||
pyqtRemoveInputHook()
|
|
||||||
pdb.set_trace()
|
|
||||||
|
|
||||||
|
|
||||||
@cmdutils.register(debug=True)
|
@cmdutils.register(debug=True)
|
||||||
def debug_crash(typ: ('exception', 'segfault')='exception'):
|
def debug_crash(typ: ('exception', 'segfault')='exception'):
|
||||||
"""Crash for debugging purposes.
|
"""Crash for debugging purposes.
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
import os
|
import os
|
||||||
import io
|
import io
|
||||||
import sys
|
import sys
|
||||||
|
import enum
|
||||||
import shlex
|
import shlex
|
||||||
import os.path
|
import os.path
|
||||||
import urllib.request
|
import urllib.request
|
||||||
@ -494,3 +495,11 @@ def disabled_excepthook():
|
|||||||
# unchanged. Otherwise, we reset it.
|
# unchanged. Otherwise, we reset it.
|
||||||
if sys.excepthook is sys.__excepthook__:
|
if sys.excepthook is sys.__excepthook__:
|
||||||
sys.excepthook = old_excepthook
|
sys.excepthook = old_excepthook
|
||||||
|
|
||||||
|
|
||||||
|
def is_enum(obj):
|
||||||
|
"""Check if a given object is an enum."""
|
||||||
|
try:
|
||||||
|
return issubclass(obj, enum.Enum)
|
||||||
|
except TypeError:
|
||||||
|
return False
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
"""The commandline in the statusbar."""
|
"""The commandline in the statusbar."""
|
||||||
|
|
||||||
from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt
|
from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QCoreApplication, QUrl
|
||||||
from PyQt5.QtWidgets import QSizePolicy, QApplication
|
from PyQt5.QtWidgets import QSizePolicy, QApplication
|
||||||
|
|
||||||
from qutebrowser.keyinput import modeman, modeparsers
|
from qutebrowser.keyinput import modeman, modeparsers
|
||||||
@ -161,7 +161,7 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit):
|
|||||||
self.show_cmd.emit()
|
self.show_cmd.emit()
|
||||||
|
|
||||||
@cmdutils.register(instance='mainwindow.status.cmd', name='set-cmd-text')
|
@cmdutils.register(instance='mainwindow.status.cmd', name='set-cmd-text')
|
||||||
def set_cmd_text_command(self, *strings):
|
def set_cmd_text_command(self, text):
|
||||||
"""Preset the statusbar to some text.
|
"""Preset the statusbar to some text.
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -170,9 +170,16 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit):
|
|||||||
strings which will get joined.
|
strings which will get joined.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
strings: A list of strings to set.
|
text: The commandline to set.
|
||||||
"""
|
"""
|
||||||
text = ' '.join(strings)
|
app = QCoreApplication.instance()
|
||||||
|
url = app.mainwindow.tabs.current_url().toString(
|
||||||
|
QUrl.FullyEncoded | QUrl.RemovePassword)
|
||||||
|
# FIXME we currently replace the URL in any place in the arguments,
|
||||||
|
# rather than just replacing it if it is a dedicated argument. We could
|
||||||
|
# split the args, but then trailing spaces would be lost, so I'm not
|
||||||
|
# sure what's the best thing to do here
|
||||||
|
text = text.replace('{url}', url)
|
||||||
if not text[0] in modeparsers.STARTCHARS:
|
if not text[0] in modeparsers.STARTCHARS:
|
||||||
raise cmdexc.CommandError(
|
raise cmdexc.CommandError(
|
||||||
"Invalid command text '{}'.".format(text))
|
"Invalid command text '{}'.".format(text))
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
import functools
|
import functools
|
||||||
|
|
||||||
from PyQt5.QtWidgets import QSizePolicy
|
from PyQt5.QtWidgets import QSizePolicy
|
||||||
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QSize, QTimer
|
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QSize, QTimer, QUrl
|
||||||
from PyQt5.QtGui import QIcon
|
from PyQt5.QtGui import QIcon
|
||||||
from PyQt5.QtWebKitWidgets import QWebPage
|
from PyQt5.QtWebKitWidgets import QWebPage
|
||||||
|
|
||||||
@ -205,7 +205,11 @@ class TabbedBrowser(tabwidget.TabWidget):
|
|||||||
Raise:
|
Raise:
|
||||||
CommandError if the current URL is invalid.
|
CommandError if the current URL is invalid.
|
||||||
"""
|
"""
|
||||||
url = self.currentWidget().cur_url
|
widget = self.currentWidget()
|
||||||
|
if widget is None:
|
||||||
|
url = QUrl()
|
||||||
|
else:
|
||||||
|
url = widget.cur_url
|
||||||
try:
|
try:
|
||||||
qtutils.ensure_valid(url)
|
qtutils.ensure_valid(url)
|
||||||
except qtutils.QtValueError as e:
|
except qtutils.QtValueError as e:
|
||||||
|
@ -221,20 +221,20 @@ class WebView(QWebView):
|
|||||||
e: The QMouseEvent.
|
e: The QMouseEvent.
|
||||||
"""
|
"""
|
||||||
if self._force_open_target is not None:
|
if self._force_open_target is not None:
|
||||||
self._open_target = self._force_open_target
|
self.open_target = self._force_open_target
|
||||||
self._force_open_target = None
|
self._force_open_target = None
|
||||||
log.mouse.debug("Setting force target: {}".format(
|
log.mouse.debug("Setting force target: {}".format(
|
||||||
self._open_target))
|
self.open_target))
|
||||||
elif (e.button() == Qt.MidButton or
|
elif (e.button() == Qt.MidButton or
|
||||||
e.modifiers() & Qt.ControlModifier):
|
e.modifiers() & Qt.ControlModifier):
|
||||||
if config.get('tabs', 'background-tabs'):
|
if config.get('tabs', 'background-tabs'):
|
||||||
self._open_target = usertypes.ClickTarget.tab_bg
|
self.open_target = usertypes.ClickTarget.tab_bg
|
||||||
else:
|
else:
|
||||||
self._open_target = usertypes.ClickTarget.tab
|
self.open_target = usertypes.ClickTarget.tab
|
||||||
log.mouse.debug("Middle click, setting target: {}".format(
|
log.mouse.debug("Middle click, setting target: {}".format(
|
||||||
self._open_target))
|
self.open_target))
|
||||||
else:
|
else:
|
||||||
self._open_target = usertypes.ClickTarget.normal
|
self.open_target = usertypes.ClickTarget.normal
|
||||||
log.mouse.debug("Normal click, setting normal target")
|
log.mouse.debug("Normal click, setting normal target")
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user