Source code for leo.plugins.vim

#@+node:EKR.20040517075715.10: * @file
#@+<< docstring >>
#@+node:ekr.20050226184411: ** << docstring >>
#@@language rest

Enables two-way communication with gVim (recommended) or Vim.


    Opens the nearest ancestor @file or @clean node in vim. Leo will update the
    file in the outline when you save the file in vim.

    Opens the selected node in vim. Leo will update the node in the outline when
    you save the file in vim.


Set the ``vim_cmd`` and ``vim_exe`` settings as shown below.

Alternatively, you can put gvim.exe is on your PATH.


``@string vim_cmd``
    The command to execute to start gvim. Something like::

        <path-to-gvim>/gvim --servername LEO

``@string vim_exe``
    The path to the gvim executable.

    True: Leo will put the node or file in a Vim tab card.

#@-<< docstring >>
#@+<< version history >>
#@+node:ekr.20050226184411.1: ** << version history >>
# Contributed by Andrea Galimberti.
# Edited by Felix Breuer.
# 1.5 EKR:
#     - Added new sections.
#     - Move most comments into docstring.
#     - Added useDoubleClick variable.
#     - Added init function.
#     - Init _vim_cmd depending on sys.platform.
# 1.6 EKR:
#     - Use keywords to get c, not
#     - Don't use during unit testing: prefer xemacs instead.
#     - Added _vim_exe
#     - Use "os.spawnv" instead of os.system.
#     - Simplified the search of
#     - Fixed bug in open_in_vim: hanged v.bodyString to v.bodyString()
# 1.7 EKR: Excellent new code by Jim Sizelove solves weird message on first open of vim.
# 1.8 EKR: Set subprocess = None if import fails.
# 1.9 EKR:
#     - Document how install subproces, and use g.importExtension to import subprocess.
#     - Import subprocess with g.importExtension.
# 1.10 EKR:
#     - Support 'vim_cmd' and 'vim_exe' settings.
#     - These override the default _vim_cmd and _vim_exe settings.
# 1.11 EKR: Emergency default for window is now the default location: c:\Program Files\vim\vim63
# 1.12 EKR:
#     - Added emergency default for 'darwin'.
#     - Corrected the call to openWith.  It must now use data=data due to a new event param.
# 1.13 EKR: The docstring now states that the open_with plugin must be enabled for this to work.
# 1.14 EKR: Emphasized that the open_with plugin must be enabled.
# 1.15 EKR: Don't open @url nodes in vim if @bool vim_plugin_opens_url_nodes setting is False.
# 1.16 TL: open_in_vim modifications
#     - support file open in gVim at same line number as Leo cursor location
#     - support file open in a gVim tab
# 1.17 EKR: Give a location message to help with settings.
# 1.18 VMV:
#     - Use gvim on Linux too, emergency default on Windows doesn't have explicit path
#     - Works when subprocess.Popen(shell=True)
# 2.0 EKR: Use *only* the vim-open-node command.  Do not pollute click handlers.
#@-<< version history >>
#@+<< documentation from Jim Sizelove >>
#@+node:ekr.20050909102921: ** << documentation from Jim Sizelove >>
#@@language rest
# I was trying to get Leo to work more effectively with Vim, my editor of choice.
# To do so, I made several changes to Leo which (I believe) make it work better.
# After much exploring and trying various things, I made a change to the os.spawnv
# section of the openWith function in This added line seems to
# prevent the "weird error message on first open of Vim." (, line 32) when
# opening Vim with os.spawnv.
# os.spawnv needs the command it is calling as the first argument in the args list
# in addition, so the command actually shows twice in the total args to os.spawnv.
# For example::
#     os.spawnv(os.P_NOWAIT, "C:/Program Files/Vim/vim63/gvim.exe",
#         ["gvim.exe", "--servername", "LEO", "--remote", "foo.txt"])
# If the call is made without the command-name as the first item in the list of
# args, like so::
#     os.spawnv(os.P_NOWAIT, "C:/Program Files/Vim/vim63/gvim.exe",
#         ["--servername", "LEO", "--remote", "foo.txt"])
# an error message pops up::
#     E247: no registered server named "GVIM": Send failed.  Trying to execute locally
# This message means that gVim is not looking for a server named "LEO", which
# presumably the user has already opened with the command "gvim --servername LEO".
# Instead it is looking for a server named "GVIM", and not finding it, opens the
# files "foo.txt" and "LEO" (notice that it didn't catch the "--servername"
# argument and thinks that "LEO" is the name of a new file to create) in two
# buffers in a local copy of gVim. Now, if the command is::
#     os.spawnv(
#         os.P_NOWAIT, "C:/Program Files/Vim/vim63/gvim.exe",
#         ["gvim.exe", "--servername", "LEO", "--remote", "foo.txt"])
# Everything works great, as long as the user doesn't close the gVim window. If
# the user has closed the gVim window, then tries to open a node in Vim, they will
# see this error message::
#     E247: no registered server named "LEO": Send failed.
# Trying to execute locally If you use the ``--remote-silent`` argument, gVim will
# start the LEO server without the error message.
# You can see which servers gVim has running by typing the following at the
# command prompt::
#     vim --serverlist
# The rest of my changes have to do with using the subprocess module instead of
# the os.system, and various os.spawn* calls. I find subprocess easier to
# understand, and it is fairly simple to use for the most common kinds of process
# calls, but is capable of all the variations you may need. It is designed to
# replace all the os.system, os.spawn, and popen calls. It is available in Python
# 2.4.
# So I added some lines to use subprocess in the OpenWith plugin and the Vim
# plugin.
# I also have added a table in the "create_open_with_menu" function that makes use
# of the various editors I have used at times. Most of those editors are called
# with subprocess.Popen.
#@-<< documentation from Jim Sizelove >>
#@+<< imports >>
#@+node:ekr.20050226184411.2: ** << imports >>
import leo.core.leoGlobals as g
import os
import subprocess
import sys
#@-<< imports >>
# This command is used to communicate with the vim server. If you use gvim
# you can leave the command as is, you do not need to change it to "gvim ..."
# New in version 1.10 of this plugin: these are emergency defaults only.
# They are typically overridden by the corresponding 'vim_cmd' and 'vim_exe' settings in
# leoSettings.leo or individual .leo files.
if sys.platform == 'win32':
    # Works on XP if you have gvim on PATH
    _vim_cmd = "gvim --servername LEO"
    _vim_exe = "gvim"
