/* * Vimplugin * * Copyright (c) 2007 by The Vimplugin Project. * * Released under the GNU General Public License * with ABSOLUTELY NO WARRANTY. * * See the file COPYING for more information. */ package org.vimplugin; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.text.MessageFormat; import java.util.Arrays; import java.util.HashMap; import java.util.Locale; import java.util.Properties; import java.util.ResourceBundle; import org.apache.tools.ant.taskdefs.condition.Os; import org.eclim.logging.Logger; import org.eclim.util.CommandExecutor; import org.eclim.util.IOUtils; import org.eclim.util.StringUtils; import org.eclipse.core.runtime.Platform; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.osgi.framework.BundleContext; import org.vimplugin.editors.VimEditorPartListener; import org.vimplugin.preferences.PreferenceConstants; /** * The main plugin class to be used in the desktop. */ public class VimPlugin extends AbstractUIPlugin { private static final Logger logger = Logger.getLogger(VimPlugin.class); private static final String GVIM_FEATURE_TEST = "redir! > <file> | silent! <command> | quit"; private static final String FEATURES_COMMAND_UNIX = "echo 'embed:' . (v:version >= 700 && has('gui_gtk')) . " + "' netbeans:' . (has('netbeans_intg')) . " + "' netbeansDocumentListen:' . " + "(v:version > 702 || (v:version == 702 && has('patch359')))"; private static final String FEATURES_COMMAND_WINDOWS = "echo 'embed:' . " + "(v:version > 701 || (v:version == 701 && has('patch091'))) . " + "' netbeans:' . (has('netbeans_intg')) . " + "' netbeansDocumentListen:' . " + "(v:version > 702 || (v:version == 702 && has('patch359')))"; /** * The shared instance. */ private static VimPlugin plugin; /** * ID of the default Vim instance. */ public static final int DEFAULT_VIMSERVER_ID = 0; private HashMap<String,Boolean> features; private VimEditorPartListener partListener; /** * Returns the shared instance. * * @return the default plugin instance */ public static VimPlugin getDefault() { return plugin; } /** * Returns an image descriptor for the image file at the given plug-in * relative path. * * @param path the path * @return the image descriptor */ public static ImageDescriptor getImageDescriptor(String path) { return AbstractUIPlugin.imageDescriptorFromPlugin("VimNB", path); } /** * Counts number of instances of vimServerNewWindow */ private int nextServerID; /** * Counts number of total buffers opened so far. If we close one buffer this * value doesn't change. */ private int numberOfBuffers; /** * Counts number of commands executed so far. Will be useful for checking * functions and replies. */ private int seqNo; /** * Store all the vim instances using their id as the key. */ private final HashMap<Integer, VimServer> vimServers = new HashMap<Integer, VimServer>(); /** * Properties instance for plugin.properties */ private Properties properties; /** * ResourceBundle containing vimplugin messages. */ private ResourceBundle messages; /** * The constructor. */ public VimPlugin() { plugin = this; properties = new Properties(); try{ properties.load(getClass().getResourceAsStream("/plugin.properties")); }catch(IOException ioe){ MessageDialog.openError( getWorkbench().getActiveWorkbenchWindow().getShell(), "Vimplugin", "Unable to load plugin.properties"); ioe.printStackTrace(); } messages = ResourceBundle.getBundle( "org/vimplugin/messages", Locale.getDefault(), getClass().getClassLoader()); } /** * Creates a {@link VimServer} for each open action. * * @return the server instance */ public int getDefaultVimServer() { return createVimServer(DEFAULT_VIMSERVER_ID); } /** * Creates a VimServer. * * @return The VimServer ID. */ public int createVimServer() { return createVimServer(nextServerID++); } /** * Create a new VimServer with the ID Specified. If a VimServer with the ID * specified already exists, then don't do anything. * * @param id ID to use for the new VimServer. * @return ID of the new VimServer. */ private int createVimServer(int id) { if (!vimServers.containsKey(id)) { VimServer vimserver = new VimServer(id); vimServers.put(id, vimserver); } return id; } /** * Stops the VimServer specified. * * @param id The ID of the VimServer to stop. * @return Success. */ public boolean stopVimServer(int id) { boolean b = false; try { b = getVimserver(id).stop(); vimServers.remove(id); } catch (IOException ioe) { MessageDialog.openError( getWorkbench().getActiveWorkbenchWindow().getShell(), "Vimplugin", "VimServer to stop not found."); ioe.printStackTrace(); } return b; } /** * Returns VimServer with the id specified. * * @param id The ID of the VimServer. * @return The VimServer with the ID specified. */ public VimServer getVimserver(int id) { return vimServers.get(id); } /** * starts the plugin. * * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) */ @Override public void start(BundleContext context) throws Exception { super.start(context); nextServerID = 1; // 0 is for the DEFAULT VimServer numberOfBuffers = 1; // Vim starts buffer count from 1 seqNo = 0; partListener = new VimEditorPartListener(); Display.getDefault().asyncExec(new Runnable(){ public void run() { IWorkbench workbench = PlatformUI.getWorkbench(); workbench.getActiveWorkbenchWindow() .getActivePage().addPartListener(partListener); } }); } /** * stop the plugin. * * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) */ @Override public void stop(BundleContext context) throws Exception { super.stop(context); plugin = null; if (PlatformUI.isWorkbenchRunning()){ IWorkbench workbench = PlatformUI.getWorkbench(); if (workbench != null){ workbench.getActiveWorkbenchWindow() .getActivePage().removePartListener(partListener); } } } /** * increment {@link #seqNo} by one. * @return the next sequence Number. */ public int nextSeqNo() { return seqNo++; } /** * Simple Setter. * @param numberOfBuffers the numberOfBuffers to set */ public void setNumberOfBuffers(int numberOfBuffers) { this.numberOfBuffers = numberOfBuffers; } /** * Simple Getter. * @return the numberOfBuffers */ public int getNumberOfBuffers() { return numberOfBuffers; } /** * Determines if the configured gvim path exists. * * @return true if the configured gvim path exists, false otherwise. */ public boolean gvimAvailable() { String gvim = VimPlugin.getDefault().getPreferenceStore() .getString(PreferenceConstants.P_GVIM); File file = new File(gvim); if (file.exists()){ return true; } return false; } /** * Determines if the configured gvim instance supports embedding. * * @return true if embedding is supported, false otherwise. */ public boolean gvimEmbedSupported() { return hasFeature("embed"); } /** * Determines if the configured gvim instance supports the required netbeans * interface. * * @return true if netbeans supported, false otherwise. */ public boolean gvimNbSupported() { return hasFeature("netbeans"); } /** * Determines if the configured gvim can <a href="http://groups.google.com/group/vim_dev/browse_thread/thread/2126395af4137d4b">reliably</a> * support the netbeans document listening events: * * @return true if document listening is reliably supported. */ public boolean gvimNbDocumentListenSupported() { return hasFeature("netbeansDocumentListen"); } public void resetGvimState() { features = null; } /** * Gets the partListener used to monitor (un)focus of vim editors. * * @return The partListener. */ public VimEditorPartListener getPartListener() { return this.partListener; } private boolean hasFeature(String name) { if (features == null){ String gvim = VimPlugin.getDefault().getPreferenceStore() .getString(PreferenceConstants.P_GVIM); try{ File tempFile = File.createTempFile("eclim_gvim", null); tempFile.deleteOnExit(); String command = FEATURES_COMMAND_UNIX; if (Platform.getOS().equals(Platform.OS_WIN32)) { command = FEATURES_COMMAND_WINDOWS; } command = GVIM_FEATURE_TEST.replaceFirst("<command>", command); command = command.replaceFirst("<file>", tempFile.getAbsolutePath().replace('\\', '/').replaceAll(" ", "\\ ")); String[] cmd = { gvim, "-f", "-X", "-u", "NONE", "-U", "NONE", "--cmd", command}; logger.debug(Arrays.toString(cmd)); CommandExecutor process = CommandExecutor.execute(cmd, 5000); if(process.getReturnCode() != 0){ logger.error("Failed to execute gvim: " + process.getErrorMessage()); return false; } FileInputStream in = null; try{ String result = IOUtils.toString(in = new FileInputStream(tempFile)); result = result.trim(); logger.debug("gvim features supported: " + result); features = new HashMap<String,Boolean>(); for(String f : StringUtils.split(result)){ String[] keyVal = StringUtils.split(f, ":"); if(keyVal.length != 2){ logger.error("Invalid response from gvim: " + result); return false; } features.put(keyVal[0], Boolean.valueOf(keyVal[1].equals("1"))); } }catch(IOException ioe){ logger.error("Unable to read temp file.", ioe); IOUtils.closeQuietly(in); return false; }finally{ IOUtils.closeQuietly(in); try{ tempFile.delete(); }catch(Exception ignore){ } } }catch(Exception e){ logger.error("Unable to execute gvim.", e); return false; } } return features.containsKey(name) && features.get(name).booleanValue(); } /** * Gets the specified property from plugin.properties. * * @param name The property name. * @return The property value or null if not found. */ public String getProperty(String name){ return properties.getProperty(name); } /** * Gets the specified property from plugin.properties. * * @param name The property name. * @param def Default value if property is not found. * @return The property value or def if not found. */ public String getProperty(String name, String def){ return properties.getProperty(name, def); } /** * Used to obtain a message from the plugin's resource bundle. * * Supports optional var args which will be used to format the message via * MessageFormat. * * @param key The message key. * @param args Optional arguments to format the message with. * @return The message. */ public String getMessage(String key, Object... args) { String message = messages.getString(key); if (Os.isFamily(Os.FAMILY_MAC)){ message = message.replaceAll("\\bgvim\\b", "mvim/gvim"); } if (args != null && args.length > 0){ return MessageFormat.format(message, args); } return message; } }