[kupfer: 11/41] vim: Use a D-Bus service in separate process
- From: Ulrik Sverdrup <usverdrup src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [kupfer: 11/41] vim: Use a D-Bus service in separate process
- Date: Tue, 26 Apr 2011 17:13:44 +0000 (UTC)
commit 23ae89d37f7c9ed5b2f69c8d8f248f46432805e0
Author: Ulrik Sverdrup <ulrik sverdrup gmail com>
Date: Tue Apr 26 18:44:00 2011 +0200
vim: Use a D-Bus service in separate process
Move most of __init__.py into vim/plugin.py and only load it at
initialize_plugin time.
The kupfer.plugin.vim.service module is a D-Bus service intended to be
run inside kupfer --exec-helper
kupfer/plugin/vim/__init__.py | 270 +-----------------------------------
kupfer/plugin/vim/plugin.py | 310 +++++++++++++++++++++++++++++++++++++++++
kupfer/plugin/vim/service.py | 121 ++++++++++++++++
3 files changed, 436 insertions(+), 265 deletions(-)
---
diff --git a/kupfer/plugin/vim/__init__.py b/kupfer/plugin/vim/__init__.py
index 53cc978..68dcc3f 100644
--- a/kupfer/plugin/vim/__init__.py
+++ b/kupfer/plugin/vim/__init__.py
@@ -5,269 +5,9 @@ __description__ = _("Recently used documents in Vim")
__version__ = "2011-04"
__author__ = "Plugin: Ulrik Sverdrup, VimCom: Ali Afshar"
-import os
-import gio
-import glib
-
-from kupfer.objects import Source, FileLeaf, Leaf, Action
-from kupfer.objects import OperationError
-from kupfer.objects import AppLeaf, TextLeaf, TextSource
-from kupfer.obj.objects import Launch
-from kupfer.obj.apps import AppLeafContentMixin
-from kupfer import datatools
-from kupfer import utils
-from kupfer import kupferstring
-
-from kupfer.plugin.vim import vimcom
-
-VIM = 'gvim'
-
-
-def get_vim_files(filepath):
- """
- Read ~/.viminfo from @filepath
-
- Look for a line like this:
- *encoding=<encoding>
-
- Return an iterator of unicode string file paths
- """
- encoding = "UTF-8"
- recents = []
- with open(filepath, "r") as f:
- for line in f:
- if line.startswith("*encoding="):
- _, enc = line.split("=")
- encoding = enc.strip()
- us_line = line.decode(encoding, "replace")
- ## Now find the jumplist
- if us_line.startswith("-' "):
- parts = us_line.split(None, 3)
- recentfile = os.path.expanduser(parts[-1].strip())
- if recentfile:
- recents.append(recentfile)
- return datatools.UniqueIterator(recents)
-
-class RecentsSource (AppLeafContentMixin, Source):
- appleaf_content_id = ("vim", "gvim")
-
- vim_viminfo_file = "~/.viminfo"
- def __init__(self, name=None):
- name = name or _("Vim Recent Documents")
- super(RecentsSource, self).__init__(name)
-
- def initialize(self):
- """Set up change monitor"""
- viminfofile = os.path.expanduser(self.vim_viminfo_file)
- gfile = gio.File(viminfofile)
- self.monitor = gfile.monitor_file(gio.FILE_MONITOR_NONE, None)
- if self.monitor:
- self.monitor.connect("changed", self._changed)
-
- def finalize(self):
- if self.monitor:
- self.monitor.cancel()
- self.monitor = None
-
- def _changed(self, monitor, file1, file2, evt_type):
- """Change callback; something changed"""
- if evt_type in (gio.FILE_MONITOR_EVENT_CREATED,
- gio.FILE_MONITOR_EVENT_DELETED,
- gio.FILE_MONITOR_EVENT_CHANGED):
- self.mark_for_update()
-
- def get_items(self):
- viminfofile = os.path.expanduser(self.vim_viminfo_file)
- if not os.path.exists(viminfofile):
- self.output_debug("Viminfo not found at", viminfofile)
- return
-
- try:
- filepaths = list(get_vim_files(viminfofile))
- except EnvironmentError:
- self.output_exc()
- return
-
- for filepath in filepaths:
- # The most confusing glib function
- # takes a unicode string and returns a
- # filesystem-encoded bytestring.
- yield FileLeaf(glib.filename_from_utf8(filepath))
-
- def get_icon_name(self):
- return "document-open-recent"
-
- def provides(self):
- yield FileLeaf
-
-
-class VimApp (AppLeaf):
- """
- This is a re-implemented AppLeaf that represents a running Vim session
-
- with a fake vim self.object for safety (this should not be needed)
- """
- serializable = None
- def __init__(self, serverid, name):
- try:
- obj = gio.unix.DesktopAppInfo("gvim.desktop")
- except RuntimeError:
- obj = gio.AppInfo(VIM)
- Leaf.__init__(self, obj, name)
- self.serverid = serverid
-
- def get_id(self):
- # use an ostensibly fake id starting with @/
- return "@/%s/%s" % (__name__, self.serverid or "")
-
- def __setstate__(self, state):
- raise NotImplementedError
-
- def __getstate__(self):
- raise NotImplementedError
-
- def get_actions(self):
- if self.serverid is not None:
- yield Launch(_("Go To"), is_running=True)
- yield SendCommand()
- yield CloseSaveAll()
- else:
- yield Launch()
-
- def launch(self, files=(), paths=(), activate=False, ctx=None):
- """
- Launch the represented application
-
- @files: a seq of GFiles (gio.File)
- @paths: a seq of bytestring paths
- @activate: activate instead of start new
- """
- if self.serverid is not None:
- argv = [VIM, '--servername', self.serverid, '--remote']
- else:
- argv = [VIM]
- if files:
- paths = [f.get_path() or f.get_uri() for f in files]
- if paths:
- argv.extend(paths)
- if paths or self.serverid is None:
- try:
- utils.spawn_async_raise(argv)
- except utils.SpawnError as exc:
- raise OperationError(exc)
- if self.serverid:
- ## focus the window we opened
- ActiveVim.vimcom.foreground(self.serverid)
-
- def get_icon_name(self):
- return 'vim'
-
- def get_description(self):
- return None
-
-class CloseSaveAll (Action):
- """ Close a vim window without forcing """
- rank_adjust = -5
- def __init__(self):
- Action.__init__(self, _("Close (Save All)"))
-
- def activate(self, obj):
- ActiveVim.vimcom.send_ex(obj.serverid, 'wqa')
-
- def get_icon_name(self):
- return "window-close"
-
-class SendCommand (Action):
- def __init__(self):
- Action.__init__(self, _("Send..."))
-
- def activate(self, obj, iobj):
- ## accept with or without starting :
- lcmd = kupferstring.tolocale(iobj.object)
- if lcmd.startswith(":"):
- lcmd = lcmd[1:]
- ActiveVim.vimcom.send_ex(obj.serverid, lcmd)
-
- def requires_object(self):
- return True
- def object_types(self):
- yield TextLeaf
- def object_source(self, for_item=None):
- return TextSource()
-
- def get_description(self):
- return _("Send ex command")
-
-class InsertInVim (Action):
- """
- Insert a given text into the currently open buffer in a vim
- session
- """
- def __init__(self):
- Action.__init__(self, _("Insert in Vim..."))
-
- def activate(self, obj, iobj):
- tmpf, tmpname = utils.get_safe_tempfile()
- tmpf.write(kupferstring.tolocale(obj.object))
- tmpf.close()
- vim_cmd = "r %s" % tmpname
- ActiveVim.vimcom.send_ex(iobj.serverid, vim_cmd)
- glib.timeout_add_seconds(10, os.unlink, tmpname)
-
- def item_types(self):
- yield TextLeaf
-
- def requires_object(self):
- return True
-
- def object_types(self):
- yield VimApp
-
- def get_icon_name(self):
- return "insert-text"
-
-
-class ActiveVim (Source):
- def __init__(self):
- Source.__init__(self, _("Active Vim Sessions"))
-
- def initialize(self):
- self.vimcom = vimcom.VimCom(self)
- ActiveVim.vimcom = self.vimcom
- self.vimcom.vim_hidden = vimcom.poller()
- self.vimcom.stop_fetching_serverlist()
- self.serverids = []
- glib.timeout_add_seconds(1, self.update_serverlist)
-
- def finalize(self):
- pid = self.vimcom.vim_hidden.pid
- if pid:
- os.close(self.vimcom.vim_hidden.childfd)
- os.kill(pid, 15)
- os.waitpid(pid, 0)
- self.vimcom.destroy()
- self.vimcom = None
- self.mark_for_update()
-
- def get_items(self):
- for x in self.serverids:
- yield VimApp(x, _("Vim Session %s") % x)
- #yield VimApp(None, _("New Vim"))
-
- def vim_new_serverlist(self, serverlist):
- """this is the inaccurate serverlist"""
- pass
-
- def on_new_serverlist(self, new_list):
- if set(new_list) != set(self.serverids):
- self.serverids = new_list
- self.mark_for_update()
-
- def update_serverlist(self):
- if self.vimcom:
- self.vimcom.get_hidden_serverlist(self.on_new_serverlist)
- return True
-
- def provides(self):
- yield VimApp
+def initialize_plugin(name):
+ global RecentsSource
+ global ActiveVim
+ global InsertInVim
+ from kupfer.plugin.vim.plugin import RecentsSource, ActiveVim, InsertInVim
diff --git a/kupfer/plugin/vim/plugin.py b/kupfer/plugin/vim/plugin.py
new file mode 100644
index 0000000..f168479
--- /dev/null
+++ b/kupfer/plugin/vim/plugin.py
@@ -0,0 +1,310 @@
+import os
+import sys
+
+import dbus
+import gio
+import glib
+
+from kupfer.objects import Source, FileLeaf, Leaf, Action
+from kupfer.objects import OperationError
+from kupfer.objects import AppLeaf, TextLeaf, TextSource
+from kupfer.obj.objects import Launch
+from kupfer.obj.apps import AppLeafContentMixin
+from kupfer import datatools
+from kupfer import utils
+from kupfer import kupferstring
+from kupfer import pretty
+from kupfer import plugin_support
+
+plugin_support.check_dbus_connection()
+
+PLUGID='vim'
+
+VIM = 'gvim'
+
+def get_vim_files(filepath):
+ """
+ Read ~/.viminfo from @filepath
+
+ Look for a line like this:
+ *encoding=<encoding>
+
+ Return an iterator of unicode string file paths
+ """
+ encoding = "UTF-8"
+ recents = []
+ with open(filepath, "r") as f:
+ for line in f:
+ if line.startswith("*encoding="):
+ _, enc = line.split("=")
+ encoding = enc.strip()
+ us_line = line.decode(encoding, "replace")
+ ## Now find the jumplist
+ if us_line.startswith("-' "):
+ parts = us_line.split(None, 3)
+ recentfile = os.path.expanduser(parts[-1].strip())
+ if recentfile:
+ recents.append(recentfile)
+ return datatools.UniqueIterator(recents)
+
+class RecentsSource (AppLeafContentMixin, Source):
+ appleaf_content_id = ("vim", "gvim")
+
+ vim_viminfo_file = "~/.viminfo"
+ def __init__(self, name=None):
+ name = name or _("Vim Recent Documents")
+ super(RecentsSource, self).__init__(name)
+
+ def initialize(self):
+ """Set up change monitor"""
+ viminfofile = os.path.expanduser(self.vim_viminfo_file)
+ gfile = gio.File(viminfofile)
+ self.monitor = gfile.monitor_file(gio.FILE_MONITOR_NONE, None)
+ if self.monitor:
+ self.monitor.connect("changed", self._changed)
+
+ def finalize(self):
+ if self.monitor:
+ self.monitor.cancel()
+ self.monitor = None
+
+ def _changed(self, monitor, file1, file2, evt_type):
+ """Change callback; something changed"""
+ if evt_type in (gio.FILE_MONITOR_EVENT_CREATED,
+ gio.FILE_MONITOR_EVENT_DELETED,
+ gio.FILE_MONITOR_EVENT_CHANGED):
+ self.mark_for_update()
+
+ def get_items(self):
+ viminfofile = os.path.expanduser(self.vim_viminfo_file)
+ if not os.path.exists(viminfofile):
+ self.output_debug("Viminfo not found at", viminfofile)
+ return
+
+ try:
+ filepaths = list(get_vim_files(viminfofile))
+ except EnvironmentError:
+ self.output_exc()
+ return
+
+ for filepath in filepaths:
+ # The most confusing glib function
+ # takes a unicode string and returns a
+ # filesystem-encoded bytestring.
+ yield FileLeaf(glib.filename_from_utf8(filepath))
+
+ def get_icon_name(self):
+ return "document-open-recent"
+
+ def provides(self):
+ yield FileLeaf
+
+def get_plugin_iface_name(plugin_id):
+ plugin_id = plugin_id.split(".")[-1]
+ interface_name = "se.kaizer.kupfer.plugin.%s" % plugin_id
+ return interface_name
+
+def get_plugin_service_obj(plugin_id, activate=True):
+ """Return the dbus proxy object for our plugin
+
+ if @activate, we will --exec-helper= the service
+ """
+ plugin_id = plugin_id.split(".")[-1]
+
+ service_name = "se.kaizer.kupfer.plugin.%s" % plugin_id
+ interface_name = "se.kaizer.kupfer.plugin.%s" % plugin_id
+ object_name = "/se/kaizer/kupfer/plugin/%s" % plugin_id
+ try:
+ bus = dbus.Bus()
+ except dbus.DBusException:
+ return None
+ try:
+ proxy_obj = bus.get_object(service_name, object_name)
+ except dbus.DBusException as exc:
+ pretty.print_debug(__name__, exc)
+ if activate:
+ return start_plugin_helper("kupfer.plugin.%s.service" % plugin_id)
+ return None
+ proxy_iface = dbus.Interface(proxy_obj, interface_name)
+ return proxy_iface
+
+def stop_plugin_service(plugin_id):
+ """
+ Return True if it was running and was stopped
+ """
+ plug_iface = get_plugin_service_obj(plugin_id, activate=False)
+ if plug_iface:
+ plug_iface.Exit(reply_handler=_dummy_handler)
+
+def start_plugin_helper(name):
+ argv = [sys.executable]
+ argv.extend(sys.argv)
+ argv.append('--exec-helper=%s' % name)
+ utils.spawn_async(argv)
+
+def _dummy_handler(*args):
+ pass
+
+class VimApp (AppLeaf):
+ """
+ This is a re-implemented AppLeaf that represents a running Vim session
+
+ with a fake vim self.object for safety (this should not be needed)
+ """
+ serializable = None
+ def __init__(self, serverid, name):
+ try:
+ obj = gio.unix.DesktopAppInfo("gvim.desktop")
+ except RuntimeError:
+ obj = gio.AppInfo(VIM)
+ Leaf.__init__(self, obj, name)
+ self.serverid = serverid
+
+ def get_id(self):
+ # use an ostensibly fake id starting with @/
+ return "@/%s/%s" % (__name__, self.serverid or "")
+
+ def __setstate__(self, state):
+ raise NotImplementedError
+
+ def __getstate__(self):
+ raise NotImplementedError
+
+ def get_actions(self):
+ if self.serverid is not None:
+ yield Launch(_("Go To"), is_running=True)
+ yield SendCommand()
+ yield CloseSaveAll()
+ else:
+ yield Launch()
+
+ def launch(self, files=(), paths=(), activate=False, ctx=None):
+ """
+ Launch the represented application
+
+ @files: a seq of GFiles (gio.File)
+ @paths: a seq of bytestring paths
+ @activate: activate instead of start new
+ """
+ if self.serverid is not None:
+ argv = [VIM, '--servername', self.serverid, '--remote']
+ else:
+ argv = [VIM]
+ if files:
+ paths = [f.get_path() or f.get_uri() for f in files]
+ if paths:
+ argv.extend(paths)
+ if paths or self.serverid is None:
+ try:
+ utils.spawn_async_raise(argv)
+ except utils.SpawnError as exc:
+ raise OperationError(exc)
+ if self.serverid:
+ ## focus the window we opened
+ def error_handler(exc):
+ ctx.register_late_error(OperationError(exc))
+ proxy_obj = get_plugin_service_obj(PLUGID)
+ if proxy_obj:
+ proxy_obj.Foreground(self.serverid,
+ reply_handler=_dummy_handler,
+ error_handler=error_handler)
+
+ def get_icon_name(self):
+ return 'vim'
+
+ def get_description(self):
+ return None
+
+class CloseSaveAll (Action):
+ """ Close a vim window without forcing """
+ rank_adjust = -5
+ def __init__(self):
+ Action.__init__(self, _("Close (Save All)"))
+
+ def activate(self, obj):
+ ActiveVim.vimcom.send_ex(obj.serverid, 'wqa')
+
+ def get_icon_name(self):
+ return "window-close"
+
+class SendCommand (Action):
+ def __init__(self):
+ Action.__init__(self, _("Send..."))
+
+ def activate(self, obj, iobj):
+ ## accept with or without starting :
+ lcmd = kupferstring.tolocale(iobj.object)
+ if lcmd.startswith(":"):
+ lcmd = lcmd[1:]
+ ActiveVim.vimcom.send_ex(obj.serverid, lcmd)
+
+ def requires_object(self):
+ return True
+ def object_types(self):
+ yield TextLeaf
+ def object_source(self, for_item=None):
+ return TextSource()
+
+ def get_description(self):
+ return _("Send ex command")
+
+class InsertInVim (Action):
+ """
+ Insert a given text into the currently open buffer in a vim
+ session
+ """
+ def __init__(self):
+ Action.__init__(self, _("Insert in Vim..."))
+
+ def activate(self, obj, iobj):
+ tmpf, tmpname = utils.get_safe_tempfile()
+ tmpf.write(kupferstring.tolocale(obj.object))
+ tmpf.close()
+ vim_cmd = "r %s" % tmpname
+ ActiveVim.vimcom.send_ex(iobj.serverid, vim_cmd)
+ glib.timeout_add_seconds(10, os.unlink, tmpname)
+
+ def item_types(self):
+ yield TextLeaf
+
+ def requires_object(self):
+ return True
+
+ def object_types(self):
+ yield VimApp
+
+ def get_icon_name(self):
+ return "insert-text"
+
+
+class ActiveVim (Source):
+ def __init__(self):
+ Source.__init__(self, _("Active Vim Sessions"))
+
+ def initialize(self):
+ self.serverids = []
+ glib.timeout_add_seconds(1, self.start_helper)
+
+ def start_helper(self):
+ bus = dbus.Bus()
+ bus.add_signal_receiver(self.on_new_serverlist,
+ signal_name="NewServerlist",
+ dbus_interface=get_plugin_iface_name(PLUGID),
+ byte_arrays=True)
+ get_plugin_service_obj(PLUGID, activate=True)
+
+ def finalize(self):
+ stop_plugin_service(PLUGID)
+
+ def get_items(self):
+ for x in self.serverids:
+ yield VimApp(x, _("Vim Session %s") % x)
+
+ def on_new_serverlist(self, new_list):
+ self.output_debug("New list:", list(new_list))
+ if set(new_list) != set(self.serverids):
+ self.serverids = map(str, new_list)
+ self.mark_for_update()
+
+ def provides(self):
+ yield VimApp
diff --git a/kupfer/plugin/vim/service.py b/kupfer/plugin/vim/service.py
new file mode 100644
index 0000000..35d2990
--- /dev/null
+++ b/kupfer/plugin/vim/service.py
@@ -0,0 +1,121 @@
+
+import os
+import sys
+
+import pygtk
+pygtk.require('2.0')
+
+import glib
+import gobject
+
+from kupfer.plugin.vim import vimcom
+
+try:
+ import dbus
+ import dbus.service
+ #import dbus.glib
+ from dbus.mainloop.glib import DBusGMainLoop
+
+except (ImportError, dbus.exceptions.DBusException) as exc:
+ print exc
+ raise SystemExit(1)
+
+PLUGID='vim'
+
+server_name = "se.kaizer.kupfer.plugin.%s" % PLUGID
+interface_name = "se.kaizer.kupfer.plugin.%s" % PLUGID
+object_name = "/se/kaizer/kupfer/plugin/%s" % PLUGID
+
+class Service (dbus.service.Object):
+ def __init__(self, mainloop, bus):
+ bus_name = dbus.service.BusName(server_name, bus=bus,
+ allow_replacement=True, replace_existing=True)
+ super(Service, self).__init__(conn=bus, object_path=object_name,
+ bus_name=bus_name)
+ self.mainloop = mainloop
+ self.initialize()
+
+ def unregister(self):
+ self.connection.release_name(server_name)
+
+ def initialize(self):
+ self.vimcom = vimcom.VimCom(self)
+ self.vimcom.vim_hidden = vimcom.poller()
+ self.vimcom.stop_fetching_serverlist()
+ self.serverids = []
+ glib.timeout_add_seconds(1, self.update_serverlist)
+
+ def finalize(self):
+ pid = self.vimcom.vim_hidden.pid
+ if pid:
+ os.close(self.vimcom.vim_hidden.childfd)
+ os.kill(pid, 15)
+ os.waitpid(pid, 0)
+ self.vimcom.destroy()
+ self.vimcom = None
+
+ def mark_for_update(self):
+ self.NewServerlist(self.serverids)
+
+ def vim_new_serverlist(self, serverlist):
+ """this is the inaccurate serverlist"""
+ ## useless callback from vimcom.VimCom
+ pass
+
+ def on_new_serverlist(self, new_list):
+ if set(new_list) != set(self.serverids):
+ self.serverids = new_list
+ self.mark_for_update()
+
+ def update_serverlist(self):
+ if self.vimcom:
+ self.vimcom.get_hidden_serverlist(self.on_new_serverlist)
+ return True
+
+ @dbus.service.method(interface_name, in_signature="ay", out_signature="b",
+ byte_arrays=True)
+ def Foreground(self, server):
+ if self.vimcom and server in self.serverids:
+ self.vimcom.foreground(server)
+ return True
+ return False
+
+ @dbus.service.method(interface_name, in_signature="ayay", out_signature="b",
+ byte_arrays=True)
+ def SendEx(self, server, excommand):
+ if self.vimcom and server in self.serverids:
+ self.vimcom.send_ex(server, excommand)
+ return True
+ return False
+
+ @dbus.service.signal(interface_name, signature="aay")
+ def NewServerlist(self, serverlist):
+ pass
+
+ @dbus.service.method(interface_name)
+ def Exit(self):
+ self.unregister()
+ self.finalize()
+ self.mainloop.quit()
+
+def start(ml):
+ #ml_wrap = DBusGMainLoop(set_as_default=True)
+ #dbus.set_default_main_loop(ml_wrap)
+ try:
+ bus = dbus.Bus()
+ except dbus.DBusException:
+ raise SystemExit(1)
+ try:
+ service = Service(ml, bus)
+ except dbus.DBusException:
+ raise SystemExit(1)
+
+def main():
+ ml_wrap = DBusGMainLoop(set_as_default=True)
+ glib.set_prgname(__name__)
+ ml = glib.MainLoop()
+ glib.idle_add(start, ml)
+ ml.run()
+
+if __name__ == '__main__':
+ main()
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]