elif sys.platform == 'darwin':
    _vim_cmd = "/Applications/ --servername LEO"
    _vim_exe = "gvim"
    _vim_cmd = "gvim --servername LEO"
    _vim_exe = "gvim"
# Global message flags.
contextmenu_message_given = False
locationMessageGiven = False
#@+node:ekr.20050226184624: ** init
[docs]def init(): '''Return True if the plugin has loaded successfully.''' ok = not # Don't conflict with xemacs plugin. if ok: # print (' enabled') # Register the handlers... # event = 'open2' # g.registerHandler(event,on_open_window) # Enable the os.system call if you want to # start a (g)vim server when Leo starts. if 0: os.system(_vim_cmd) g.plugin_signon(__name__) return ok
#@+node:ekr.20150326150910.1: ** g.command('vim-open-file')
[docs]@g.command('vim-open-file') def vim_open_file_command(event): ''' Open the entire file in (g)vim.''' c = event.get('c') if c: VimCommander(c, entire_file=True)
#@+node:ekr.20120315101404.9745: ** g.command('vim-open-node')
[docs]@g.command('vim-open-node') def vim_open_node_command(event): ''' open the selected node in (g)vim.''' c = event.get('c') if c: VimCommander(c, entire_file=False)
#@+node:ekr.20150326153420.1: ** class VimCommander
[docs]class VimCommander(object): '''A class implementing the vim plugin.''' #@+others #@+node:ekr.20150326155343.1: *3* vim.ctor def __init__(self, c, entire_file): '''Ctor for the VimCommander class.''' self.c = c self.entire_file = entire_file # compute settings. getBool, getString = c.config.getBool, c.config.getString self.open_url_nodes = getBool('vim_plugin_opens_url_nodes') self.trace = False or getBool('vim_plugin_trace') self.uses_tab = getBool('vim_plugin_uses_tab_feature') self.vim_cmd = getString('vim_cmd') or _vim_cmd self.vim_exe = getString('vim_exe') or _vim_exe # Give messages. global locationMessageGiven if self.trace and not locationMessageGiven: locationMessageGiven = True print('vim_cmd: %s' % self.vim_cmd) print('vim_exe: %s' % self.vim_exe) self.open_in_vim() #@+node:ekr.20150326183310.1: *3* vim.error
[docs] def error(self, s): '''Report an error.''' g.es_print(s, color='red')
#@+node:ekr.20120315101404.9746: *3* vim.open_in_vim & helpers
[docs] def open_in_vim(self): '''Open p in vim, or the entire enclosing file if entire_file is True.''' p = self.c.p if not self.check_args(): return root = self.find_root(p) if self.entire_file else p if not root: return path = self.find_path_for_node(root) if path and self.should_open_old_file(path, root): cmd = self.vim_cmd + "--remote-send '<C-\\><C-N>:e " + path + "<CR>'" if self.trace: g.trace('os.system(%s)' % cmd) os.system(cmd) else: # Open a new temp file. if path: self.forget_path(path) self.open_file(root)
#@+node:ekr.20150326183613.1: *4* vim.check_args & helper
[docs] def check_args(self): '''Return True of basic checks pass.''' p = self.c.p contextMenu = self.load_context_menu() if not contextMenu: return False if not self.open_url_nodes and p.h.startswith('@url'): return False else: return True
#@+node:ekr.20150326154203.1: *5* vim.load_context_menu
[docs] def load_context_menu(self): '''Load the contextmenu plugin.''' global contextmenu_message_given contextMenu = g.loadOnePlugin('', verbose=True) if not contextMenu and not contextmenu_message_given: contextmenu_message_given = True self.error('can not load') return contextMenu
#@+node:ekr.20150326180515.1: *4* vim.find_path_for_node
[docs] def find_path_for_node(self, p): '''Search the open-files list for a file corresponding to p.''' efc = path = efc.find_path_for_node(p) return path
#@+node:ekr.20150326173414.1: *4* vim.find_root
[docs] def find_root(self, p): '''Return the nearest ancestor @auto or @clean node.''' assert self.entire_file for p2 in p.self_and_parents(): if p2.isAnyAtFileNode(): return p2 self.error('no parent @auto or @clean node: %s' % p.h) return None
#@+node:ekr.20150326173301.1: *4* vim.forget_path
[docs] def forget_path(self, path): ''' Stop handling the path: - Remove the path from the list of open-with files. - Send a command to vim telling it to close the path. ''' assert path # Don't do this: it prevents efc from reopening paths. # efc = # if efc: efc.forget_path(path) if 0: # Dubious. if g.os_path_exists(path): os.remove(path) cmd = self.vim_cmd + "--remote-send '<C-\\><C-N>:bd " + path + "<CR>'" os.system(cmd)
#@+node:ekr.20150326181247.1: *4* vim.get_cursor_arg
[docs] def get_cursor_arg(self): '''Compute the cursor argument for vim.''' wrapper = self.c.frame.body.wrapper s = wrapper.getAllText() ins = wrapper.getInsertPoint() row, col = g.convertPythonIndexToRowCol(s, ins) return "+" + str(row + 1)
# This is an Ex command, not a normal Vim command. See: # # and # #@+node:ekr.20150326180928.1: *4* vim.open_file
[docs] def open_file(self, root): '''Open the the file in vim using c.openWith.''' c = self.c efc = # Common arguments. tab_arg = "-tab" if self.uses_tab else "" remote_arg = "--remote" + tab_arg + "-silent" args = [self.vim_exe, "--servername", "LEO", remote_arg] # No cursor arg. if self.entire_file: # vim-open-file args.append('+0') # Go to first line of the file. This is an Ex command. assert root.isAnyAtFileNode(), root dir_ = g.setDefaultDirectory(c, root) fn = c.os_path_finalize_join(dir_, root.anyAtFileNodeName()) else: # vim-open-node args.append(self.get_cursor_arg()) # Set the cursor position to the current line in the node. ext = 'txt' fn = efc.create_temp_file(c, ext, c.p) c_arg = '%s %s' % (' '.join(args), fn) command = 'subprocess.Popen(%s,shell=True)' % c_arg try: subprocess.Popen(c_arg, shell=True) except OSError: g.es_print(command) g.es_exception()
#@+node:ekr.20150326173000.1: *4* vim.should_open_old_file
[docs] def should_open_old_file(self, path, root): '''Return True if we should open the old temp file.''' v = root.v return ( path and g.os_path_exists(path) and hasattr(v.b, '_vim_old_body') and v.b == v._vim_old_body )
#@+node:ekr.20150326175258.1: *3* vim.write_root (not used)
[docs] def write_root(self, root): '''Return the concatenation of all bodies in p's tree.''' result = [] for p in root.self_and_subtree(): s = p.b result.append(s if s.endswith('\n') else s.rstrip() + '\n') return ''.join(result)
#@-others #@-others #@@language python #@@tabwidth -4 #@-leo