/* * Copyright (c) 2003-2005 RubyPeople. * * This file is part of the Ruby Development Tools (RDT) plugin for eclipse. * RDT is subject to the "Common Public License (CPL) v 1.0". You may not use * RDT except in compliance with the License. For further information see * org.rubypeople.rdt/rdt.license. */ package org.rubypeople.rdt.internal.ui; import java.io.IOException; import java.util.PropertyResourceBundle; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Preferences; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunchListener; import org.eclipse.jface.action.GroupMarker; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.text.templates.ContextTypeRegistry; import org.eclipse.jface.text.templates.persistence.TemplateStore; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.search.ui.IContextMenuConstants; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.editors.text.EditorsUI; import org.eclipse.ui.navigator.ICommonMenuConstants; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.eclipse.ui.texteditor.ChainedPreferenceStore; import org.eclipse.ui.texteditor.ConfigurationElementSorter; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.rubypeople.rdt.core.IBuffer; import org.rubypeople.rdt.core.IRubyInformation; import org.rubypeople.rdt.core.IRubyScript; import org.rubypeople.rdt.core.RubyCore; import org.rubypeople.rdt.core.WorkingCopyOwner; import org.rubypeople.rdt.internal.corext.util.OpenTypeHistory; import org.rubypeople.rdt.internal.corext.util.TypeFilter; import org.rubypeople.rdt.internal.formatter.OldCodeFormatter; import org.rubypeople.rdt.internal.ui.infoviews.RiUtility; import org.rubypeople.rdt.internal.ui.packageview.PackageExplorerPart; import org.rubypeople.rdt.internal.ui.preferences.MembersOrderPreferenceCache; import org.rubypeople.rdt.internal.ui.preferences.MockupPreferenceStore; import org.rubypeople.rdt.internal.ui.rubyeditor.ASTProvider; import org.rubypeople.rdt.internal.ui.rubyeditor.DocumentAdapter; import org.rubypeople.rdt.internal.ui.rubyeditor.RubyDocumentProvider; import org.rubypeople.rdt.internal.ui.rubyeditor.RubyScriptDocumentProvider; import org.rubypeople.rdt.internal.ui.rubyeditor.WorkingCopyManager; import org.rubypeople.rdt.internal.ui.text.PreferencesAdapter; import org.rubypeople.rdt.internal.ui.text.folding.RubyFoldingStructureProviderRegistry; import org.rubypeople.rdt.internal.ui.text.ruby.hover.RubyEditorTextHoverDescriptor; import org.rubypeople.rdt.internal.ui.text.template.contentassist.RubyTemplateAccess; import org.rubypeople.rdt.internal.ui.viewsupport.ProblemMarkerManager; import org.rubypeople.rdt.ui.PreferenceConstants; import org.rubypeople.rdt.ui.text.RubyTextTools; import org.rubypeople.rdt.ui.viewsupport.ImageDescriptorRegistry; /** * Ruby Plugin class */ public class RubyPlugin extends AbstractUIPlugin { /** * plugin */ protected static RubyPlugin plugin; /** * PLUGIN_ID */ public static final String PLUGIN_ID = "org.rubypeople.rdt.ui"; //$NON-NLS-1$ /** * textTools */ protected RubyTextTools textTools; /** * rubyFileMatcher */ protected RubyFileMatcher rubyFileMatcher; private WorkingCopyManager fWorkingCopyManager; private RubyDocumentProvider fDocumentProvider; /** * pluginProperties */ protected PropertyResourceBundle pluginProperties; /** * The combined preference store. * * @since 3.0 */ private IPreferenceStore fCombinedPreferenceStore; /** * Mockup preference store for firing events and registering listeners on project setting changes. FIXME: Temporary * solution. * * @since 3.0 */ private MockupPreferenceStore fMockupPreferenceStore; private RubyFoldingStructureProviderRegistry fFoldingStructureProviderRegistry; private ImageDescriptorRegistry fImageDescriptorRegistry; private MembersOrderPreferenceCache fMembersOrderPreferenceCache; private RubyScriptDocumentProvider fExternalRubyDocumentProvider; private RubyEditorTextHoverDescriptor[] fRubyEditorTextHoverDescriptors; /** * Default instance of the appearance type filters. * * @since 1.0 */ private TypeFilter fTypeFilter; private ProblemMarkerManager fProblemMarkerManager; private ASTProvider fASTProvider; private ILaunchListener launchListener; private RubyExplorerTracker fRubyExplorerTracker; /** * Ruby plugin default constructor */ public RubyPlugin() { super(); } /** * Returns the mock-up preference store for firing events and registering listeners on project setting changes. * Temporary solution. * * @return the mock-up preference store */ public MockupPreferenceStore getMockupPreferenceStore() { if (fMockupPreferenceStore == null) fMockupPreferenceStore = new MockupPreferenceStore(); return fMockupPreferenceStore; } /** * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) */ public void start(BundleContext context) throws Exception { plugin = this; super.start(context); // Here's where the magic happens that makes the IRubyScript's contents // get re-routed to the IDocument's latest contents WorkingCopyOwner.setPrimaryBufferProvider(new WorkingCopyOwner() { public IBuffer createBuffer(IRubyScript workingCopy) { IRubyScript original = workingCopy.getPrimary(); IResource resource = original.getResource(); if (resource instanceof IFile) return new DocumentAdapter(workingCopy, (IFile) resource); return DocumentAdapter.NULL; } }); ensurePreferenceStoreBackwardsCompatibility(); if (PlatformUI.isWorkbenchRunning()) { // Initialize AST provider getASTProvider(); new InitializeAfterLoadJob().schedule(); } } /** * Installs backwards compatibility for the preference store. */ private void ensurePreferenceStoreBackwardsCompatibility() { IPreferenceStore store = getPreferenceStore(); // must add here to guarantee that it is the first in the listener list fMembersOrderPreferenceCache = new MembersOrderPreferenceCache(); fMembersOrderPreferenceCache.install(store); } /* package */ void initializeAfterLoad(IProgressMonitor monitor) { // Swap in our process console manager for theirs launchListener = new AptanaProcessConsoleManager(); DebugPlugin.getDefault().getLaunchManager().addLaunchListener(launchListener); getBundle().getBundleContext().registerService(IRubyInformation.class.getName(), new RiUtility(), null); OpenTypeHistory.getInstance().checkConsistency(monitor); new RubyInstalledDetector().schedule(); forceRDTUIPluginToLoad(); } private static void forceRDTUIPluginToLoad() { try { Bundle b = Platform.getBundle("com.aptana.rdt.ui"); if (b == null) return; Class c = b.loadClass("com.aptana.rdt.ui.AptanaRDTUIPlugin"); if (c == null) return; c.newInstance(); } catch (Exception e) { // ignore } } /** * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) */ public void stop(BundleContext context) throws Exception { try { if (fWorkingCopyManager != null) { fWorkingCopyManager.shutdown(); fWorkingCopyManager = null; } if (fDocumentProvider != null) { fDocumentProvider.shutdown(); fDocumentProvider = null; } if (textTools != null) { textTools.dispose(); textTools = null; } if (fTypeFilter != null) { fTypeFilter.dispose(); fTypeFilter = null; } if (fMembersOrderPreferenceCache != null) { fMembersOrderPreferenceCache.dispose(); fMembersOrderPreferenceCache = null; } if (fRubyExplorerTracker != null) { PackageExplorerPart explorer = PackageExplorerPart.getFromActivePerspective(); if (explorer != null) { explorer.getSite().getSelectionProvider().removeSelectionChangedListener(fRubyExplorerTracker); } fRubyExplorerTracker = null; } if (launchListener != null) { DebugPlugin.getDefault().getLaunchManager().removeLaunchListener(launchListener); launchListener = null; } } finally { super.stop(context); } } /** * @param string */ public static void log(String string) { log(IStatus.OK, string); } /** * Gets the default plugin * * @return - ruby plugin */ public static RubyPlugin getDefault() { return plugin; } /** * Gets the workspace * * @return - workspace */ public static IWorkspace getWorkspace() { return RubyCore.getWorkspace(); } /** * Gets the active workbench window * * @return - workbench window */ public static IWorkbenchWindow getActiveWorkbenchWindow() { return getDefault().getWorkbench().getActiveWorkbenchWindow(); } /** * Logs a status object * * @param status */ public static void log(IStatus status) { getDefault().getLog().log(status); System.out.println(status.getMessage()); if (status.getException() != null) status.getException().printStackTrace(); } /** * Logs a throwable object * * @param e */ public static void log(Throwable e) { log(new Status(IStatus.ERROR, PLUGIN_ID, IStatus.ERROR, RubyUIMessages.RdtUiPlugin_internalErrorOccurred, e)); } /** * Logs a message with throwable * * @param severity * @param message * @param e */ public static void log(int severity, String message, Throwable e) { Status status = new Status(severity, PLUGIN_ID, IStatus.OK, message, e); RubyPlugin.log(status); } /** * Gets the active workbench shell * * @return - shell */ public static Shell getActiveWorkbenchShell() { return getActiveWorkbenchWindow().getShell(); } /** * Gets the ruby text tools * * @return - ruby text tools */ public synchronized RubyTextTools getRubyTextTools() { if (textTools == null) textTools = new RubyTextTools(getPreferenceStore(), RubyCore.getPlugin().getPluginPreferences()); return textTools; } /** * Gets the code formatter * * @return - code formatter */ public OldCodeFormatter getCodeFormatter() { return new OldCodeFormatter(RubyCore.getOptions()); } /** * Gets the active page * * @return - page */ public static IWorkbenchPage getActivePage() { IWorkbenchWindow window = getDefault().getWorkbench().getActiveWorkbenchWindow(); if (window == null) return null; return window.getActivePage(); } /** * Gets the ruby file matcher * * @return - ruby file matcher */ public RubyFileMatcher getRubyFileMatcher() { // be lazy in Plugin class if (rubyFileMatcher == null) { rubyFileMatcher = new RubyFileMatcher(); } return rubyFileMatcher; } /** * Gets the selected resource * * @return - resource */ public IResource getSelectedResource() { IWorkbenchPage page = RubyPlugin.getActivePage(); if (page == null) { return null; } // first try: a selection in the navigator or ruby resource view ISelection selection = page.getSelection(); if (selection instanceof IStructuredSelection && !selection.isEmpty()) { IStructuredSelection structuredSelection = (IStructuredSelection) selection; Object obj = structuredSelection.getFirstElement(); if (obj instanceof IResource) { return (IResource) obj; } } // second try: an editor is selected IEditorPart part = page.getActiveEditor(); if (part == null) { return null; } IEditorInput input = part.getEditorInput(); return (IResource) input.getAdapter(IResource.class); } /** * True is file is a ruby file * * @param file * @return - true if ruby file */ public boolean isRubyFile(IFile file) { // TODO: this is work in progress. Once we can use the content-type // extension point, this method must be removed return this.getRubyFileMatcher().hasRubyEditorAssociation(file); } /** * True if resource is a ruby file * * @param resource * @return - true if ruby file */ public boolean isRubyFile(IResource resource) { if (resource == null || !(resource instanceof IFile)) { return false; } return isRubyFile((IFile) resource); } /** * @return - working copy manager */ public WorkingCopyManager getWorkingCopyManager() { if (fWorkingCopyManager == null) { RubyDocumentProvider provider = getRubyDocumentProvider(); fWorkingCopyManager = new WorkingCopyManager(provider); } return fWorkingCopyManager; } /** * Gets the ruby document provider * * @return - ruby doc provider */ public synchronized RubyDocumentProvider getRubyDocumentProvider() { if (fDocumentProvider == null) fDocumentProvider = new RubyDocumentProvider(); return fDocumentProvider; } /** * Returns the registry of the extensions to the <code>org.rubypeople.rdt.ui.rubyFoldingStructureProvider</code> * extension point. * * @return the registry of contributed <code>IRubyFoldingStructureProvider</code> * @since 3.0 */ public synchronized RubyFoldingStructureProviderRegistry getFoldingStructureProviderRegistry() { if (fFoldingStructureProviderRegistry == null) fFoldingStructureProviderRegistry = new RubyFoldingStructureProviderRegistry(); return fFoldingStructureProviderRegistry; } /** * @return - plugin id */ public static String getPluginId() { return PLUGIN_ID; } /** * @param severity * @param string */ public static void log(int severity, String string) { log(new Status(severity, PLUGIN_ID, IStatus.OK, string, null)); } /** * Returns a combined preference store, this store is read-only. * * @return the combined preference store * @since 3.0 */ public IPreferenceStore getCombinedPreferenceStore() { if (fCombinedPreferenceStore == null) { IPreferenceStore generalTextStore = EditorsUI.getPreferenceStore(); fCombinedPreferenceStore = new ChainedPreferenceStore(new IPreferenceStore[] { getPreferenceStore(), new PreferencesAdapter(RubyCore.getPlugin().getPluginPreferences()), generalTextStore }); } return fCombinedPreferenceStore; } /** * Logs an error message * * @param message */ public static void logErrorMessage(String message) { log(new Status(IStatus.ERROR, getPluginId(), IRubyStatusConstants.INTERNAL_ERROR, message, null)); } /** * Returns the template store for the java editor templates. * * @return the template store for the ruby editor templates * @since 3.0 */ public TemplateStore getTemplateStore() { return RubyTemplateAccess.getDefault().getTemplateStore(); } /** * Returns the template context type registry for the ruby plugin. * * @return the template context type registry for the ruby plugin * @since 3.0 */ public ContextTypeRegistry getTemplateContextRegistry() { return RubyTemplateAccess.getDefault().getContextTypeRegistry(); } /** * Is this plugin debugging * * @return - true if debugging */ public static boolean isDebug() { return getDefault().isDebugging(); } /** * Creates the Ruby plugin standard groups in a context menu. * * @param menu * the menu manager to be populated */ public static void createStandardGroups(IMenuManager menu) { if (!menu.isEmpty()) return; menu.add(new Separator(IContextMenuConstants.GROUP_NEW)); menu.add(new GroupMarker(IContextMenuConstants.GROUP_GOTO)); menu.add(new Separator(IContextMenuConstants.GROUP_OPEN)); menu.add(new GroupMarker(IContextMenuConstants.GROUP_SHOW)); menu.add(new Separator(ICommonMenuConstants.GROUP_EDIT)); menu.add(new Separator(IContextMenuConstants.GROUP_REORGANIZE)); menu.add(new Separator(IContextMenuConstants.GROUP_GENERATE)); menu.add(new Separator(IContextMenuConstants.GROUP_SEARCH)); menu.add(new Separator(IContextMenuConstants.GROUP_BUILD)); menu.add(new Separator(IContextMenuConstants.GROUP_ADDITIONS)); menu.add(new Separator(IContextMenuConstants.GROUP_VIEWER_SETUP)); menu.add(new Separator(IContextMenuConstants.GROUP_PROPERTIES)); } /** * Gets the image descriptor registry * * @return - image descriptor registry */ public static ImageDescriptorRegistry getImageDescriptorRegistry() { return getDefault().internalGetImageDescriptorRegistry(); } private synchronized ImageDescriptorRegistry internalGetImageDescriptorRegistry() { if (fImageDescriptorRegistry == null) fImageDescriptorRegistry = new ImageDescriptorRegistry(); return fImageDescriptorRegistry; } /** * Gets members order preference cache * * @return - cache */ public synchronized MembersOrderPreferenceCache getMemberOrderPreferenceCache() { // initialized on startup return fMembersOrderPreferenceCache; } /** * Gets the external document provider * * @return - ruby script doc provider */ public synchronized RubyScriptDocumentProvider getExternalDocumentProvider() { if (fExternalRubyDocumentProvider == null) fExternalRubyDocumentProvider = new RubyScriptDocumentProvider(); return fExternalRubyDocumentProvider; } /** * Gets the plugin properties * * @return - property resource bundle */ public PropertyResourceBundle getPluginProperties() { if (pluginProperties == null) { try { pluginProperties = new PropertyResourceBundle( FileLocator.openStream(this.getBundle(), new Path("plugin.properties"), false)); } catch (IOException e) { log(e); } } return pluginProperties; } /** * Gets the type filter * * @return - type filter */ public synchronized TypeFilter getTypeFilter() { if (fTypeFilter == null) fTypeFilter = new TypeFilter(); return fTypeFilter; } /** * Returns a section in the Ruby plugin's dialog settings. If the section doesn't exist yet, it is created. * * @param name * the name of the section * @return the section of the given name * @since 1.0 */ public IDialogSettings getDialogSettingsSection(String name) { IDialogSettings dialogSettings = getDialogSettings(); IDialogSettings section = dialogSettings.getSection(name); if (section == null) { section = dialogSettings.addNewSection(name); } return section; } /** * Returns all Ruby editor text hovers contributed to the workbench. * * @return an array of RubyEditorTextHoverDescriptor * @since 1.0 */ public RubyEditorTextHoverDescriptor[] getRubyEditorTextHoverDescriptors() { Preferences prefs = getPluginPreferences(); if (prefs != null && !prefs.getBoolean(PreferenceConstants.HOVERS_ENABLED)) { return new RubyEditorTextHoverDescriptor[0]; } if (fRubyEditorTextHoverDescriptors == null) { fRubyEditorTextHoverDescriptors = RubyEditorTextHoverDescriptor.getContributedHovers(); ConfigurationElementSorter sorter = new ConfigurationElementSorter() { /* * @see org.eclipse.ui.texteditor.ConfigurationElementSorter#getConfigurationElement(java.lang.Object) */ public IConfigurationElement getConfigurationElement(Object object) { return ((RubyEditorTextHoverDescriptor) object).getConfigurationElement(); } }; sorter.sort(fRubyEditorTextHoverDescriptors); // Move Best Match hover to front for (int i = 0; i < fRubyEditorTextHoverDescriptors.length - 1; i++) { if (PreferenceConstants.ID_BESTMATCH_HOVER.equals(fRubyEditorTextHoverDescriptors[i].getId())) { RubyEditorTextHoverDescriptor hoverDescriptor = fRubyEditorTextHoverDescriptors[i]; for (int j = i; j > 0; j--) fRubyEditorTextHoverDescriptors[j] = fRubyEditorTextHoverDescriptors[j - 1]; fRubyEditorTextHoverDescriptors[0] = hoverDescriptor; break; } } } return fRubyEditorTextHoverDescriptors; } /** * Gets the problem marker manager * * @return - problem marker manager */ public synchronized ProblemMarkerManager getProblemMarkerManager() { if (fProblemMarkerManager == null) fProblemMarkerManager = new ProblemMarkerManager(); return fProblemMarkerManager; } /** * Returns the AST provider. * * @return the AST provider * @since 3.0 */ public synchronized ASTProvider getASTProvider() { if (fASTProvider == null) fASTProvider = new ASTProvider(); return fASTProvider; } /** * Gets the project tracker * * @return - project trakcer */ public RubyExplorerTracker getProjectTracker() { if (fRubyExplorerTracker == null) { fRubyExplorerTracker = new RubyExplorerTracker(); } return fRubyExplorerTracker; } /** * 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(PLUGIN_ID, path); //$NON-NLS-1$ } }