qutebrowser/qutebrowser/misc/editor.py

131 lines
4.8 KiB
Python
Raw Normal View History

2014-06-19 09:04:37 +02:00
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
2016-01-04 07:12:39 +01:00
# Copyright 2014-2016 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
2014-05-21 15:47:21 +02:00
#
# This file is part of qutebrowser.
#
# qutebrowser is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# qutebrowser is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
"""Launcher for an external editor."""
import os
2014-08-26 19:10:14 +02:00
import tempfile
2014-05-21 15:47:21 +02:00
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QProcess
2014-05-21 15:47:21 +02:00
2014-08-26 19:10:14 +02:00
from qutebrowser.config import config
from qutebrowser.utils import message, log
from qutebrowser.misc import guiprocess
2014-05-21 15:47:21 +02:00
class ExternalEditor(QObject):
2014-09-24 22:22:02 +02:00
"""Class to simplify editing a text in an external editor.
Attributes:
_text: The current text before the editor is opened.
_oshandle: The OS level handle to the tmpfile.
_filehandle: The file handle to the tmpfile.
_proc: The GUIProcess of the editor.
2014-09-28 22:13:14 +02:00
_win_id: The window ID the ExternalEditor is associated with.
2014-09-24 22:22:02 +02:00
"""
2014-05-21 15:47:21 +02:00
editing_finished = pyqtSignal(str)
2014-09-28 22:13:14 +02:00
def __init__(self, win_id, parent=None):
2014-05-21 15:47:21 +02:00
super().__init__(parent)
self._text = None
self._oshandle = None
self._filename = None
self._proc = None
2014-09-28 22:13:14 +02:00
self._win_id = win_id
2014-05-21 15:47:21 +02:00
def _cleanup(self):
2014-05-21 17:29:09 +02:00
"""Clean up temporary files after the editor closed."""
2015-08-19 09:34:44 +02:00
if self._oshandle is None or self._filename is None:
# Could not create initial file.
return
2014-05-21 15:47:21 +02:00
try:
os.close(self._oshandle)
if self._proc.exit_status() != QProcess.CrashExit:
os.remove(self._filename)
except OSError as e:
# NOTE: Do not replace this with "raise CommandError" as it's
# executed async.
2014-09-28 22:13:14 +02:00
message.error(self._win_id,
"Failed to delete tempfile... ({})".format(e))
2014-05-21 15:47:21 +02:00
2015-09-11 08:32:37 +02:00
@pyqtSlot(int, QProcess.ExitStatus)
2014-05-21 15:47:21 +02:00
def on_proc_closed(self, exitcode, exitstatus):
"""Write the editor text into the form field and clean up tempfile.
Callback for QProcess when the editor was closed.
"""
2014-08-26 20:15:41 +02:00
log.procs.debug("Editor closed")
2014-05-21 15:47:21 +02:00
if exitstatus != QProcess.NormalExit:
# No error/cleanup here, since we already handle this in
# on_proc_error.
2014-05-21 15:47:21 +02:00
return
try:
if exitcode != 0:
return
2014-08-20 20:57:10 +02:00
encoding = config.get('general', 'editor-encoding')
try:
with open(self._filename, 'r', encoding=encoding) as f:
text = f.read() # pragma: no branch
except OSError as e:
# NOTE: Do not replace this with "raise CommandError" as it's
# executed async.
message.error(self._win_id, "Failed to read back edited file: "
"{}".format(e))
return
2014-08-26 20:15:41 +02:00
log.procs.debug("Read back: {}".format(text))
self.editing_finished.emit(text)
finally:
self._cleanup()
2014-05-21 15:47:21 +02:00
@pyqtSlot(QProcess.ProcessError)
def on_proc_error(self, _err):
2014-05-21 15:47:21 +02:00
self._cleanup()
def edit(self, text):
"""Edit a given text.
Args:
text: The initial text to edit.
"""
if self._text is not None:
2014-05-21 15:47:21 +02:00
raise ValueError("Already editing a file!")
self._text = text
try:
2015-05-29 14:49:52 +02:00
self._oshandle, self._filename = tempfile.mkstemp(
text=True, prefix='qutebrowser-editor-')
if text:
encoding = config.get('general', 'editor-encoding')
with open(self._filename, 'w', encoding=encoding) as f:
2015-08-19 09:34:44 +02:00
f.write(text) # pragma: no branch
except OSError as e:
message.error(self._win_id, "Failed to create initial file: "
"{}".format(e))
return
self._proc = guiprocess.GUIProcess(self._win_id, what='editor',
parent=self)
2015-06-11 19:10:01 +02:00
self._proc.finished.connect(self.on_proc_closed)
self._proc.error.connect(self.on_proc_error)
2014-05-21 15:47:21 +02:00
editor = config.get('general', 'editor')
executable = editor[0]
2016-01-31 23:56:11 +01:00
args = [arg.replace('{}', self._filename) for arg in editor[1:]]
2014-08-26 20:15:41 +02:00
log.procs.debug("Calling \"{}\" with args {}".format(executable, args))
self._proc.start(executable, args)