jp (blog/preview): added inotify support, so preview can be updated each time the file is modified (actually IN_CLOSE_WRITE is used, but this may change in the future)
This commit is contained in:
parent
d34f3ab948
commit
e2eb4672b0
1 changed files with 72 additions and 18 deletions
|
|
@ -60,6 +60,7 @@ METADATA_SUFF = '_metadata.json'
|
|||
KEY_TO_REMOVE_METADATA = ('id','content', 'content_xhtml', 'comments_node', 'comments_service')
|
||||
|
||||
URL_REDIRECT_PREFIX = 'url_redirect_'
|
||||
INOTIFY_INSTALL = '"pip install inotify"'
|
||||
|
||||
|
||||
class BlogCommon(object):
|
||||
|
|
@ -313,46 +314,99 @@ class Edit(base.CommandBase, BlogCommon):
|
|||
class Preview(base.CommandBase, BlogCommon):
|
||||
|
||||
def __init__(self, host):
|
||||
super(Preview, self).__init__(host, 'preview', help=_(u'preview a blog content'))
|
||||
super(Preview, self).__init__(host, 'preview', use_verbose=True, help=_(u'preview a blog content'))
|
||||
|
||||
def add_parser_options(self):
|
||||
self.parser.add_argument("--inotify", type=str, choices=('auto', 'true', 'false'), default=u'auto', help=_(u"use inotify to handle preview"))
|
||||
self.parser.add_argument("file", type=base.unicode_decoder, nargs='?', default=u'current', help=_(u"path to the content file"))
|
||||
|
||||
def showPreview(self, content_xhtml, file_obj):
|
||||
import webbrowser
|
||||
import urllib
|
||||
def showPreview(self):
|
||||
# we implement showPreview here so we don't have to import webbroser and urllib
|
||||
# when preview is not used
|
||||
url = 'file:{}'.format(self.urllib.quote(self.preview_file_path))
|
||||
self.webbrowser.open_new_tab(url)
|
||||
|
||||
def updateContent(self):
|
||||
with open(self.current_file_path, 'rb') as f:
|
||||
content_xhtml = f.read().decode('utf-8')
|
||||
if self.syntax != 'XHTML':
|
||||
# we use safe=True because we want to have a preview as close as possible to what the
|
||||
# people will see
|
||||
content_xhtml = self.host.bridge.syntaxConvert(content_xhtml, self.syntax, 'XHTML', True, self.profile)
|
||||
|
||||
xhtml = (u'<html xmlns="http://www.w3.org/1999/xhtml">' +
|
||||
u'<head><meta http-equiv="Content-Type" content="text/html;charset=utf-8" /></head>'+
|
||||
'<body>{}</body>' +
|
||||
u'</html>').format(content_xhtml)
|
||||
file_obj.write(xhtml.encode('utf-8'))
|
||||
url = 'file:{}'.format(urllib.quote(file_obj.name))
|
||||
webbrowser.open_new_tab(url)
|
||||
|
||||
with open(self.preview_file_path, 'wb') as f:
|
||||
f.write(xhtml.encode('utf-8'))
|
||||
|
||||
def start(self):
|
||||
import webbrowser
|
||||
import urllib
|
||||
self.webbrowser, self.urllib = webbrowser, urllib
|
||||
|
||||
if self.args.inotify != 'false':
|
||||
try:
|
||||
import inotify.adapters
|
||||
import inotify.constants
|
||||
except ImportError:
|
||||
if self.args.inotify == 'auto':
|
||||
inotify = None
|
||||
self.disp(u'inotify module not found, deactivating feature. You can install it with {install}'.format(install=INOTIFY_INSTALL))
|
||||
else:
|
||||
self.disp(u"inotify not found, can't activate the feature! Please install it with {install}".format(install=INOTIFY_INSTALL), error=True)
|
||||
self.host.quit(1)
|
||||
else:
|
||||
# we deactivate logging in inotify, which is quite annoying
|
||||
try:
|
||||
inotify.adapters._LOGGER.setLevel(40)
|
||||
except AttributeError:
|
||||
self.disp(u"Logger doesn't exists, inotify may have chanded", error=True)
|
||||
else:
|
||||
inotify=None
|
||||
|
||||
sat_conf = config.parseMainConf()
|
||||
SYNTAX_EXT.update(config.getConfig(sat_conf, 'jp', CONF_SYNTAX_EXT, {}))
|
||||
|
||||
open_cb = self.showPreview
|
||||
update_cb = self.showPreview
|
||||
|
||||
# which file do we need to edit?
|
||||
if self.args.file == 'current':
|
||||
current_file_path = self.getCurrentFile(sat_conf)
|
||||
self.current_file_path = self.getCurrentFile(sat_conf)
|
||||
else:
|
||||
current_file_path = os.path.abspath(self.args.file)
|
||||
self.current_file_path = os.path.abspath(self.args.file)
|
||||
|
||||
syntax = self.guessSyntaxFromPath(sat_conf, current_file_path)
|
||||
self.syntax = self.guessSyntaxFromPath(sat_conf, self.current_file_path)
|
||||
|
||||
if syntax != 'XHTML':
|
||||
with open(current_file_path, 'rb') as f:
|
||||
# we use safe=True because we want to have a preview as close as possible to what the
|
||||
# people will see
|
||||
content_xhtml = self.host.bridge.syntaxConvert(f.read(), syntax, 'XHTML', True, self.profile)
|
||||
|
||||
# at this point the syntax is converted, we can display the preview
|
||||
preview_file = tempfile.NamedTemporaryFile(suffix='.xhtml', delete=False)
|
||||
self.preview_file_path = preview_file.name
|
||||
preview_file.close()
|
||||
self.updateContent()
|
||||
|
||||
with tempfile.NamedTemporaryFile(suffix='.xhtml', delete=False) as f:
|
||||
if inotify is None:
|
||||
# XXX: we don't delete file automatically because browser need it (and webbrowser.open can return before it is read)
|
||||
self.disp(u'temporary file created at {}\nthis file will NOT BE DELETED AUTOMATICALLY, please delete it yourself when you have finished'.format(f.name))
|
||||
self.showPreview(content_xhtml, f)
|
||||
self.disp(u'temporary file created at {}\nthis file will NOT BE DELETED AUTOMATICALLY, please delete it yourself when you have finished'.format(self.preview_file_path))
|
||||
open_cb()
|
||||
else:
|
||||
open_cb()
|
||||
i = inotify.adapters.Inotify(block_duration_s=60) # no need for 1 s duraction, inotify drive actions here
|
||||
# XXX: we only check IN_CLOSE_WRITE, but that may need to be changed depending on editor
|
||||
# experience will tell the appropriate values
|
||||
i.add_watch(self.current_file_path, mask=inotify.constants.IN_CLOSE_WRITE)
|
||||
try:
|
||||
for event in i.event_gen():
|
||||
if event is not None:
|
||||
self.disp(u"Content updated", 1)
|
||||
self.updateContent()
|
||||
update_cb()
|
||||
finally:
|
||||
os.unlink(self.preview_file_path)
|
||||
i.remove_watch(self.current_file_path)
|
||||
|
||||
|
||||
class Import(base.CommandAnswering):
|
||||
|
|
|
|||
Loading…
Reference in a new issue