Source code for pywikibot.editor
#!/usr/bin/python
"""Text editor class for your favourite editor."""
#
# (C) Pywikibot team, 2004-2020
#
# Distributed under the terms of the MIT license.
#
import codecs
import os
import subprocess
import tempfile
from sys import platform
from textwrap import fill
from typing import Optional
import pywikibot
from pywikibot import config
try:
from pywikibot.userinterfaces import gui
except ImportError as e:
gui = e
[docs]class TextEditor:
"""Text editor."""
def _command(self, file_name, text, jump_index=None):
"""Return editor selected in user-config.py."""
if jump_index:
# Some editors make it possible to mark occurrences of substrings,
# or to jump to the line of the first occurrence.
# TODO: Find a better solution than hardcoding these, e.g. a config
# option.
line = text[:jump_index].count('\n')
column = jump_index - (text[:jump_index].rfind('\n') + 1)
else:
line = column = 0
# Linux editors. We use startswith() because some users might use
# parameters.
if config.editor.startswith('kate'):
command = ['-l', '%i' % (line + 1), '-c', '%i' % (column + 1)]
elif config.editor.startswith('gedit'):
command = ['+%i' % (line + 1)] # seems not to support columns
elif config.editor.startswith('emacs'):
command = ['+%i' % (line + 1)] # seems not to support columns
elif config.editor.startswith('jedit'):
command = ['+line:%i' % (line + 1)] # seems not to support columns
elif config.editor.startswith('vim'):
command = ['+%i' % (line + 1)] # seems not to support columns
elif config.editor.startswith('nano'):
command = ['+%i,%i' % (line + 1, column + 1)]
# Windows editors
elif config.editor.lower().endswith('notepad++.exe'):
command = ['-n%i' % (line + 1)] # seems not to support columns
else:
command = []
# See T102465 for problems relating to using config.editor unparsed.
command = [config.editor] + command + [file_name]
pywikibot.log('Running editor: %s' % TextEditor._concat(command))
return command
@staticmethod
def _concat(command):
return ' '.join("'{0}'".format(part) if ' ' in part else part
for part in command)
[docs] def edit(self, text: str, jumpIndex: Optional[int] = None,
highlight: Optional[str] = None) -> Optional[str]:
"""
Call the editor and thus allows the user to change the text.
Halts the thread's operation until the editor is closed.
@param text: the text to be edited
@param jumpIndex: position at which to put the caret
@param highlight: each occurrence of this substring will be highlighted
@return: the modified text, or None if the user didn't save the text
file in his text editor
"""
if config.editor:
handle, tempFilename = tempfile.mkstemp()
tempFilename = '{}.{}'.format(tempFilename,
config.editor_filename_extension)
try:
with codecs.open(tempFilename, 'w',
encoding=config.editor_encoding) as tempFile:
tempFile.write(text)
creationDate = os.stat(tempFilename).st_mtime
cmd = self._command(tempFilename, text, jumpIndex)
subprocess.run(cmd, shell=platform == 'win32')
lastChangeDate = os.stat(tempFilename).st_mtime
if lastChangeDate == creationDate:
# Nothing changed
return None
with codecs.open(tempFilename, 'r',
encoding=config.editor_encoding) as temp_file:
newcontent = temp_file.read()
return newcontent
finally:
os.close(handle)
os.unlink(tempFilename)
if isinstance(gui, ImportError):
raise ImportError(fill(
'Could not load GUI modules: {}. No editor available. '
'Set your favourite editor in user-config.py "editor", '
'or install python packages tkinter and idlelib, which '
'are typically part of Python but may be packaged separately '
'on your platform.'.format(gui)) + '\n')
return pywikibot.ui.editText(text, jumpIndex=jumpIndex,
highlight=highlight)