Add open_editor command

This commit is contained in:
Florian Bruhin 2014-04-29 18:00:22 +02:00
parent 6420a7abfc
commit bc02df0bde
4 changed files with 111 additions and 2 deletions

View File

@ -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...")

View File

@ -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.

View File

@ -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(

View File

@ -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