Add open_editor command
This commit is contained in:
parent
6420a7abfc
commit
bc02df0bde
@ -17,14 +17,21 @@
|
||||
|
||||
"""The main tabbed browser widget."""
|
||||
|
||||
import os
|
||||
import logging
|
||||
from tempfile import mkstemp
|
||||
from functools import partial
|
||||
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
from PyQt5.QtCore import pyqtSlot, Qt, QObject
|
||||
from PyQt5.QtCore import pyqtSlot, Qt, QObject, QProcess
|
||||
from PyQt5.QtGui import QClipboard
|
||||
from PyQt5.QtPrintSupport import QPrinter, QPrintDialog, QPrintPreviewDialog
|
||||
|
||||
import qutebrowser.utils.url as urlutils
|
||||
import qutebrowser.utils.message as message
|
||||
import qutebrowser.commands.utils as cmdutils
|
||||
import qutebrowser.utils.webelem as webelem
|
||||
import qutebrowser.config.config as config
|
||||
|
||||
|
||||
class CurCommandDispatcher(QObject):
|
||||
@ -331,3 +338,51 @@ class CurCommandDispatcher(QObject):
|
||||
"""
|
||||
tab = self._tabs.currentWidget()
|
||||
tab.zoom(-count)
|
||||
|
||||
@cmdutils.register(instance='mainwindow.tabs.cur', modes=['insert'],
|
||||
name='open_editor', hide=True)
|
||||
def editor(self):
|
||||
"""Open an external editor with the current form field."""
|
||||
frame = self._tabs.currentWidget().page_.currentFrame()
|
||||
elem = frame.findFirstElement(webelem.SELECTORS['editable_focused'])
|
||||
if elem.isNull():
|
||||
message.error("No editable element focused!")
|
||||
return
|
||||
oshandle, filename = mkstemp(text=True)
|
||||
text = elem.evaluateJavaScript('this.value')
|
||||
if text:
|
||||
with open(filename, 'w') as f:
|
||||
f.write(text)
|
||||
proc = QProcess(self)
|
||||
proc.finished.connect(partial(self.on_editor_closed, elem, oshandle,
|
||||
filename))
|
||||
editor = config.get('general', 'editor')
|
||||
executable = editor[0]
|
||||
args = [arg.replace('{}', filename) for arg in editor[1:]]
|
||||
logging.debug("Calling \"{}\" with args {}".format(executable, args))
|
||||
proc.start(executable, args)
|
||||
|
||||
def on_editor_closed(self, elem, oshandle, filename, exitcode,
|
||||
exitstatus):
|
||||
"""Gets called by QProcess when the editor was closed.
|
||||
|
||||
Writes the editor text into the form field.
|
||||
"""
|
||||
logging.debug("Editor closed")
|
||||
if exitcode != 0 or exitstatus != QProcess.NormalExit:
|
||||
message.error("Editor did quit abnormally (status {})!".format(
|
||||
exitcode))
|
||||
return
|
||||
if elem.isNull():
|
||||
message.error("Element vanished while editing!")
|
||||
return
|
||||
with open(filename, 'r') as f:
|
||||
text = ''.join(f.readlines())
|
||||
text = webelem.javascript_escape(text)
|
||||
logging.debug("Read back: {}".format(text))
|
||||
elem.evaluateJavaScript("this.value='{}'".format(text))
|
||||
os.close(oshandle)
|
||||
try:
|
||||
os.remove(filename)
|
||||
except PermissionError:
|
||||
message.error("Failed to delete tempfile...")
|
||||
|
@ -17,6 +17,8 @@
|
||||
|
||||
"""Setting options used for qutebrowser."""
|
||||
|
||||
import shlex
|
||||
|
||||
from PyQt5.QtGui import QColor
|
||||
|
||||
import qutebrowser.commands.utils as cmdutils
|
||||
@ -168,6 +170,29 @@ class String(BaseType):
|
||||
self.maxlen))
|
||||
|
||||
|
||||
class ShellCommand(String):
|
||||
|
||||
"""A shellcommand which is split via shlex.
|
||||
|
||||
Attributes:
|
||||
placeholder: If there should be a placeholder.
|
||||
"""
|
||||
|
||||
typestr = 'shell-cmd'
|
||||
|
||||
def __init__(self, placeholder=False):
|
||||
self.placeholder = placeholder
|
||||
super().__init__()
|
||||
|
||||
def validate(self, value):
|
||||
super().validate(value)
|
||||
if self.placeholder and '{}' not in value:
|
||||
raise ValidationError(value, "needs to contain a {}-placeholder.")
|
||||
|
||||
def transform(self, value):
|
||||
return shlex.split(value)
|
||||
|
||||
|
||||
class Bool(BaseType):
|
||||
|
||||
"""Base class for a boolean setting.
|
||||
|
@ -84,7 +84,9 @@ SECTION_DESC = {
|
||||
"Keybindings for insert mode.\n"
|
||||
"Since normal keypresses are passed through, only special keys are "
|
||||
"supported in this mode.\n"
|
||||
"An useful command to map here is the hidden command leave_mode."),
|
||||
"Useful hidden commands to map in this section:\n"
|
||||
" open_editor: Open a texteditor with the focused field.\n"
|
||||
" leave_mode: Leave the command mode."),
|
||||
'keybind.hint': (
|
||||
"Keybindings for hint mode.\n"
|
||||
"Since normal keypresses are passed through, only special keys are "
|
||||
@ -177,6 +179,11 @@ DATA = OrderedDict([
|
||||
('background-tabs',
|
||||
SettingValue(types.Bool(), 'false'),
|
||||
"Whether to open new tabs (middleclick/ctrl+click) in background"),
|
||||
|
||||
('editor',
|
||||
SettingValue(types.ShellCommand(placeholder=True), 'gvim -f "{}"'),
|
||||
"The editor (and arguments) to use for the open_editor binding. "
|
||||
"Use {} for the filename. Gets split via shutils."),
|
||||
)),
|
||||
|
||||
('completion', sect.KeyValue(
|
||||
@ -468,6 +475,7 @@ DATA = OrderedDict([
|
||||
('keybind.insert', sect.ValueList(
|
||||
types.KeyBindingName(), types.KeyBinding(),
|
||||
('<Escape>', 'leave_mode'),
|
||||
('<Ctrl-E>', 'open_editor'),
|
||||
)),
|
||||
|
||||
('keybind.hint', sect.ValueList(
|
||||
|
@ -72,3 +72,24 @@ def is_visible(e, frame=None):
|
||||
# out of screen
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def javascript_escape(text):
|
||||
"""Escapes special values in strings.
|
||||
|
||||
This maybe makes them work with QWebElement::evaluateJavaScript.
|
||||
Maybe.
|
||||
"""
|
||||
# This is a list of tuples because order matters, and using OrderedDict
|
||||
# makes no sense because we don't actually need dict-like properties.
|
||||
replacements = [
|
||||
('\\', r'\\'),
|
||||
('\n', r'\n'),
|
||||
('\t', r'\t'),
|
||||
("'", r"\'"),
|
||||
('"', r'\"'),
|
||||
]
|
||||
text = text.rstrip('\n')
|
||||
for orig, repl in replacements:
|
||||
text = text.replace(orig, repl)
|
||||
return text
|
||||
|
Loading…
Reference in New Issue
Block a user