/******************************************************************************* * Copyright (c) 2006 Business Objects Software Limited and others. * All rights reserved. * This file is made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Business Objects Software Limited - initial API and implementation *******************************************************************************/ /* * CALEditor.java * Creation date: Jan 27, 2006. * By: Edward Lam */ package org.openquark.cal.eclipse.ui.caleditor; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; import java.text.BreakIterator; import java.text.CharacterIterator; import java.text.MessageFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.ResourceBundle; import java.util.Stack; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFileModificationValidator; import org.eclipse.core.resources.IStorage; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.ResourceAttributes; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.ListenerList; import org.eclipse.core.runtime.Status; import org.eclipse.jdt.internal.core.JarEntryFile; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.GroupMarker; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IContributionItem; import org.eclipse.jface.action.IContributionManagerOverrides; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IStatusLineManager; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.dialogs.IMessageProvider; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.BadPositionCategoryException; import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; import org.eclipse.jface.text.DefaultInformationControl; import org.eclipse.jface.text.DefaultLineTracker; import org.eclipse.jface.text.DocumentCommand; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.DocumentRewriteSession; import org.eclipse.jface.text.DocumentRewriteSessionType; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentExtension; import org.eclipse.jface.text.IDocumentListener; import org.eclipse.jface.text.IInformationControl; import org.eclipse.jface.text.IInformationControlCreator; import org.eclipse.jface.text.ILineTracker; import org.eclipse.jface.text.IPositionUpdater; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextOperationTarget; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.text.ITextViewerExtension; import org.eclipse.jface.text.ITypedRegion; import org.eclipse.jface.text.IWidgetTokenKeeper; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.TextSelection; import org.eclipse.jface.text.TextUtilities; import org.eclipse.jface.text.TextViewer; import org.eclipse.jface.text.formatter.IFormattingContext; import org.eclipse.jface.text.information.IInformationPresenter; import org.eclipse.jface.text.information.InformationPresenter; import org.eclipse.jface.text.link.ILinkedModeListener; import org.eclipse.jface.text.link.LinkedModeModel; import org.eclipse.jface.text.link.LinkedModeUI; import org.eclipse.jface.text.link.LinkedPosition; import org.eclipse.jface.text.link.LinkedPositionGroup; import org.eclipse.jface.text.link.LinkedModeUI.ExitFlags; import org.eclipse.jface.text.link.LinkedModeUI.IExitPolicy; import org.eclipse.jface.text.reconciler.IReconciler; import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.IAnnotationAccessExtension; import org.eclipse.jface.text.source.IOverviewRuler; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.text.source.ISourceViewerExtension2; import org.eclipse.jface.text.source.IVerticalRuler; import org.eclipse.jface.text.source.IVerticalRulerInfo; import org.eclipse.jface.text.source.SourceViewerConfiguration; import org.eclipse.jface.text.source.projection.ProjectionSupport; import org.eclipse.jface.text.source.projection.ProjectionViewer; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.DecoratingLabelProvider; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.TreePath; import org.eclipse.jface.viewers.TreeSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.window.Window; import org.eclipse.search.ui.ISearchPageContainer; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ST; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.custom.VerifyKeyListener; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseListener; import org.eclipse.swt.events.VerifyEvent; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.Shell; import org.eclipse.team.core.RepositoryProvider; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.IPartListener2; import org.eclipse.ui.IStorageEditorInput; import org.eclipse.ui.IWindowListener; import org.eclipse.ui.IWorkbenchActionConstants; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPartReference; import org.eclipse.ui.IWorkbenchPartSite; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.WorkbenchException; import org.eclipse.ui.XMLMemento; import org.eclipse.ui.dialogs.SaveAsDialog; import org.eclipse.ui.editors.text.DefaultEncodingSupport; import org.eclipse.ui.editors.text.EditorsUI; import org.eclipse.ui.editors.text.IEncodingSupport; import org.eclipse.ui.editors.text.ITextEditorHelpContextIds; import org.eclipse.ui.part.FileEditorInput; import org.eclipse.ui.texteditor.AbstractDecoratedTextEditor; import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants; import org.eclipse.ui.texteditor.AbstractMarkerAnnotationModel; import org.eclipse.ui.texteditor.ChainedPreferenceStore; import org.eclipse.ui.texteditor.ContentAssistAction; import org.eclipse.ui.texteditor.IDocumentProvider; import org.eclipse.ui.texteditor.ITextEditor; import org.eclipse.ui.texteditor.ITextEditorActionConstants; import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds; import org.eclipse.ui.texteditor.ITextEditorExtension; import org.eclipse.ui.texteditor.IUpdate; import org.eclipse.ui.texteditor.SelectMarkerRulerAction; import org.eclipse.ui.texteditor.SourceViewerDecorationSupport; import org.eclipse.ui.texteditor.TextNavigationAction; import org.eclipse.ui.texteditor.TextOperationAction; import org.eclipse.ui.texteditor.link.EditorLinkedModeUI; import org.eclipse.ui.views.contentoutline.ContentOutlinePage; import org.eclipse.ui.views.contentoutline.IContentOutlinePage; import org.openquark.cal.compiler.ClassInstance; import org.openquark.cal.compiler.CompilerMessageLogger; import org.openquark.cal.compiler.LanguageInfo; import org.openquark.cal.compiler.MessageLogger; import org.openquark.cal.compiler.ModuleContainer; import org.openquark.cal.compiler.ModuleName; import org.openquark.cal.compiler.ModuleTypeInfo; import org.openquark.cal.compiler.QualifiedName; import org.openquark.cal.compiler.ScopedEntity; import org.openquark.cal.compiler.SourceMetricsManager; import org.openquark.cal.compiler.SourcePosition; import org.openquark.cal.compiler.SourceRange; import org.openquark.cal.compiler.ModuleContainer.ISourceManager; import org.openquark.cal.compiler.SearchResult.Precise; import org.openquark.cal.compiler.SourceModel.FunctionDefn; import org.openquark.cal.compiler.SourceModel.FunctionTypeDeclaration; import org.openquark.cal.compiler.SourceModel.InstanceDefn; import org.openquark.cal.compiler.SourceModel.SourceElement; import org.openquark.cal.compiler.SourceModel.TypeClassDefn; import org.openquark.cal.compiler.SourceModel.TypeConstructorDefn; import org.openquark.cal.compiler.SourceModel.FunctionDefn.Primitive; import org.openquark.cal.compiler.SourceModel.TypeClassDefn.ClassMethodDefn; import org.openquark.cal.compiler.SourceModel.TypeConstructorDefn.AlgebraicType; import org.openquark.cal.compiler.SourceModel.TypeConstructorDefn.AlgebraicType.DataConsDefn; import org.openquark.cal.eclipse.core.CALEclipseCorePlugin; import org.openquark.cal.eclipse.core.CALModelManager; import org.openquark.cal.eclipse.core.CoreOptionIDs; import org.openquark.cal.eclipse.core.CALModelManager.SourceManagerFactory; import org.openquark.cal.eclipse.core.formatter.DefaultCodeFormatterConstants; import org.openquark.cal.eclipse.core.util.Util; import org.openquark.cal.eclipse.ui.CALEclipseUIPlugin; import org.openquark.cal.eclipse.ui.CALHelpContextIds; import org.openquark.cal.eclipse.ui.IContextMenuConstants; import org.openquark.cal.eclipse.ui.actions.ActionMessages; import org.openquark.cal.eclipse.ui.actions.ActionUtilities; import org.openquark.cal.eclipse.ui.actions.AddBlockCommentAction; import org.openquark.cal.eclipse.ui.actions.CALEditorActionDefinitionIds; import org.openquark.cal.eclipse.ui.actions.GeneralActionGroup; import org.openquark.cal.eclipse.ui.actions.GenerateActionGroup; import org.openquark.cal.eclipse.ui.actions.GotoElementAction; import org.openquark.cal.eclipse.ui.actions.IndentAction; import org.openquark.cal.eclipse.ui.actions.OpenDeclarationAction; import org.openquark.cal.eclipse.ui.actions.QuickSearchInWorkspace; import org.openquark.cal.eclipse.ui.actions.RemoveBlockCommentAction; import org.openquark.cal.eclipse.ui.actions.RenameAction; import org.openquark.cal.eclipse.ui.actions.ShowTooltipDescriptionAction; import org.openquark.cal.eclipse.ui.preferences.PreferenceConstants; import org.openquark.cal.eclipse.ui.text.CALHeuristicScanner; import org.openquark.cal.eclipse.ui.text.CALPairMatcher; import org.openquark.cal.eclipse.ui.text.CALPartitions; import org.openquark.cal.eclipse.ui.text.CALSourceViewerConfiguration; import org.openquark.cal.eclipse.ui.text.CALTextTools; import org.openquark.cal.eclipse.ui.text.CALWordIterator; import org.openquark.cal.eclipse.ui.text.DocumentCharacterIterator; import org.openquark.cal.eclipse.ui.text.HTMLTextPresenter; import org.openquark.cal.eclipse.ui.text.PreferencesAdapter; import org.openquark.cal.eclipse.ui.text.SmartBackspaceManager; import org.openquark.cal.eclipse.ui.text.Symbols; import org.openquark.cal.eclipse.ui.util.CodeFormatterUtil; import org.openquark.cal.eclipse.ui.util.CoreUtility; import org.openquark.cal.eclipse.ui.views.CALModuleContentProvider; import org.openquark.cal.eclipse.ui.views.CALWorkspace; import org.openquark.cal.eclipse.ui.views.ForeignDecorator; import org.openquark.cal.eclipse.ui.views.ModuleTreeContentProvider; import org.openquark.cal.eclipse.ui.views.ModuleTreeLabelProvider; import org.openquark.cal.eclipse.ui.views.OutlineTreeContentProvider; import org.openquark.cal.eclipse.ui.views.ProblemMarkerDecorator; import org.openquark.cal.eclipse.ui.views.ScopeDecorator; import org.openquark.util.Pair; import org.openquark.util.UnsafeCast; /** * A CAL-specific text editor. * * @author Edward Lam */ public class CALEditor extends AbstractDecoratedTextEditor { /** Preference key for matching brackets */ protected final static String MATCHING_BRACKETS = PreferenceConstants.EDITOR_MATCHING_BRACKETS; /** Preference key for matching brackets color */ protected final static String MATCHING_BRACKETS_COLOR = PreferenceConstants.EDITOR_MATCHING_BRACKETS_COLOR; protected final static char[] BRACKETS = { '{', '}', '(', ')', '[', ']', '<', '>' }; /** The information presenter. */ private InformationPresenter fInformationPresenter; /** * The internal shell activation listener for updating occurrences. */ private final ActivationListener fActivationListener= new ActivationListener(); private OpenDeclarationAction openDeclarationAction; /** * The editor selection changed listener. */ private EditorSelectionChangedListener fEditorSelectionChangedListener; /** * The encoding support for the editor. */ protected DefaultEncodingSupport encodingSupport; /** * This editor's projection support */ private ProjectionSupport fProjectionSupport; /** The editor's bracket matcher */ protected CALPairMatcher fBracketMatcher = new CALPairMatcher(BRACKETS); /** * The folding runner. */ private ToggleFoldingRunner fFoldingRunner; /** The bracket inserter. */ private final BracketInserter fBracketInserter = new BracketInserter(); // These variables are used for the outline view. private CALOutlinePage outlinePage; private ModuleTreeContentProvider moduleTreeContentProvider; private static final CALModelManager calModelManager = CALModelManager.getCALModelManager(); /** The standard action groups added to the menu */ private GenerateActionGroup generateActionGroup; private GeneralActionGroup refactorActionGroup; private GeneralActionGroup searchReferencesActionGroup; private GeneralActionGroup searchDeclarationsActionGroup; /** * Listeners to the changes of the current selection of the editor. */ ListenerList selectedEntitiesListeners = new ListenerList(); /* * TODOEL: * This is mostly copied from TextViewer. * - Change font from Colors and Fonts preference page */ /** * Creates a new text editor. */ public CALEditor() { // TEMP: Assign an auto-indent strategy. ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer instanceof TextViewer) { ((TextViewer)getSourceViewer()).prependAutoEditStrategy(new DefaultIndentLineAutoEditStrategy(), "org.openquark.cal.eclipse.core.calSource"); } setDocumentProvider(CALEclipseUIPlugin.getDefault().getCALDocumentProvider()); setEditorContextMenuId("#CALEditorContext"); //$NON-NLS-1$ setRulerContextMenuId("#CALRulerContext"); //$NON-NLS-1$ setHelpContextId(ITextEditorHelpContextIds.TEXT_EDITOR); // Note: the Java editor installs its own help context (CompilationUnitEditor.java). } @Override protected void initializeKeyBindingScopes() { setKeyBindingScopes(new String[] { "org.openquark.cal.eclipse.ui.calEditorScope" }); //$NON-NLS-1$ } /** * {@inheritDoc} */ @Override protected void initializeEditor() { IPreferenceStore store = createCombinedPreferenceStore(null); setPreferenceStore(store); CALTextTools textTools = CALEclipseUIPlugin.getDefault().getCALTextTools(); setSourceViewerConfiguration(new CALSourceViewerConfiguration(textTools.getColorManager(), store, this, CALPartitions.CAL_PARTITIONING)); } public final ISourceViewer getViewer() { return getSourceViewer(); } /** * Creates and returns the preference store for this CAL editor with the given input. * * @param input The editor input for which to create the preference store * @return the preference store for this editor */ private IPreferenceStore createCombinedPreferenceStore(IEditorInput input) { List<IPreferenceStore> stores = new ArrayList<IPreferenceStore>(3); /* * TODOEL: Add project scoped preferences: */ // From JavaEditor: // IJavaProject project = EditorUtility.getJavaProject(input); // if (project != null) { // stores.add(new EclipsePreferencesAdapter(new ProjectScope(project.getProject()), JavaCore.PLUGIN_ID)); // } loadState(); stores.add(CALEclipseUIPlugin.getDefault().getPreferenceStore()); stores.add(new PreferencesAdapter(CALEclipseCorePlugin.getDefault().getPluginPreferences())); stores.add(EditorsUI.getPreferenceStore()); return new ChainedPreferenceStore(stores.toArray(new IPreferenceStore[stores.size()])); } private final String section_name = "org.openquark.cal.eclipse.ui.caleditor.CALEditor"; //$NON-NLS-1$ private final String memento_key = "memento"; //$NON-NLS-1$ /* * @see IWorkbenchPart#dispose() */ @Override public void dispose() { saveState(); ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer instanceof ITextViewerExtension) { ((ITextViewerExtension)sourceViewer).removeVerifyKeyListener(fBracketInserter); } if (outlinePage != null){ outlinePage.dispose(); selectedEntitiesListeners.remove(outlinePage); outlinePage = null; } if (encodingSupport != null) { encodingSupport.dispose(); encodingSupport = null; } super.dispose(); if (fProjectionSupport != null) { fProjectionSupport.dispose(); fProjectionSupport = null; } } /** * Installs the encoding support on the given text editor. * <p> * Subclasses may override to install their own encoding * support or to disable the default encoding support. * </p> */ protected void installEncodingSupport() { encodingSupport = new DefaultEncodingSupport(); encodingSupport.initialize(this); } @Override protected void performSave(boolean overwrite, IProgressMonitor progressMonitor){ super.performSave(overwrite, progressMonitor); { IDocument document = getDocumentProvider().getDocument(getEditorInput()); if (document instanceof PartiallySynchronizedDocument){ PartiallySynchronizedDocument psd = (PartiallySynchronizedDocument) document; psd.wasSaved(); } } } /** * The <code>TextEditor</code> implementation of this <code>AbstractTextEditor</code> * method asks the user for the workspace path of a file resource and saves the document there. * * can only do a saveas if not read only. * can only do a saveas if the input if from a file (not a jar) * * @param progressMonitor the progress monitor to be used */ @Override protected void performSaveAs(IProgressMonitor progressMonitor) { if (!isEditable()) { return; } IEditorInput input = getEditorInput(); if (input instanceof IFileEditorInput) { return; } Shell shell = getSite().getShell(); SaveAsDialog dialog = new SaveAsDialog(shell); IFile original = ((IFileEditorInput) input).getFile(); if (original != null) { dialog.setOriginalFile(original); } dialog.create(); IDocumentProvider provider = getDocumentProvider(); if (provider == null) { // editor has programmatically been closed while the dialog was open return; } if (provider.isDeleted(input) && original != null) { String message= MessageFormat.format("The original file ''{0}'' has been deleted.", new Object[] { original.getName() }); dialog.setErrorMessage(null); dialog.setMessage(message, IMessageProvider.WARNING); } if (dialog.open() == Window.CANCEL) { if (progressMonitor != null) { progressMonitor.setCanceled(true); } return; } IPath filePath = dialog.getResult(); if (filePath == null) { if (progressMonitor != null) { progressMonitor.setCanceled(true); } return; } IWorkspace workspace = ResourcesPlugin.getWorkspace(); IFile file = workspace.getRoot().getFile(filePath); final IEditorInput newInput = new FileEditorInput(file); boolean success = false; try { provider.aboutToChange(newInput); provider.saveDocument(progressMonitor, newInput, provider.getDocument(input), true); success= true; } catch (CoreException x) { IStatus status = x.getStatus(); if (status == null || status.getSeverity() != IStatus.CANCEL) { // TextEditorMessages.Editor_error_save_title; String title = "Problems During Save As..."; // TextEditorMessages.Editor_error_save_message String msg = MessageFormat.format("The original file ''{0}'' has been deleted.", new Object[] { x.getMessage() }); if (status != null) { switch (status.getSeverity()) { case IStatus.INFO: MessageDialog.openInformation(shell, title, msg); break; case IStatus.WARNING: MessageDialog.openWarning(shell, title, msg); break; default: MessageDialog.openError(shell, title, msg); } } else { MessageDialog.openError(shell, title, msg); } } } finally { provider.changed(newInput); if (success) { setInput(newInput); } } if (progressMonitor != null) { progressMonitor.setCanceled(!success); } } /* * @see org.eclipse.ui.part.EditorPart#isSaveAsAllowed() */ @Override public boolean isSaveAsAllowed() { return true; } /* * @see AbstractTextEditor#createActions() */ @Override protected void createActions() { installEncodingSupport(); super.createActions(); Action action = new IndentAction(CALEditorMessages.getBundleForConstructedKeys(), "Indent.", this, false); //$NON-NLS-1$ action.setActionDefinitionId(CALEditorActionDefinitionIds.INDENT); setAction("Indent", action); //$NON-NLS-1$ markAsStateDependentAction("Indent", true); //$NON-NLS-1$ markAsSelectionDependentAction("Indent", true); //$NON-NLS-1$ PlatformUI.getWorkbench().getHelpSystem().setHelp(action, CALHelpContextIds.INDENT_ACTION); action = new IndentAction(CALEditorMessages.getBundleForConstructedKeys(), "Indent.", this, true); //$NON-NLS-1$ setAction("IndentOnTab", action); //$NON-NLS-1$ markAsStateDependentAction("IndentOnTab", true); //$NON-NLS-1$ markAsSelectionDependentAction("IndentOnTab", true); //$NON-NLS-1$ if (getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_SMART_TAB)) { // don't replace Shift Right - have to make sure their enablement is mutually exclusive // removeActionActivationCode(ITextEditorActionConstants.SHIFT_RIGHT); setActionActivationCode("IndentOnTab", '\t', -1, SWT.NONE); //$NON-NLS-1$ } // Set up open declaration action { openDeclarationAction = new OpenDeclarationAction(CALEditorMessages.getBundleForConstructedKeys(), "OpenDeclaration.", this); //$NON-NLS-1$ openDeclarationAction.setActionDefinitionId(CALEditorActionDefinitionIds.OPENDECLARATION_COMMAND); setAction("OpenDeclaration", openDeclarationAction); //$NON-NLS-1$ } // Set up rename action { action = new RenameAction(CALEditorMessages.getBundleForConstructedKeys(), "Rename.", this); //$NON-NLS-1$ action.setActionDefinitionId(CALEditorActionDefinitionIds.RENAMEACTION); setAction("Rename", action); //$NON-NLS-1$ } { action = new QuickSearchInWorkspace(CALEditorMessages.getBundleForConstructedKeys(), "SearchReferencesInWorkspace.", this, ISearchPageContainer.WORKSPACE_SCOPE, QuickSearchInWorkspace.REFERENCES); //$NON-NLS-1$ action.setActionDefinitionId(CALEditorActionDefinitionIds.SEARCH_REFERENCES_IN_WORKSPACE); setAction("SearchReferencesInWorkspace", action); //$NON-NLS-1$ } { action = new QuickSearchInWorkspace(CALEditorMessages.getBundleForConstructedKeys(), "SearchReferencesInProject.", this, ISearchPageContainer.SELECTED_PROJECTS_SCOPE, QuickSearchInWorkspace.REFERENCES); //$NON-NLS-1$ action.setActionDefinitionId(CALEditorActionDefinitionIds.SEARCH_REFERENCES_IN_PROJECT); setAction("SearchReferencesInProject", action); //$NON-NLS-1$ } { action = new QuickSearchInWorkspace(CALEditorMessages.getBundleForConstructedKeys(), "SearchDeclarationsInWorkspace.", this, ISearchPageContainer.WORKSPACE_SCOPE, QuickSearchInWorkspace.DEFINITIONS); //$NON-NLS-1$ action.setActionDefinitionId(CALEditorActionDefinitionIds.SEARCH_DECLARATIONS_IN_WORKSPACE); setAction("SearchDeclarationsInWorkspace", action); //$NON-NLS-1$ } { action = new QuickSearchInWorkspace(CALEditorMessages.getBundleForConstructedKeys(), "SearchDeclarationsInProject.", this, ISearchPageContainer.SELECTED_PROJECTS_SCOPE, QuickSearchInWorkspace.DEFINITIONS); //$NON-NLS-1$ action.setActionDefinitionId(CALEditorActionDefinitionIds.SEARCH_DECLARATIONS_IN_PROJECTS); setAction("SearchDeclarationsInProject", action); //$NON-NLS-1$ } { action= new TextOperationAction(CALEditorMessages.getBundleForConstructedKeys(),"ShowOutline.", this, CALSourceViewer.SHOW_OUTLINE, true); //$NON-NLS-1$ action.setActionDefinitionId(CALEditorActionDefinitionIds.SHOW_OUTLINE); setAction(CALEditorActionDefinitionIds.SHOW_OUTLINE, action); // PlatformUI.getWorkbench().getHelpSystem().setHelp(action, ICALHelpContextIds.SHOW_OUTLINE_ACTION); } // Set up show tooltip description action { action = new ShowTooltipDescriptionAction(CALEditorMessages.getBundleForConstructedKeys(), "ShowTooltipDescription.", this); //$NON-NLS-1$ action.setActionDefinitionId(CALEditorActionDefinitionIds.SHOWTOOLTIPDESCRIPTION); setAction("ShowTooltipDescription", action); //$NON-NLS-1$ } // Set up goto next function action { action = new GotoElementAction(CALEditorMessages.getBundleForConstructedKeys(), "GotoNextFunction.", this, GotoElementAction.Direction.Next); //$NON-NLS-1$ action.setActionDefinitionId(CALEditorActionDefinitionIds.GOTO_NEXT_ELEMENT_COMMAND); setAction("GotoNextFunction", action); //$NON-NLS-1$ } // Set up goto next function action { action = new GotoElementAction(CALEditorMessages.getBundleForConstructedKeys(), "GotoPreviousFunction.", this, GotoElementAction.Direction.Previous); //$NON-NLS-1$ action.setActionDefinitionId(CALEditorActionDefinitionIds.GOTO_PREVIOUS_ELEMENT_COMMAND); setAction("GotoPreviousFunction", action); //$NON-NLS-1$ } // Set up the content assist action { action = new ContentAssistAction(CALEditorMessages.getBundleForConstructedKeys(), "ContentAssistProposal.", this); //$NON-NLS-1$ final String id = ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS; action.setActionDefinitionId(id); setAction("ContentAssistProposal", action); markAsStateDependentAction("ContentAssistProposal", true); } action = new TextOperationAction(CALEditorMessages.getBundleForConstructedKeys(), "Comment.", this, ITextOperationTarget.PREFIX); //$NON-NLS-1$ action.setActionDefinitionId(CALEditorActionDefinitionIds.COMMENT); setAction("Comment", action); //$NON-NLS-1$ markAsStateDependentAction("Comment", true); //$NON-NLS-1$ PlatformUI.getWorkbench().getHelpSystem().setHelp(action, CALHelpContextIds.COMMENT_ACTION); action = new TextOperationAction(CALEditorMessages.getBundleForConstructedKeys(), "Uncomment.", this, ITextOperationTarget.STRIP_PREFIX); //$NON-NLS-1$ action.setActionDefinitionId(CALEditorActionDefinitionIds.UNCOMMENT); setAction("Uncomment", action); //$NON-NLS-1$ markAsStateDependentAction("Uncomment", true); //$NON-NLS-1$ PlatformUI.getWorkbench().getHelpSystem().setHelp(action, CALHelpContextIds.UNCOMMENT_ACTION); action = new ToggleCommentAction(CALEditorMessages.getBundleForConstructedKeys(), "ToggleComment.", this); //$NON-NLS-1$ action.setActionDefinitionId(CALEditorActionDefinitionIds.TOGGLE_COMMENT); setAction("ToggleComment", action); //$NON-NLS-1$ markAsStateDependentAction("ToggleComment", true); //$NON-NLS-1$ PlatformUI.getWorkbench().getHelpSystem().setHelp(action, CALHelpContextIds.TOGGLE_COMMENT_ACTION); configureToggleCommentAction(); action = new GenerateElementCommentAction(CALEditorMessages.getBundleForConstructedKeys(), "GenerateElementComment.", this); //$NON-NLS-1$ action.setActionDefinitionId(CALEditorActionDefinitionIds.GENERATE_ELEMENT_COMMENT); setAction("GenerateElementComment", action); //$NON-NLS-1$ markAsStateDependentAction("GenerateElementComment", true); //$NON-NLS-1$ PlatformUI.getWorkbench().getHelpSystem().setHelp(action, CALHelpContextIds.GENERATE_ELEMENT_COMMENT_ACTION); action = new CleanImportsAction(CALEditorMessages.getBundleForConstructedKeys(), "CleanImports.", this); //$NON-NLS-1$ action.setActionDefinitionId(CALEditorActionDefinitionIds.CLEAN_IMPORTS); setAction("CleanImports", action); //$NON-NLS-1$ markAsStateDependentAction("CleanImports", true); //$NON-NLS-1$ PlatformUI.getWorkbench().getHelpSystem().setHelp(action, CALHelpContextIds.CLEAN_IMPORTS_ACTION); action = new TypeDeclarationInserter(CALEditorMessages.getBundleForConstructedKeys(), "TypeDeclarationInserter.", this); //$NON-NLS-1$ action.setActionDefinitionId(CALEditorActionDefinitionIds.TYPE_DECLARATION_INSERTER); setAction("TypeDeclarationInserter", action); //$NON-NLS-1$ markAsStateDependentAction("TypeDeclarationInserter", true); //$NON-NLS-1$ PlatformUI.getWorkbench().getHelpSystem().setHelp(action, CALHelpContextIds.TYPE_DECLARATION_INSERTER_ACTION); action = new PrettyPrinterAction(CALEditorMessages.getBundleForConstructedKeys(), "PrettyPrinter.", this); //$NON-NLS-1$ action.setActionDefinitionId(CALEditorActionDefinitionIds.PRETTY_PRINTER); setAction("PrettyPrinter", action); //$NON-NLS-1$ markAsStateDependentAction("PrettyPrinter", true); //$NON-NLS-1$ PlatformUI.getWorkbench().getHelpSystem().setHelp(action, CALHelpContextIds.PRETTY_PRINTER_ACTION); action = new AddBlockCommentAction(CALEditorMessages.getBundleForConstructedKeys(), "AddBlockComment.", this); //$NON-NLS-1$ action.setActionDefinitionId(CALEditorActionDefinitionIds.ADD_BLOCK_COMMENT); setAction("AddBlockComment", action); //$NON-NLS-1$ markAsStateDependentAction("AddBlockComment", true); //$NON-NLS-1$ markAsSelectionDependentAction("AddBlockComment", true); //$NON-NLS-1$ PlatformUI.getWorkbench().getHelpSystem().setHelp(action, CALHelpContextIds.ADD_BLOCK_COMMENT_ACTION); action = new RemoveBlockCommentAction(CALEditorMessages.getBundleForConstructedKeys(), "RemoveBlockComment.", this); //$NON-NLS-1$ action.setActionDefinitionId(CALEditorActionDefinitionIds.REMOVE_BLOCK_COMMENT); setAction("RemoveBlockComment", action); //$NON-NLS-1$ markAsStateDependentAction("RemoveBlockComment", true); //$NON-NLS-1$ markAsSelectionDependentAction("RemoveBlockComment", true); //$NON-NLS-1$ PlatformUI.getWorkbench().getHelpSystem().setHelp(action, CALHelpContextIds.REMOVE_BLOCK_COMMENT_ACTION); generateActionGroup = new GenerateActionGroup(this, ITextEditorActionConstants.GROUP_EDIT); refactorActionGroup = new GeneralActionGroup(this, ITextEditorActionConstants.GROUP_EDIT, "org.eclipse.jdt.ui.refactor.menu", "org.eclipse.jdt.ui.edit.text.java.refactor.quickMenu", ActionMessages.RefactorMenu_label){ @Override protected int fillSubMenu(IMenuManager source) { int added = 0; added += addEditorAction(source, "Rename"); //$NON-NLS-1$ return added; } }; searchReferencesActionGroup = new GeneralActionGroup(this, ITextEditorActionConstants.GROUP_FIND, "org.eclipse.jdt.ui.search.references.menu", null, ActionMessages.ReferencesMenu_label) { @Override protected int fillSubMenu(IMenuManager source) { int added = 0; added += addEditorAction(source, "SearchReferencesInWorkspace"); //$NON-NLS-1$ added += addEditorAction(source, "SearchReferencesInProject"); //$NON-NLS-1$ return added; } }; searchDeclarationsActionGroup = new GeneralActionGroup(this, ITextEditorActionConstants.GROUP_FIND, "org.eclipse.jdt.ui.search.declarations.menu", null, ActionMessages.DeclarationsMenu_label) { @Override protected int fillSubMenu(IMenuManager source) { int added = 0; added += addEditorAction(source, "SearchDeclarationsInWorkspace"); //$NON-NLS-1$ added += addEditorAction(source, "SearchDeclarationsInProject"); //$NON-NLS-1$ return added; } }; // This intercepts clicks on the lightbulb of compiler errors in order to // activate quick fixes. { setAction(ITextEditorActionConstants.RULER_CLICK, new CALEditorSelectAnnotationRulerAction( CALEditorMessages.getBundleForConstructedKeys(), "CALEditorSelectAnnotationRulerAction.", this, getVerticalRuler()) //$NON-NLS-1$ ); } } /** * Handle the ruler click on problem in order to show quick fixes when the lightbulb icon is clicked. */ public class CALEditorSelectAnnotationRulerAction extends SelectMarkerRulerAction { private boolean fIsEditable; private final ITextEditor fTextEditor; private Position fPosition; private final ResourceBundle fBundle; private final String fPrefix; public CALEditorSelectAnnotationRulerAction(ResourceBundle bundle, String prefix, ITextEditor editor, IVerticalRulerInfo ruler) { super(bundle, prefix, editor, ruler); fTextEditor = editor; fBundle = bundle; fPrefix = prefix; } @Override public void run() { runWithEvent(null); } /* * @see org.eclipse.jface.action.IAction#runWithEvent(org.eclipse.swt.widgets.Event) * @since 3.2 */ @Override public void runWithEvent(Event event) { if (fIsEditable) { ITextOperationTarget operation = (ITextOperationTarget) fTextEditor.getAdapter(ITextOperationTarget.class); final int opCode= ISourceViewer.QUICK_ASSIST; if (operation != null && operation.canDoOperation(opCode)) { fTextEditor.selectAndReveal(fPosition.getOffset(), fPosition.getLength()); operation.doOperation(opCode); } return; } super.run(); } @Override public void update() { checkReadOnly(); if (fIsEditable) { setEnabled(true); // super.update() might change this later initialize(fBundle, fPrefix + "QuickFix."); //$NON-NLS-1$ return; } super.update(); } private void checkReadOnly() { fPosition = null; fIsEditable = false; AbstractMarkerAnnotationModel model = getAnnotationModel(); IAnnotationAccessExtension annotationAccess = getAnnotationAccessExtension(); IDocument document= getDocument(); if (model == null) { return; } int layer = Integer.MIN_VALUE; for (Iterator<Annotation> iter = UnsafeCast.unsafeCast(model.getAnnotationIterator()); iter.hasNext(); ) { Annotation annotation = iter.next(); if (annotation.isMarkedDeleted()) { continue; } int annotationLayer = annotationAccess.getLayer(annotation); if (annotationAccess != null) { if (annotationLayer < layer) { continue; } } Position position = model.getPosition(annotation); if (!includesRulerLine(position, document)) { continue; } boolean isReadOnly = fTextEditor instanceof ITextEditorExtension && ((ITextEditorExtension)fTextEditor).isEditorInputReadOnly(); if (!isReadOnly) { fPosition = position; fIsEditable = true; layer = annotationLayer; continue; } } } } /* * @see StatusTextEditor#getStatusHeader(IStatus) */ @Override protected String getStatusHeader(IStatus status) { if (encodingSupport != null) { String message = encodingSupport.getStatusHeader(status); if (message != null) { return message; } } return super.getStatusHeader(status); } /* * @see StatusTextEditor#getStatusBanner(IStatus) */ @Override protected String getStatusBanner(IStatus status) { if (encodingSupport != null) { String message = encodingSupport.getStatusBanner(status); if (message != null) { return message; } } return super.getStatusBanner(status); } /* * @see StatusTextEditor#getStatusMessage(IStatus) */ @Override protected String getStatusMessage(IStatus status) { if (encodingSupport != null) { String message = encodingSupport.getStatusMessage(status); if (message != null) { return message; } } return super.getStatusMessage(status); } /* * @see AbstractTextEditor#doSetInput(IEditorInput) */ @Override protected void doSetInput(IEditorInput input) throws CoreException { CALSourceViewer sourceViewer = (CALSourceViewer)getSourceViewer(); if (sourceViewer == null) { setPreferenceStore(createCombinedPreferenceStore(input)); internalDoSetInput(input); return; } // uninstall & unregister preference store listener getSourceViewerDecorationSupport(sourceViewer).uninstall(); ((ISourceViewerExtension2)sourceViewer).unconfigure(); setPreferenceStore(createCombinedPreferenceStore(input)); // install & register preference store listener sourceViewer.configure(getSourceViewerConfiguration()); getSourceViewerDecorationSupport(sourceViewer).install(getPreferenceStore()); internalDoSetInput(input); configureTabConverter(); configureToggleCommentAction(); // if (fJavaEditorErrorTickUpdater != null) // fJavaEditorErrorTickUpdater.updateEditorImage(getInputJavaElement()); } private void internalDoSetInput(IEditorInput input) throws CoreException { CALSourceViewer calSourceViewer = (CALSourceViewer)getSourceViewer(); IPreferenceStore store = getPreferenceStore(); if (calSourceViewer != null && isFoldingEnabled() && (store == null || !store.getBoolean(PreferenceConstants.EDITOR_SHOW_SEGMENTS))) { calSourceViewer.prepareDelayedProjection(); } super.doSetInput(input); if (calSourceViewer != null && calSourceViewer.getReconciler() == null) { IReconciler reconciler= getSourceViewerConfiguration().getReconciler(calSourceViewer); if (reconciler != null) { reconciler.install(calSourceViewer); calSourceViewer.setReconciler(reconciler); } } if (encodingSupport != null) { encodingSupport.reset(); } } // Save the Properties settings. private void saveState(){ XMLMemento memento= XMLMemento.createWriteRoot("caleditor"); //$NON-NLS-1$ calModuleContentProvider.saveState(memento); { IPreferenceStore preferenceStore = CALEclipseUIPlugin.getDefault().getPreferenceStore(); memento.putInteger(PreferenceConstants.EDITOR_QUICK_OUTLINE_SHOW_PRIVATE_SYMBOLS, preferenceStore.getBoolean(PreferenceConstants.EDITOR_QUICK_OUTLINE_SHOW_PRIVATE_SYMBOLS) ? 1 : 0); memento.putInteger(PreferenceConstants.EDITOR_QUICK_OUTLINE_SHOW_ELEMENT_HIERARCHY, preferenceStore.getBoolean(PreferenceConstants.EDITOR_QUICK_OUTLINE_SHOW_ELEMENT_HIERARCHY) ? 1 : 0); } StringWriter writer= new StringWriter(); try { memento.save(writer); } catch (IOException e) { // ignore the failed save } IDialogSettings section= CALEclipseUIPlugin.getDefault().getDialogSettings().getSection(section_name); if (section == null) { section= CALEclipseUIPlugin.getDefault().getDialogSettings().addNewSection(section_name); } section.put(memento_key, writer.getBuffer().toString()); } public void loadState(){ // Load the properties state. XMLMemento memento = null; IDialogSettings section = CALEclipseUIPlugin.getDefault().getDialogSettings().getSection(section_name); if (section != null) { String settings = section.get(memento_key); if (settings != null) { try { memento = XMLMemento.createReadRoot(new StringReader(settings)); calModuleContentProvider.loadState(memento); { IPreferenceStore preferenceStore = CALEclipseUIPlugin.getDefault().getPreferenceStore(); { Integer value = memento.getInteger(PreferenceConstants.EDITOR_QUICK_OUTLINE_SHOW_PRIVATE_SYMBOLS); if (value != null){ preferenceStore.setDefault(PreferenceConstants.EDITOR_QUICK_OUTLINE_SHOW_PRIVATE_SYMBOLS, value == 1); } } { Integer value = memento.getInteger(PreferenceConstants.EDITOR_QUICK_OUTLINE_SHOW_ELEMENT_HIERARCHY); if (value != null){ preferenceStore.setDefault(PreferenceConstants.EDITOR_QUICK_OUTLINE_SHOW_ELEMENT_HIERARCHY, value == 1); } } } } catch (WorkbenchException e) { // skip the restore } } } } /** * This class is the adapter for the Outline view. This resuses the same content providers * as the CAL Workspace. */ class CALOutlinePage extends ContentOutlinePage implements SelectedEntitiesChanged { private ISelectionChangedListener selectionChangeListener_outlineView; private boolean syncEvent = false; public void selectedEntitiesChanged(LinkedList<Object> pathToEntity) { moduleTreeContentProvider.augmentPath(pathToEntity); TreeSelection treeSelection = new TreeSelection(new TreePath(pathToEntity.toArray())); // make sure that the selectionChanged listener does not update the current // position as though the user had selected an item in the tree view. syncEvent = true; getTreeViewer().setSelection(treeSelection); } @Override public void makeContributions( IMenuManager menuManager, IToolBarManager toolBarManager, IStatusLineManager statusLineManager) { moduleTreeContentProvider.fillLocalPullDown(menuManager); } @Override public void dispose(){ saveState(); super.dispose(); } @Override public void createControl(Composite parent) { super.createControl(parent); TreeViewer viewer = getTreeViewer(); DecoratingLabelProvider labelProvider = new DecoratingLabelProvider( new DecoratingLabelProvider( new DecoratingLabelProvider( new ModuleTreeLabelProvider(calModuleContentProvider), new ScopeDecorator()), new ProblemMarkerDecorator()), new ForeignDecorator()); // Set up the content providers viewer.setContentProvider(moduleTreeContentProvider = new OutlineTreeContentProvider (CALEditor.this, calModuleContentProvider, viewer, labelProvider)); viewer.setLabelProvider(labelProvider); viewer.addSelectionChangedListener(this); viewer.setInput(moduleTreeContentProvider.getRoot ()); // When an object is selected this function updated the editor to the current position. selectionChangeListener_outlineView = new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { // This was caused by the sync'ing of the editor current position so do not treat // this as though the user had selected the item. if (syncEvent){ syncEvent = false; return; } TreeSelection treeSelection = (TreeSelection) event.getSelection(); // Find the element select if any Object firstElement = treeSelection.getFirstElement(); if (firstElement != null){ Object entity = firstElement; SourceRange position = null; ModuleName moduleName = null; if (entity instanceof ScopedEntity){ QualifiedName name = ((ScopedEntity) entity).getName(); Precise searchPosition = calModelManager.getSourceMetrics().getPosition(name, CoreUtility.toCategory(entity)); // this can happen when the user uses the outline/workspace // page for navigation during a rebuild. if (searchPosition != null){ position = searchPosition.getSourceRange(); moduleName = name.getModuleName(); } } else if (entity instanceof ClassInstance){ ClassInstance classInstance = (ClassInstance) entity; MessageLogger logger = new MessageLogger(); position = calModelManager.getSourceMetrics().getPosition(classInstance, logger); moduleName = classInstance.getModuleName(); } if (position != null){ // Open the editor to the correct position IStorage definitionFile = calModelManager.getInputSourceFile(moduleName); IEditorPart editorPart; try { editorPart = CoreUtility.openInEditor(definitionFile, true); CoreUtility.showPosition(editorPart, definitionFile, position); setFocus(); } catch (PartInitException e) { CALEclipseUIPlugin.log(new Status(IStatus.ERROR, CALEclipseUIPlugin.PLUGIN_ID, IStatus.OK, "", e)); //$NON-NLS-1$ } } else{ // Some error, since the ScopedEntity exists but source model can't find it. CoreUtility.showErrorOnStatusLine(CALEditor.this, ActionMessages.OpenAction_error_messageBadSelection_CAL); } } } }; viewer.addSelectionChangedListener(selectionChangeListener_outlineView); // Configure the context menu. MenuManager menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$ moduleTreeContentProvider.fillContextMenu(menuMgr); menuMgr.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); menuMgr.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS + "-end")); //$NON-NLS-1$ menuMgr.setOverrides(new IContributionManagerOverrides() { public Integer getAccelerator(IContributionItem item) { return null; } public String getAcceleratorText(IContributionItem item) { return null; } public Boolean getEnabled(IContributionItem item) { return null; } public String getText(IContributionItem item) { return null; } public Boolean getVisible(IContributionItem arg0) { return null; } }); Menu menu = menuMgr.createContextMenu(viewer.getTree()); viewer.getTree().setMenu(menu); // Be sure to register it so that other plug-ins can add actions. getSite().registerContextMenu("org.openquark.cal.eclipse.ui.caleditor.outline", menuMgr, viewer); //$NON-NLS-1$ } } public static final CALModuleContentProvider calModuleContentProvider = new CALModuleContentProvider() { @Override public boolean getShowModuleHierarchy() { return false; } @Override public boolean getShowElementHierarchy() { return CALEclipseUIPlugin.getDefault().getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_OUTLINE_SHOW_ELEMENT_HIERARCHY); } @Override public boolean getShowPrivateElements() { return CALEclipseUIPlugin.getDefault().getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_OUTLINE_SHOW_PRIVATE_SYMBOLS); } @Override public boolean getLinkWithEditor() { return CALEclipseCorePlugin.getDefault().getPluginPreferences().getBoolean(PreferenceConstants.EDITOR_OUTLINE_LINK_WITH_EDITOR); } @Override public void setShowModuleHierarchy(boolean value) { } @Override public void setShowElementHierarchy(boolean value) { CALEclipseUIPlugin.getDefault().getPreferenceStore().setDefault(PreferenceConstants.EDITOR_OUTLINE_SHOW_ELEMENT_HIERARCHY, value); } @Override public void setShowPrivateElements(boolean value) { CALEclipseUIPlugin.getDefault().getPreferenceStore().setDefault(PreferenceConstants.EDITOR_OUTLINE_SHOW_PRIVATE_SYMBOLS, value); } @Override public void setLinkWithEditor(boolean value) { CALEclipseCorePlugin.getDefault().getPluginPreferences().setDefault(PreferenceConstants.EDITOR_OUTLINE_LINK_WITH_EDITOR, value); } @Override public CALModelManager getCALModelManager() { return calModelManager; } }; /* * @see IAdaptable#getAdapter(java.lang.Class) */ @Override public Object getAdapter(Class required) { if (IEncodingSupport.class.equals(required)) { return encodingSupport; } if (required.equals(IContentOutlinePage.class)) { if (outlinePage == null){ outlinePage = new CALOutlinePage(); selectedEntitiesListeners.add(outlinePage); } return outlinePage; } if (fProjectionSupport != null) { Object adapter = fProjectionSupport.getAdapter(getSourceViewer(), required); if (adapter != null) { return adapter; } } // if (required == IContextProvider.class) // return JavaUIHelp.getHelpContextProvider(this, IJavaHelpContextIds.JAVA_EDITOR); if (SmartBackspaceManager.class.equals(required)) { if (getSourceViewer() instanceof CALSourceViewer) { return ((CALSourceViewer)getSourceViewer()).getBackspaceManager(); } } return super.getAdapter(required); } /* * @see org.eclipse.ui.texteditor.AbstractTextEditor#updatePropertyDependentActions() */ @Override protected void updatePropertyDependentActions() { super.updatePropertyDependentActions(); if (encodingSupport != null) { encodingSupport.reset(); } } /* * @see org.eclipse.ui.texteditor.AbstractTextEditor#editorContextMenuAboutToShow(org.eclipse.jface.action.IMenuManager) */ @Override protected void editorContextMenuAboutToShow(IMenuManager menu) { super.editorContextMenuAboutToShow(menu); menu.appendToGroup(ITextEditorActionConstants.GROUP_SAVE, new Separator(IContextMenuConstants.GROUP_OPEN)); menu.insertAfter(IContextMenuConstants.GROUP_OPEN, new GroupMarker(IContextMenuConstants.GROUP_SHOW)); IAction action= getAction(CALEditorActionDefinitionIds.SHOW_OUTLINE); menu.appendToGroup(IContextMenuConstants.GROUP_OPEN, action); generateActionGroup.fillContextMenu(menu); refactorActionGroup.fillContextMenu(menu); searchReferencesActionGroup.fillContextMenu(menu); searchDeclarationsActionGroup.fillContextMenu(menu); } private TabConverter tabConverter; interface ITextConverter { void customizeDocumentCommand(IDocument document, DocumentCommand command); } public class AdaptedSourceViewer extends CALSourceViewer implements MouseListener { private List<ITextConverter> fTextConverters; private boolean fIgnoreTextConverters= false; private IInformationPresenter fOutlinePresenter; public AdaptedSourceViewer(Composite parent, IVerticalRuler verticalRuler, IOverviewRuler overviewRuler, boolean showAnnotationsOverview, int styles, IPreferenceStore store) { super(parent, verticalRuler, overviewRuler, showAnnotationsOverview, styles, store); } public ModuleName getModuleName(){ return CALEditor.this.getModuleName(); } public SourceManagerFactory getSourceManagerFactory(boolean updateDocumentIfPossible){ return CALEditor.getSourceManagerFactory(updateDocumentIfPossible, getSite().getShell(), CALEditor.this); } public CALEditor getEditor(){ return CALEditor.this; } /* * @see ITextOperationTarget#doOperation(int) */ @Override public void doOperation(int operation) { if (getTextWidget() == null) { return; } switch (operation) { // case CONTENTASSIST_PROPOSALS: // long time= CODE_ASSIST_DEBUG ? System.currentTimeMillis() : 0; // String msg= fContentAssistant.showPossibleCompletions(); // if (CODE_ASSIST_DEBUG) { // long delta= System.currentTimeMillis() - time; // System.err.println("Code Assist (total): " + delta); //$NON-NLS-1$ // } // setStatusLineErrorMessage(msg); // return; // case CORRECTIONASSIST_PROPOSALS: // msg= fCorrectionAssistant.showPossibleCompletions(); // setStatusLineErrorMessage(msg); // return; case SHOW_OUTLINE: if (fOutlinePresenter != null) fOutlinePresenter.showInformation(); return; case UNDO: fIgnoreTextConverters= true; super.doOperation(operation); fIgnoreTextConverters= false; return; case REDO: fIgnoreTextConverters= true; super.doOperation(operation); fIgnoreTextConverters= false; return; } super.doOperation(operation); } /* * @see ITextOperationTarget#canDoOperation(int) */ @Override public boolean canDoOperation(int operation) { // if (operation == CORRECTIONASSIST_PROPOSALS) // return isEditable(); return super.canDoOperation(operation); } /* * @see org.eclipse.jface.text.source.ISourceViewerExtension2#unconfigure() */ @Override public void unconfigure() { // if (fCorrectionAssistant != null) { // fCorrectionAssistant.uninstall(); // fCorrectionAssistant= null; // } // Remove the mouse listener { StyledText text= getTextWidget(); if (text == null || text.isDisposed()) { return; } text.removeMouseListener(this); } super.unconfigure(); } public void insertTextConverter(ITextConverter textConverter, int index) { throw new UnsupportedOperationException(); } public void addTextConverter(ITextConverter textConverter) { if (fTextConverters == null) { fTextConverters= new ArrayList<ITextConverter>(1); fTextConverters.add(textConverter); } else if (!fTextConverters.contains(textConverter)) { fTextConverters.add(textConverter); } } public void removeTextConverter(ITextConverter textConverter) { if (fTextConverters != null) { fTextConverters.remove(textConverter); if (fTextConverters.size() == 0) { fTextConverters= null; } } } /* * @see TextViewer#customizeDocumentCommand(DocumentCommand) */ @Override protected void customizeDocumentCommand(DocumentCommand command) { super.customizeDocumentCommand(command); if (!fIgnoreTextConverters && fTextConverters != null) { for (final ITextConverter textConverter : fTextConverters) { textConverter.customizeDocumentCommand(getDocument(), command); } } } // http://dev.eclipse.org/bugs/show_bug.cgi?id=19270 public void updateIndentationPrefixes() { SourceViewerConfiguration configuration= getSourceViewerConfiguration(); String[] types = configuration.getConfiguredContentTypes(this); for (final String type : types) { String[] prefixes = configuration.getIndentPrefixes(this, type); if (prefixes != null && prefixes.length > 0) { setIndentPrefixes(prefixes, type); } } } /** * {@inheritDoc} */ @Override public boolean requestWidgetToken(IWidgetTokenKeeper requester) { if (PlatformUI.getWorkbench().getHelpSystem().isContextHelpDisplayed()) { return false; } return super.requestWidgetToken(requester); } /** * {@inheritDoc} */ @Override public boolean requestWidgetToken(IWidgetTokenKeeper requester, int priority) { if (PlatformUI.getWorkbench().getHelpSystem().isContextHelpDisplayed()) { return false; } return super.requestWidgetToken(requester, priority); } /** * {@inheritDoc} */ @Override public void configure(SourceViewerConfiguration configuration) { super.configure(configuration); // fCorrectionAssistant= new JavaCorrectionAssistant(CompilationUnitEditor.this); // fCorrectionAssistant.install(this); if (configuration instanceof CALSourceViewerConfiguration) { CALSourceViewerConfiguration calSVCconfiguration= (CALSourceViewerConfiguration)configuration; fOutlinePresenter= calSVCconfiguration.getOutlinePresenter(this, false); if (fOutlinePresenter != null) fOutlinePresenter.install(this); } // Add the mouse listener { StyledText text= getTextWidget(); if (text == null || text.isDisposed()) { return; } text.addMouseListener(this); } } /** * {@inheritDoc} */ @Override public IFormattingContext createFormattingContext() { /* * TODOEL */ return super.createFormattingContext(); // IFormattingContext context= new CommentFormattingContext(); // // Map preferences; // IJavaElement inputJavaElement= getInputJavaElement(); // IJavaProject javaProject= inputJavaElement != null ? inputJavaElement.getJavaProject() : null; // if (javaProject == null) // preferences= new HashMap(JavaCore.getOptions()); // else // preferences= new HashMap(javaProject.getOptions(true)); // // context.setProperty(FormattingContextProperties.CONTEXT_PREFERENCES, preferences); // // return context; } public void mouseDoubleClick(MouseEvent arg0) { } public void mouseDown(MouseEvent arg0) { } public void mouseUp(MouseEvent arg0) { } } /** * Internal activation listener. */ private class ActivationListener implements IWindowListener { /* * @see org.eclipse.ui.IWindowListener#windowActivated(org.eclipse.ui.IWorkbenchWindow) */ public void windowActivated(IWorkbenchWindow window) { // if (window == getEditorSite().getWorkbenchWindow() && fMarkOccurrenceAnnotations && isActivePart()) { // fForcedMarkOccurrencesSelection= getSelectionProvider().getSelection(); // SelectionListenerWithASTManager.getDefault().forceSelectionChange(JavaEditor.this, (ITextSelection)fForcedMarkOccurrencesSelection); // } } /* * @see org.eclipse.ui.IWindowListener#windowDeactivated(org.eclipse.ui.IWorkbenchWindow) */ public void windowDeactivated(IWorkbenchWindow window) { // if (window == getEditorSite().getWorkbenchWindow() && fMarkOccurrenceAnnotations && isActivePart()) // removeOccurrenceAnnotations(); } /* * @see org.eclipse.ui.IWindowListener#windowClosed(org.eclipse.ui.IWorkbenchWindow) */ public void windowClosed(IWorkbenchWindow window) { } /* * @see org.eclipse.ui.IWindowListener#windowOpened(org.eclipse.ui.IWorkbenchWindow) */ public void windowOpened(IWorkbenchWindow window) { } } /** * Updates the Java outline page selection and this editor's range indicator. */ private class EditorSelectionChangedListener extends AbstractSelectionChangedListener { /* * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent) */ public void selectionChanged(SelectionChangedEvent event) { // XXX: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=56161 CALEditor.this.selectionChanged(event); } } static class TabConverter implements ITextConverter { private int fTabRatio; private ILineTracker fLineTracker; public TabConverter() { //do nothing } public void setNumberOfSpacesPerTab(int ratio) { fTabRatio = ratio; } public void setLineTracker(ILineTracker lineTracker) { fLineTracker = lineTracker; } private int insertTabString(StringBuilder buffer, int offsetInLine) { if (fTabRatio == 0) { return 0; } int remainder = offsetInLine % fTabRatio; remainder = fTabRatio - remainder; for (int i = 0; i < remainder; i++) { buffer.append(' '); } return remainder; } public void customizeDocumentCommand(IDocument document, DocumentCommand command) { String text = command.text; if (text == null) { return; } int index = text.indexOf('\t'); if (index > -1) { StringBuilder buffer = new StringBuilder(); fLineTracker.set(command.text); int lines = fLineTracker.getNumberOfLines(); try { for (int i = 0; i < lines; i++) { int offset = fLineTracker.getLineOffset(i); int endOffset = offset + fLineTracker.getLineLength(i); String line = text.substring(offset, endOffset); int position = 0; if (i == 0) { IRegion firstLine = document.getLineInformationOfOffset(command.offset); position = command.offset - firstLine.getOffset(); } int length = line.length(); for (int j = 0; j < length; j++) { char c = line.charAt(j); if (c == '\t') { position += insertTabString(buffer, position); } else { buffer.append(c); ++position; } } } command.text = buffer.toString(); } catch (BadLocationException x) { //do nothing } } } } /** * Runner that will toggle folding either instantly (if the editor is * visible) or the next time it becomes visible. If a runner is started when * there is already one registered, the registered one is canceled as * toggling folding twice is a no-op. * <p> * The access to the fFoldingRunner field is not thread-safe, it is assumed * that <code>runWhenNextVisible</code> is only called from the UI thread. * </p> */ private final class ToggleFoldingRunner implements IPartListener2 { /** * The workbench page we registered the part listener with, or <code>null</code>. */ private IWorkbenchPage fPage; /** * Does the actual toggling of projection. */ private void toggleFolding() { ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer instanceof ProjectionViewer) { ProjectionViewer pv = (ProjectionViewer)sourceViewer; if (pv.isProjectionMode() != isFoldingEnabled()) { if (pv.canDoOperation(ProjectionViewer.TOGGLE)) { pv.doOperation(ProjectionViewer.TOGGLE); } } } } /** * Makes sure that the editor's folding state is correct the next time it becomes visible. If it already is * visible, it toggles the folding state. If not, it either registers a part listener to toggle folding when the * editor becomes visible, or cancels an already registered runner. */ public void runWhenNextVisible() { // if there is one already: toggling twice is the identity if (fFoldingRunner != null) { fFoldingRunner.cancel(); return; } IWorkbenchPartSite site = getSite(); if (site != null) { IWorkbenchPage page = site.getPage(); if (!page.isPartVisible(CALEditor.this)) { // if we're not visible - defer until visible fPage = page; fFoldingRunner = this; page.addPartListener(this); return; } } // we're visible - run now toggleFolding(); } /** * Remove the listener and clear the field. */ private void cancel() { if (fPage != null) { fPage.removePartListener(this); fPage = null; } if (fFoldingRunner == this) { fFoldingRunner = null; } } /* * @see org.eclipse.ui.IPartListener2#partVisible(org.eclipse.ui.IWorkbenchPartReference) */ public void partVisible(IWorkbenchPartReference partRef) { if (CALEditor.this.equals(partRef.getPart(false))) { cancel(); toggleFolding(); } } /* * @see org.eclipse.ui.IPartListener2#partClosed(org.eclipse.ui.IWorkbenchPartReference) */ public void partClosed(IWorkbenchPartReference partRef) { if (CALEditor.this.equals(partRef.getPart(false))) { cancel(); } } public void partActivated(IWorkbenchPartReference partRef) { } public void partBroughtToTop(IWorkbenchPartReference partRef) { } public void partDeactivated(IWorkbenchPartReference partRef) { } public void partOpened(IWorkbenchPartReference partRef) { } public void partHidden(IWorkbenchPartReference partRef) { } public void partInputChanged(IWorkbenchPartReference partRef) { } } /** * Configures the toggle comment action */ private void configureToggleCommentAction() { IAction action = getAction("ToggleComment"); //$NON-NLS-1$ if (action instanceof ToggleCommentAction) { ISourceViewer sourceViewer = getSourceViewer(); SourceViewerConfiguration configuration = getSourceViewerConfiguration(); ((ToggleCommentAction)action).configure(sourceViewer, configuration); } } private void configureTabConverter() { if (tabConverter != null) { tabConverter.setLineTracker(new DefaultLineTracker()); } } private void startTabConversion() { if (tabConverter == null) { tabConverter = new TabConverter(); configureTabConverter(); tabConverter.setNumberOfSpacesPerTab(getTabSize()); AdaptedSourceViewer asv = (AdaptedSourceViewer) getSourceViewer(); asv.addTextConverter(tabConverter); // http://dev.eclipse.org/bugs/show_bug.cgi?id=19270 asv.updateIndentationPrefixes(); } } private void stopTabConversion() { if (tabConverter != null) { AdaptedSourceViewer asv = (AdaptedSourceViewer)getSourceViewer(); asv.removeTextConverter(tabConverter); // http://dev.eclipse.org/bugs/show_bug.cgi?id=19270 asv.updateIndentationPrefixes(); tabConverter = null; } } public int getTabSize() { // IJavaElement element= getInputJavaElement(); // IJavaProject project= element == null ? null : element.getJavaProject(); return CodeFormatterUtil.getTabWidth(null); } @Override protected boolean affectsTextPresentation(PropertyChangeEvent event) { return ((CALSourceViewerConfiguration)getSourceViewerConfiguration()).affectsTextPresentation(event) || super.affectsTextPresentation(event); } @Override protected void setPreferenceStore(IPreferenceStore store) { super.setPreferenceStore(store); if (getSourceViewerConfiguration() instanceof CALSourceViewerConfiguration) { CALTextTools textTools = CALEclipseUIPlugin.getDefault().getCALTextTools(); setSourceViewerConfiguration(new CALSourceViewerConfiguration(textTools.getColorManager(), store, this, CALPartitions.CAL_PARTITIONING)); } if (getSourceViewer() instanceof CALSourceViewer) { ((CALSourceViewer)getSourceViewer()).setPreferenceStore(store); } } @Override protected void handlePreferenceStoreChanged(PropertyChangeEvent event) { String property = event.getProperty(); if (AbstractDecoratedTextEditorPreferenceConstants.EDITOR_TAB_WIDTH.equals(property)) { /* * Ignore tab setting since we rely on the formatter preferences. * We do this outside the try-finally block to avoid that EDITOR_TAB_WIDTH * is handled by the sub-class (AbstractDecoratedTextEditor). */ return; } try { ISourceViewer asv = getSourceViewer(); if (asv == null) { return; } if (PreferenceConstants.EDITOR_CLOSE_BRACES.equals(property)) { fBracketInserter.setCloseBracesEnabled(getPreferenceStore().getBoolean(property)); return; } if (PreferenceConstants.EDITOR_CLOSE_BRACKETS.equals(property)) { fBracketInserter.setCloseBracketsEnabled(getPreferenceStore().getBoolean(property)); return; } if (PreferenceConstants.EDITOR_CLOSE_STRINGS.equals(property)) { fBracketInserter.setCloseStringsEnabled(getPreferenceStore().getBoolean(property)); return; } if (DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR.equals(property)) { if (isTabConversionEnabled()) { startTabConversion(); } else { stopTabConversion(); } } if (PreferenceConstants.EDITOR_SMART_TAB.equals(property)) { if (getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_SMART_TAB)) { setActionActivationCode("IndentOnTab", '\t', -1, SWT.NONE); //$NON-NLS-1$ } else { removeActionActivationCode("IndentOnTab"); //$NON-NLS-1$ } } ((CALSourceViewerConfiguration)getSourceViewerConfiguration()).handlePropertyChangeEvent(event); if (DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE.equals(property) || DefaultCodeFormatterConstants.FORMATTER_INDENTATION_SIZE.equals(property) || DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR.equals(property)) { StyledText textWidget = asv.getTextWidget(); int tabWidth = getSourceViewerConfiguration().getTabWidth(asv); if (textWidget.getTabs() != tabWidth) { textWidget.setTabs(tabWidth); } return; } if (PreferenceConstants.EDITOR_FOLDING_ENABLED.equals(property)) { if (asv instanceof ProjectionViewer) { new ToggleFoldingRunner().runWhenNextVisible(); } return; } } finally { super.handlePreferenceStoreChanged(event); } if (AbstractDecoratedTextEditorPreferenceConstants.SHOW_RANGE_INDICATOR.equals(property)) { // superclass already installed the range indicator Object newValue= event.getNewValue(); ISourceViewer viewer= getSourceViewer(); if (newValue != null && viewer != null) { if (Boolean.valueOf(newValue.toString()).booleanValue()) { // adjust the highlightrange in order to get the magnet right after changing the selection Point selection= viewer.getSelectedRange(); adjustHighlightRange(selection.x, selection.y); } } } } /** * Initializes the given viewer's colors. * * @param viewer the viewer to be initialized */ @Override protected void initializeViewerColors(ISourceViewer viewer) { // handled by CALSourceViewer } /** * {@inheritDoc} */ @Override public int getOrientation() { return SWT.LEFT_TO_RIGHT; //CAL editors are always left to right by default } @Override public void createPartControl(Composite parent) { super.createPartControl(parent); IInformationControlCreator informationControlCreator = new IInformationControlCreator() { public IInformationControl createInformationControl(Shell shell) { boolean cutDown = false; int style = cutDown ? SWT.NONE : (SWT.V_SCROLL | SWT.H_SCROLL); return new DefaultInformationControl(shell, SWT.RESIZE | SWT.TOOL, style, new HTMLTextPresenter(cutDown)); } }; fInformationPresenter = new InformationPresenter(informationControlCreator); fInformationPresenter.setSizeConstraints(60, 10, true, true); fInformationPresenter.install(getSourceViewer()); fEditorSelectionChangedListener = new EditorSelectionChangedListener(); fEditorSelectionChangedListener.install(getSelectionProvider()); // if (fMarkOccurrenceAnnotations) // installOccurrencesFinder(); // // if (isSemanticHighlightingEnabled()) // installSemanticHighlighting(); PlatformUI.getWorkbench().addWindowListener(fActivationListener); if (isTabConversionEnabled()) { startTabConversion(); } /* * Bracket insertion. */ IPreferenceStore preferenceStore = getPreferenceStore(); boolean closeBraces = preferenceStore.getBoolean(PreferenceConstants.EDITOR_CLOSE_BRACES); boolean closeBrackets = preferenceStore.getBoolean(PreferenceConstants.EDITOR_CLOSE_BRACKETS); boolean closeStrings = preferenceStore.getBoolean(PreferenceConstants.EDITOR_CLOSE_STRINGS); // boolean closeAngularBrackets = false; fBracketInserter.setCloseBracesEnabled(closeBraces); fBracketInserter.setCloseBracketsEnabled(closeBrackets); fBracketInserter.setCloseStringsEnabled(closeStrings); // fBracketInserter.setCloseAngularBracketsEnabled(closeAngularBrackets); ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer instanceof ITextViewerExtension) { ((ITextViewerExtension)sourceViewer).prependVerifyKeyListener(fBracketInserter); } } private boolean isTabConversionEnabled() { // IJavaElement element = getInputJavaElement(); // IJavaProject project = element == null ? null : element.getJavaProject(); String option; // if (project == null) option = CALEclipseCorePlugin.getOption(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR); // else // option = project.getOption(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, true); return CoreOptionIDs.SPACE.equals(option); } // private boolean isTabConversionEnabled() { // IPreferenceStore store = getPreferenceStore(); // return store.getBoolean(PreferenceConstants.EDITOR_SPACES_FOR_TABS); // } @Override protected ISourceViewer createSourceViewer(Composite parent, IVerticalRuler verticalRuler, int styles) { IPreferenceStore store = getPreferenceStore(); AdaptedSourceViewer adaptedSourceViewer = new AdaptedSourceViewer(parent, verticalRuler, getOverviewRuler(), isOverviewRulerVisible(), styles, store); // JavaUIHelp.setHelp(this, viewer.getTextWidget(), CALHelpContextIds.CAL_EDITOR); /* * This is a performance optimization to reduce the computation of * the text presentation triggered by {@link #setVisibleDocument(IDocument)} */ if (adaptedSourceViewer != null && isFoldingEnabled() && (store == null || !store.getBoolean(PreferenceConstants.EDITOR_SHOW_SEGMENTS))) { adaptedSourceViewer.prepareDelayedProjection(); } fProjectionSupport = new ProjectionSupport(adaptedSourceViewer, getAnnotationAccess(), getSharedColors()); fProjectionSupport.addSummarizableAnnotationType("org.eclipse.ui.workbench.texteditor.error"); //$NON-NLS-1$ fProjectionSupport.addSummarizableAnnotationType("org.eclipse.ui.workbench.texteditor.warning"); //$NON-NLS-1$ fProjectionSupport.install(); // ensure source viewer decoration support has been created and configured getSourceViewerDecorationSupport(adaptedSourceViewer); return adaptedSourceViewer; } /** * {@inheritDoc} */ @Override protected void configureSourceViewerDecorationSupport(SourceViewerDecorationSupport support) { support.setCharacterPairMatcher(fBracketMatcher); support.setMatchingCharacterPainterPreferenceKeys(MATCHING_BRACKETS, MATCHING_BRACKETS_COLOR); super.configureSourceViewerDecorationSupport(support); } boolean isFoldingEnabled() { return CALEclipseUIPlugin.getDefault().getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_FOLDING_ENABLED); } /** * React to changed selection. */ protected void selectionChanged(SelectionChangedEvent event) { if (getSelectionProvider() == null) { return; } synchronizeOutlinePage(event); // ISourceReference element= computeHighlightRangeSourceReference(); // if (getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_SYNC_OUTLINE_ON_CURSOR_MOVE)) // synchronizeOutlinePage(element); // setSelection(element, false); // updateStatusLine(); } /** * Listens to changes of the current selection of the editor. * * @author Greg McClement */ public interface SelectedEntitiesChanged{ public void selectedEntitiesChanged(LinkedList<Object> pathToEntity); }; void synchronizeOutlinePage(SelectionChangedEvent event){ final Object[] workspaceListeners = CALWorkspace.getSelectedEntitiesChangedListeners(); if (selectedEntitiesListeners.size() > 0 || workspaceListeners.length > 0){ LinkedList<Object> pathToEntity = getPathToSelectedEntity(event); if (pathToEntity != null){ { Object[] outlineListeners = selectedEntitiesListeners.getListeners(); for (int i = 0; i < outlineListeners.length; ++i) { final SelectedEntitiesChanged listener = (SelectedEntitiesChanged) outlineListeners[i]; listener.selectedEntitiesChanged(pathToEntity); } } { for (int i = 0; i < workspaceListeners.length; ++i) { final SelectedEntitiesChanged listener = (SelectedEntitiesChanged) workspaceListeners[i]; listener.selectedEntitiesChanged(pathToEntity); } } } } } LinkedList<Object> getPathToSelectedEntity(SelectionChangedEvent event){ if (!calModuleContentProvider.getLinkWithEditor()){ return null; } ISelection selection = event.getSelection(); if (selection instanceof TextSelection){ ITextSelection textSelection = (ITextSelection) selection; final CALEditor textEditor = CALEditor.this; final IDocument document = ActionUtilities.getDocument(textEditor); if (document != null) { final IStorage storage = textEditor.getStorage(); ModuleName moduleName = calModelManager.getModuleName(storage); final int offset = textSelection.getOffset(); int firstLine; try { firstLine = document.getLineOfOffset(offset); final int column = CoreUtility.getColumn(firstLine, offset, document); SourceElement[] sourceElements = calModelManager.getSourceMetrics().findContainingSourceElement(moduleName, firstLine + 1, column + 1); if (sourceElements == null){ return null; } ModuleTypeInfo mti = calModelManager.getModuleTypeInfo(moduleName); if (mti == null){ return null; } LinkedList<Object> scopedEntities = new LinkedList<Object>(); // ScopedEntity or ClassInstance for(int i = 0; i < sourceElements.length; ++i){ SourceElement sourceElement = sourceElements[i]; if (sourceElement instanceof FunctionTypeDeclaration){ FunctionTypeDeclaration ftd = (FunctionTypeDeclaration) sourceElement; scopedEntities.addLast(mti.getFunction(ftd.getFunctionName())); } else if (sourceElement instanceof FunctionDefn){ FunctionDefn ftd = (FunctionDefn) sourceElement; scopedEntities.addLast(mti.getFunction(ftd.getName())); } else if (sourceElement instanceof AlgebraicType){ AlgebraicType at = (AlgebraicType) sourceElement; scopedEntities.addLast(mti.getTypeConstructor(at.getTypeConsName())); } else if (sourceElement instanceof DataConsDefn){ DataConsDefn dcd = (DataConsDefn) sourceElement; scopedEntities.addLast(mti.getDataConstructor(dcd.getDataConsName())); } else if (sourceElement instanceof TypeClassDefn){ TypeClassDefn tcd = (TypeClassDefn) sourceElement; scopedEntities.addLast(mti.getTypeClass(tcd.getTypeClassName())); } else if (sourceElement instanceof Primitive){ Primitive p = (Primitive) sourceElement; scopedEntities.addLast(mti.getFunction(p.getName())); } else if (sourceElement instanceof TypeConstructorDefn){ TypeConstructorDefn tcd = (TypeConstructorDefn) sourceElement; scopedEntities.addLast(mti.getTypeConstructor(tcd.getTypeConsName())); } else if (sourceElement instanceof ClassMethodDefn){ ClassMethodDefn cmd = (ClassMethodDefn) sourceElement; scopedEntities.addLast(mti.getClassMethod(cmd.getMethodName())); } else if (sourceElement instanceof InstanceDefn){ InstanceDefn instanceDefn = (InstanceDefn) sourceElement; final int nClassInstances = mti.getNClassInstances(); for (int iClassInstance = 0; iClassInstance < nClassInstances; ++iClassInstance) { ClassInstance classInstance = mti.getNthClassInstance(iClassInstance); if (SourceMetricsManager.same(classInstance, instanceDefn, mti)){ scopedEntities.addLast(classInstance); break; } } } else{ throw new IllegalStateException(); } if (scopedEntities.size() == 0 || scopedEntities.getLast() == null){ return null; } } // if the module type info is out of sync with the source file then // the path will contain null so just return null array since the // path is not valid for (final Object object : scopedEntities) { if (object == null){ return null; } } return scopedEntities; } catch (BadLocationException e) { // will only happen on concurrent modification CALEclipseUIPlugin.log(new Status(IStatus.ERROR, CALEclipseUIPlugin.PLUGIN_ID, IStatus.OK, "", e)); //$NON-NLS-1$ return null; } } } return null; } /** * {@inheritDoc} */ @Override protected void createNavigationActions() { super.createNavigationActions(); final StyledText textWidget = getSourceViewer().getTextWidget(); IAction action = new SmartLineStartAction(textWidget, false); action.setActionDefinitionId(ITextEditorActionDefinitionIds.LINE_START); setAction(ITextEditorActionDefinitionIds.LINE_START, action); action = new SmartLineStartAction(textWidget, true); action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_LINE_START); setAction(ITextEditorActionDefinitionIds.SELECT_LINE_START, action); action = new NavigatePreviousSubWordAction(); action.setActionDefinitionId(ITextEditorActionDefinitionIds.WORD_PREVIOUS); setAction(ITextEditorActionDefinitionIds.WORD_PREVIOUS, action); textWidget.setKeyBinding(SWT.CTRL | SWT.ARROW_LEFT, SWT.NULL); action = new NavigateNextSubWordAction(); action.setActionDefinitionId(ITextEditorActionDefinitionIds.WORD_NEXT); setAction(ITextEditorActionDefinitionIds.WORD_NEXT, action); textWidget.setKeyBinding(SWT.CTRL | SWT.ARROW_RIGHT, SWT.NULL); action = new SelectPreviousSubWordAction(); action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_WORD_PREVIOUS); setAction(ITextEditorActionDefinitionIds.SELECT_WORD_PREVIOUS, action); textWidget.setKeyBinding(SWT.CTRL | SWT.SHIFT | SWT.ARROW_LEFT, SWT.NULL); action = new SelectNextSubWordAction(); action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_WORD_NEXT); setAction(ITextEditorActionDefinitionIds.SELECT_WORD_NEXT, action); textWidget.setKeyBinding(SWT.CTRL | SWT.SHIFT | SWT.ARROW_RIGHT, SWT.NULL); action = new DeletePreviousSubWordAction(); action.setActionDefinitionId(ITextEditorActionDefinitionIds.DELETE_PREVIOUS_WORD); setAction(ITextEditorActionDefinitionIds.DELETE_PREVIOUS_WORD, action); textWidget.setKeyBinding(SWT.CTRL | SWT.BS, SWT.NULL); markAsStateDependentAction(ITextEditorActionDefinitionIds.DELETE_PREVIOUS_WORD, true); action = new DeleteNextSubWordAction(); action.setActionDefinitionId(ITextEditorActionDefinitionIds.DELETE_NEXT_WORD); setAction(ITextEditorActionDefinitionIds.DELETE_NEXT_WORD, action); textWidget.setKeyBinding(SWT.CTRL | SWT.DEL, SWT.NULL); markAsStateDependentAction(ITextEditorActionDefinitionIds.DELETE_NEXT_WORD, true); } /** * This action implements smart home. * * Instead of going to the start of a line it does the following: * - if smart home/end is enabled and the caret is after the line's first non-whitespace then the caret is moved * directly before it, taking CALDoc and multi-line comments into account. - if the caret is before the line's * first non-whitespace the caret is moved to the beginning of the line - if the caret is at the beginning of the * line see first case. * * @author Edward Lam */ protected class SmartLineStartAction extends LineStartAction { /** * Creates a new smart line start action * * @param textWidget the styled text widget * @param doSelect a boolean flag which tells if the text up to the beginning of the line should be selected */ public SmartLineStartAction(final StyledText textWidget, final boolean doSelect) { super(textWidget, doSelect); } /* * @see org.eclipse.ui.texteditor.AbstractTextEditor.LineStartAction#getLineStartPosition(java.lang.String, int, java.lang.String) */ @Override protected int getLineStartPosition(final IDocument document, final String line, final int length, final int offset) { String type = IDocument.DEFAULT_CONTENT_TYPE; try { type = TextUtilities.getContentType(document, CALPartitions.CAL_PARTITIONING, offset, true); } catch (BadLocationException exception) { // Should not happen } int index = super.getLineStartPosition(document, line, length, offset); if (type.equals(CALPartitions.CAL_DOC) || type.equals(CALPartitions.CAL_MULTI_LINE_COMMENT)) { if (index < length - 1 && line.charAt(index) == '*' && line.charAt(index + 1) != '/') { do { ++index; } while (index < length && LanguageInfo.isCALWhitespace(line.charAt(index))); } } else { if (index < length - 1 && line.charAt(index) == '/' && line.charAt(index + 1) == '/') { index++; do { ++index; } while (index < length && LanguageInfo.isCALWhitespace(line.charAt(index))); } } return index; } } /** * Text navigation action to navigate to the next sub-word. * * @author Edward Lam */ protected abstract class NextSubWordAction extends TextNavigationAction { protected CALWordIterator fIterator = new CALWordIterator(); /** * Creates a new next sub-word action. * * @param code Action code for the default operation. Must be an action code from @see org.eclipse.swt.custom.ST. */ protected NextSubWordAction(int code) { super(getSourceViewer().getTextWidget(), code); } /* * @see org.eclipse.jface.action.IAction#run() */ @Override public void run() { // Check whether we are in a java code partition and the preference is enabled final IPreferenceStore store = getPreferenceStore(); if (!store.getBoolean(PreferenceConstants.EDITOR_SUB_WORD_NAVIGATION)) { super.run(); return; } final ISourceViewer viewer = getSourceViewer(); final IDocument document = viewer.getDocument(); fIterator.setText((CharacterIterator)new DocumentCharacterIterator(document)); int position = widgetOffset2ModelOffset(viewer, viewer.getTextWidget().getCaretOffset()); if (position == -1) { return; } int next = findNextPosition(position); if (next != BreakIterator.DONE) { setCaretPosition(next); getTextWidget().showSelection(); fireSelectionChanged(); } } /** * Finds the next position after the given position. * * @param position the current position * @return the next position */ protected int findNextPosition(int position) { ISourceViewer viewer = getSourceViewer(); int widget = -1; while (position != BreakIterator.DONE && widget == -1) { // TODO: optimize position = fIterator.following(position); if (position != BreakIterator.DONE) { widget = modelOffset2WidgetOffset(viewer, position); } } return position; } /** * Sets the caret position to the sub-word boundary given with <code>position</code>. * * @param position Position where the action should move the caret */ protected abstract void setCaretPosition(int position); } /** * Text navigation action to navigate to the next sub-word. * * @author Edward Lam */ protected class NavigateNextSubWordAction extends NextSubWordAction { /** * Creates a new navigate next sub-word action. */ public NavigateNextSubWordAction() { super(ST.WORD_NEXT); } /* * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.NextSubWordAction#setCaretPosition(int) */ @Override protected void setCaretPosition(final int position) { getTextWidget().setCaretOffset(modelOffset2WidgetOffset(getSourceViewer(), position)); } } /** * Text operation action to delete the next sub-word. * * @author Edward Lam */ protected class DeleteNextSubWordAction extends NextSubWordAction implements IUpdate { /** * Creates a new delete next sub-word action. */ public DeleteNextSubWordAction() { super(ST.DELETE_WORD_NEXT); } /* * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.NextSubWordAction#setCaretPosition(int) */ @Override protected void setCaretPosition(final int position) { if (!validateEditorInputState()) { return; } final ISourceViewer viewer = getSourceViewer(); final int caret, length; Point selection = viewer.getSelectedRange(); if (selection.y != 0) { caret = selection.x; length = selection.y; } else { caret = widgetOffset2ModelOffset(viewer, viewer.getTextWidget().getCaretOffset()); length = position - caret; } try { viewer.getDocument().replace(caret, length, ""); //$NON-NLS-1$ } catch (BadLocationException exception) { // Should not happen } } /* * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.NextSubWordAction#findNextPosition(int) */ @Override protected int findNextPosition(int position) { return fIterator.following(position); } /* * @see org.eclipse.ui.texteditor.IUpdate#update() */ public void update() { setEnabled(isEditorInputModifiable()); } } /** * Text operation action to select the next sub-word. * * @author Edward Lam */ protected class SelectNextSubWordAction extends NextSubWordAction { /** * Creates a new select next sub-word action. */ public SelectNextSubWordAction() { super(ST.SELECT_WORD_NEXT); } /* * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.NextSubWordAction#setCaretPosition(int) */ @Override protected void setCaretPosition(final int position) { final ISourceViewer viewer = getSourceViewer(); final StyledText text = viewer.getTextWidget(); if (text != null && !text.isDisposed()) { final Point selection = text.getSelection(); final int caret = text.getCaretOffset(); final int offset = modelOffset2WidgetOffset(viewer, position); if (caret == selection.x) { text.setSelectionRange(selection.y, offset - selection.y); } else { text.setSelectionRange(selection.x, offset - selection.x); } } } } /** * Text navigation action to navigate to the previous sub-word. * * @author Edward Lam */ protected abstract class PreviousSubWordAction extends TextNavigationAction { protected CALWordIterator fIterator = new CALWordIterator(); /** * Creates a new previous sub-word action. * * @param code Action code for the default operation. Must be an action code from @see org.eclipse.swt.custom.ST. */ protected PreviousSubWordAction(final int code) { super(getSourceViewer().getTextWidget(), code); } /* * @see org.eclipse.jface.action.IAction#run() */ @Override public void run() { // Check whether we are in a java code partition and the preference is enabled final IPreferenceStore store = getPreferenceStore(); if (!store.getBoolean(PreferenceConstants.EDITOR_SUB_WORD_NAVIGATION)) { super.run(); return; } final ISourceViewer viewer = getSourceViewer(); final IDocument document = viewer.getDocument(); fIterator.setText((CharacterIterator)new DocumentCharacterIterator(document)); int position = widgetOffset2ModelOffset(viewer, viewer.getTextWidget().getCaretOffset()); if (position == -1) { return; } int previous = findPreviousPosition(position); if (previous != BreakIterator.DONE) { setCaretPosition(previous); getTextWidget().showSelection(); fireSelectionChanged(); } } /** * Finds the previous position before the given position. * * @param position the current position * @return the previous position */ protected int findPreviousPosition(int position) { ISourceViewer viewer = getSourceViewer(); int widget = -1; while (position != BreakIterator.DONE && widget == -1) { // TODO: optimize position = fIterator.preceding(position); if (position != BreakIterator.DONE) { widget = modelOffset2WidgetOffset(viewer, position); } } return position; } /** * Sets the caret position to the sub-word boundary given with <code>position</code>. * * @param position Position where the action should move the caret */ protected abstract void setCaretPosition(int position); } /** * Text navigation action to navigate to the previous sub-word. * * @author Edward Lam */ protected class NavigatePreviousSubWordAction extends PreviousSubWordAction { /** * Creates a new navigate previous sub-word action. */ public NavigatePreviousSubWordAction() { super(ST.WORD_PREVIOUS); } /* * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.PreviousSubWordAction#setCaretPosition(int) */ @Override protected void setCaretPosition(final int position) { getTextWidget().setCaretOffset(modelOffset2WidgetOffset(getSourceViewer(), position)); } } /** * Text operation action to delete the previous sub-word. * * @author Edward Lam */ protected class DeletePreviousSubWordAction extends PreviousSubWordAction implements IUpdate { /** * Creates a new delete previous sub-word action. */ public DeletePreviousSubWordAction() { super(ST.DELETE_WORD_PREVIOUS); } /* * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.PreviousSubWordAction#setCaretPosition(int) */ @Override protected void setCaretPosition(int position) { if (!validateEditorInputState()) { return; } final int length; final ISourceViewer viewer = getSourceViewer(); Point selection = viewer.getSelectedRange(); if (selection.y != 0) { position = selection.x; length = selection.y; } else { length = widgetOffset2ModelOffset(viewer, viewer.getTextWidget().getCaretOffset()) - position; } try { viewer.getDocument().replace(position, length, ""); //$NON-NLS-1$ } catch (BadLocationException exception) { // Should not happen } } /* * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.PreviousSubWordAction#findPreviousPosition(int) */ @Override protected int findPreviousPosition(int position) { return fIterator.preceding(position); } /* * @see org.eclipse.ui.texteditor.IUpdate#update() */ public void update() { setEnabled(isEditorInputModifiable()); } } /** * Text operation action to select the previous sub-word. * * @author Edward Lam */ protected class SelectPreviousSubWordAction extends PreviousSubWordAction { /** * Creates a new select previous sub-word action. */ public SelectPreviousSubWordAction() { super(ST.SELECT_WORD_PREVIOUS); } /* * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.PreviousSubWordAction#setCaretPosition(int) */ @Override protected void setCaretPosition(final int position) { final ISourceViewer viewer = getSourceViewer(); final StyledText text = viewer.getTextWidget(); if (text != null && !text.isDisposed()) { final Point selection = text.getSelection(); final int caret = text.getCaretOffset(); final int offset = modelOffset2WidgetOffset(viewer, position); if (caret == selection.x) { text.setSelectionRange(selection.y, offset - selection.y); } else { text.setSelectionRange(selection.x, offset - selection.x); } } } } /** * @author Edward Lam */ private class ExitPolicy implements IExitPolicy { final char fExitCharacter; final char fEscapeCharacter; final Stack<BracketLevel> fStack; final int fSize; public ExitPolicy(char exitCharacter, char escapeCharacter, Stack<BracketLevel> stack) { fExitCharacter = exitCharacter; fEscapeCharacter = escapeCharacter; fStack = stack; fSize = fStack.size(); } /* * @see org.eclipse.jdt.internal.ui.text.link.LinkedPositionUI.ExitPolicy#doExit(org.eclipse.jdt.internal.ui.text.link.LinkedPositionManager, * org.eclipse.swt.events.VerifyEvent, int, int) */ public ExitFlags doExit(LinkedModeModel model, VerifyEvent event, int offset, int length) { if (fSize == fStack.size() && !isMasked(offset)) { if (event.character == fExitCharacter) { BracketLevel level = fStack.peek(); if (level.fFirstPosition.offset > offset || level.fSecondPosition.offset < offset) { return null; } if (level.fSecondPosition.offset == offset && length == 0) { // don't enter the character if if its the closing peer return new ExitFlags(ILinkedModeListener.UPDATE_CARET, false); } } // when entering an anonymous class between the parenthesis', we don't want // to jump after the closing parenthesis when return is pressed if (event.character == SWT.CR && offset > 0) { IDocument document = getSourceViewer().getDocument(); try { if (document.getChar(offset - 1) == '{') { return new ExitFlags(ILinkedModeListener.EXIT_ALL, true); } } catch (BadLocationException e) { } } } return null; } private boolean isMasked(int offset) { IDocument document = getSourceViewer().getDocument(); try { return fEscapeCharacter == document.getChar(offset - 1); } catch (BadLocationException e) { } return false; } } /** * @author Edward Lam */ private static class BracketLevel { int fOffset; int fLength; LinkedModeUI fUI; Position fFirstPosition; Position fSecondPosition; } /** * Position updater that takes any changes at the borders of a position to not belong to the position. * @author Edward Lam */ private static class ExclusivePositionUpdater implements IPositionUpdater { /** The position category. */ private final String fCategory; /** * Creates a new updater for the given <code>category</code>. * * @param category the new category. */ public ExclusivePositionUpdater(String category) { fCategory = category; } /* * @see org.eclipse.jface.text.IPositionUpdater#update(org.eclipse.jface.text.DocumentEvent) */ public void update(DocumentEvent event) { int eventOffset = event.getOffset(); int eventOldLength = event.getLength(); int eventNewLength = event.getText() == null ? 0 : event.getText().length(); int deltaLength = eventNewLength - eventOldLength; try { Position[] positions = event.getDocument().getPositions(fCategory); for (int i = 0; i != positions.length; i++) { Position position = positions[i]; if (position.isDeleted()) { continue; } int offset = position.getOffset(); int length = position.getLength(); int end = offset + length; if (offset >= eventOffset + eventOldLength) { // position comes // after change - shift position.setOffset(offset + deltaLength); } else if (end <= eventOffset) { // position comes way before change - // leave alone } else if (offset <= eventOffset && end >= eventOffset + eventOldLength) { // event completely internal to the position - adjust length position.setLength(length + deltaLength); } else if (offset < eventOffset) { // event extends over end of position - adjust length int newEnd = eventOffset; position.setLength(newEnd - offset); } else if (end > eventOffset + eventOldLength) { // event extends from before position into it - adjust offset // and length // offset becomes end of event, length adjusted accordingly int newOffset = eventOffset + eventNewLength; position.setOffset(newOffset); position.setLength(end - newOffset); } else { // event consumes the position - delete it position.delete(); } } } catch (BadPositionCategoryException e) { // ignore and return } } /** * Returns the position category. * * @return the position category */ public String getCategory() { return fCategory; } } /** * Closes braces, brackets, parents, string and char literals automatically, if configured. * @author Edward Lam */ private class BracketInserter implements VerifyKeyListener, ILinkedModeListener { private boolean fCloseBraces = true; private boolean fCloseBrackets = true; private boolean fCloseStrings = true; // private boolean fCloseAngularBrackets = true; private final String CATEGORY = toString(); private final IPositionUpdater fUpdater = new ExclusivePositionUpdater(CATEGORY); private final Stack<BracketLevel> fBracketLevelStack = new Stack<BracketLevel>(); public void setCloseBracesEnabled(boolean enabled) { fCloseBraces = enabled; } public void setCloseBracketsEnabled(boolean enabled) { fCloseBrackets = enabled; } public void setCloseStringsEnabled(boolean enabled) { fCloseStrings = enabled; } // public void setCloseAngularBracketsEnabled(boolean enabled) { // fCloseAngularBrackets = enabled; // } // private boolean isAngularIntroducer(String identifier) { // return identifier.length() > 0 && (Character.isUpperCase(identifier.charAt(0)) || identifier.startsWith("final") //$NON-NLS-1$ // || identifier.startsWith("public") //$NON-NLS-1$ // || identifier.startsWith("public") //$NON-NLS-1$ // || identifier.startsWith("protected") //$NON-NLS-1$ // || identifier.startsWith("private")); //$NON-NLS-1$ // } /* * @see org.eclipse.swt.custom.VerifyKeyListener#verifyKey(org.eclipse.swt.events.VerifyEvent) */ public void verifyKey(VerifyEvent event) { // early pruning to slow down normal typing as little as possible if (!event.doit || getInsertMode() != SMART_INSERT) { return; } switch (event.character) { case '{': case '(': // case '<': case '[': case '\'': case '\"': break; default: return; } final ISourceViewer sourceViewer = getSourceViewer(); IDocument document = sourceViewer.getDocument(); final Point selection = sourceViewer.getSelectedRange(); final int offset = selection.x; final int length = selection.y; try { IRegion startLine = document.getLineInformationOfOffset(offset); IRegion endLine = document.getLineInformationOfOffset(offset + length); CALHeuristicScanner scanner = new CALHeuristicScanner(document); int nextToken = scanner.nextToken(offset + length, endLine.getOffset() + endLine.getLength()); String next = nextToken == Symbols.TokenEOF ? null : document.get(offset, scanner.getPosition() - offset).trim(); int prevToken = scanner.previousToken(offset - 1, startLine.getOffset()); int prevTokenOffset = scanner.getPosition() + 1; String previous = prevToken == Symbols.TokenEOF ? null : document.get(prevTokenOffset, offset - prevTokenOffset).trim(); switch (event.character) { case '{': if (!fCloseBraces || nextToken == Symbols.TokenCONSIDENT || nextToken == Symbols.TokenOTHERIDENT || next != null && next.length() > 1) { return; } break; case '(': if (!fCloseBrackets || nextToken == Symbols.TokenLPAREN || nextToken == Symbols.TokenCONSIDENT || nextToken == Symbols.TokenOTHERIDENT || next != null && next.length() > 1) { return; } break; // case '<': // if (!(fCloseAngularBrackets && fCloseBrackets) || nextToken == Symbols.TokenLESSTHAN || prevToken != Symbols.TokenLBRACE // && prevToken != Symbols.TokenRBRACE && prevToken != Symbols.TokenSEMICOLON && prevToken != Symbols.TokenSYNCHRONIZED // && prevToken != Symbols.TokenSTATIC && (prevToken != Symbols.TokenIDENT || !isAngularIntroducer(previous)) && prevToken != Symbols.TokenEOF) // return; // break; case '[': if (!fCloseBrackets || nextToken == Symbols.TokenCONSIDENT || nextToken == Symbols.TokenOTHERIDENT || next != null && next.length() > 1) { return; } break; case '\'': case '"': if (!fCloseStrings || nextToken == Symbols.TokenCONSIDENT || nextToken == Symbols.TokenOTHERIDENT || prevToken == Symbols.TokenCONSIDENT || prevToken == Symbols.TokenOTHERIDENT || next != null && next.length() > 1 || previous != null && previous.length() > 1) { return; } break; default: return; } ITypedRegion partition = TextUtilities.getPartition(document, CALPartitions.CAL_PARTITIONING, offset, true); if (!IDocument.DEFAULT_CONTENT_TYPE.equals(partition.getType())) { return; } if (!validateEditorInputState()) { return; } final char character = event.character; final char closingCharacter = getPeerCharacter(character); final StringBuilder buffer = new StringBuilder(); buffer.append(character); buffer.append(closingCharacter); document.replace(offset, length, buffer.toString()); BracketLevel level = new BracketLevel(); fBracketLevelStack.push(level); LinkedPositionGroup group = new LinkedPositionGroup(); group.addPosition(new LinkedPosition(document, offset + 1, 0, LinkedPositionGroup.NO_STOP)); LinkedModeModel model = new LinkedModeModel(); model.addLinkingListener(this); model.addGroup(group); model.forceInstall(); level.fOffset = offset; level.fLength = 2; // set up position tracking for our magic peers if (fBracketLevelStack.size() == 1) { document.addPositionCategory(CATEGORY); document.addPositionUpdater(fUpdater); } level.fFirstPosition = new Position(offset, 1); level.fSecondPosition = new Position(offset + 1, 1); document.addPosition(CATEGORY, level.fFirstPosition); document.addPosition(CATEGORY, level.fSecondPosition); level.fUI = new EditorLinkedModeUI(model, sourceViewer); level.fUI.setSimpleMode(true); level.fUI.setExitPolicy(new ExitPolicy(closingCharacter, getEscapeCharacter(closingCharacter), fBracketLevelStack)); level.fUI.setExitPosition(sourceViewer, offset + 2, 0, Integer.MAX_VALUE); level.fUI.setCyclingMode(LinkedModeUI.CYCLE_NEVER); level.fUI.enter(); IRegion newSelection = level.fUI.getSelectedRegion(); sourceViewer.setSelectedRange(newSelection.getOffset(), newSelection.getLength()); event.doit = false; } catch (BadLocationException e) { CALEclipseUIPlugin.log(e); } catch (BadPositionCategoryException e) { CALEclipseUIPlugin.log(e); } } /* * @see org.eclipse.jface.text.link.ILinkedModeListener#left(org.eclipse.jface.text.link.LinkedModeModel, int) */ public void left(LinkedModeModel environment, int flags) { final BracketLevel level = fBracketLevelStack.pop(); if (flags != ILinkedModeListener.EXTERNAL_MODIFICATION) { return; } // remove brackets final ISourceViewer sourceViewer = getSourceViewer(); final IDocument document = sourceViewer.getDocument(); if (document instanceof IDocumentExtension) { IDocumentExtension extension = (IDocumentExtension)document; extension.registerPostNotificationReplace(null, new IDocumentExtension.IReplace() { public void perform(IDocument d, IDocumentListener owner) { if ((level.fFirstPosition.isDeleted || level.fFirstPosition.length == 0) && !level.fSecondPosition.isDeleted && level.fSecondPosition.offset == level.fFirstPosition.offset) { try { document.replace(level.fSecondPosition.offset, level.fSecondPosition.length, null); } catch (BadLocationException e) { CALEclipseUIPlugin.log(e); } } if (fBracketLevelStack.size() == 0) { document.removePositionUpdater(fUpdater); try { document.removePositionCategory(CATEGORY); } catch (BadPositionCategoryException e) { CALEclipseUIPlugin.log(e); } } } }); } } /* * @see org.eclipse.jface.text.link.ILinkedModeListener#suspend(org.eclipse.jface.text.link.LinkedModeModel) */ public void suspend(LinkedModeModel environment) { } /* * @see org.eclipse.jface.text.link.ILinkedModeListener#resume(org.eclipse.jface.text.link.LinkedModeModel, int) */ public void resume(LinkedModeModel environment, int flags) { } } /** * Helper for bracket inserter. * @param character a character which appears in a string * @return the character which must precede the character in order to escape it in the string. */ private static char getEscapeCharacter(char character) { switch (character) { case '"': case '\'': return '\\'; default: return 0; } } /** * @param character a character which has a "peer" in the cal language. * @return the character's peer. eg. for an opening bracket, returns the closing bracket. */ private static char getPeerCharacter(char character) { switch (character) { case '(': return ')'; case ')': return '('; case '{': return '}'; case '}': return '{'; // case '<': // return '>'; // // case '>': // return '<'; case '[': return ']'; case ']': return '['; case '"': return character; case '\'': return character; default: throw new IllegalArgumentException(); } } public ModuleName getModuleName(){ IStorage memberFile = getStorage(); return calModelManager.getModuleName(memberFile); } public IStorage getStorage() { IEditorInput input = getEditorInput(); if (input instanceof IStorageEditorInput) { try { return ((IStorageEditorInput) input).getStorage(); } catch (CoreException e) { Util.log(e, "Failure getting Editor input storage for " + this); } } return null; } public SourceManagerFactory getSourceManagerFactory(final boolean updateDocumentIfPossible){ return getSourceManagerFactory(updateDocumentIfPossible, getSite().getShell(), this); } /** * @return The source manager factory. */ public static SourceManagerFactory getSourceManagerFactory(boolean notUsed, final Shell shell, final CALEditor calEditor){ return new CALModelManager.SourceManagerFactory() { public ISourceManager getSourceManager(ModuleName name) { return new SourceManager(shell); } }; } private static class SourceManager implements ModuleContainer.ISourceManager, ModuleContainer.ISourceManager2{ private final Map<ModuleName, IStorage> moduleNameToIStorage = new HashMap<ModuleName, IStorage>(); private final Shell shell; private final CALDocumentProvider documentProvider = CALEclipseUIPlugin.getDefault().getCALDocumentProvider(); private DocumentRewriteSession documentRewriteSession = null; SourceManager(Shell shell){ this.shell = shell; } private IStorage getStorage(ModuleName name){ IStorage storage = moduleNameToIStorage.get(name); if (storage == null){ storage = calModelManager.getInputSourceFile(name); moduleNameToIStorage.put(name, storage); } return storage; } public boolean isWriteable(ModuleName name){ IStorage storage = getStorage(name); if (storage == null){ return false; } if (storage instanceof JarEntryFile) { return false; } IFile file = (IFile) storage; if (file.isReadOnly()){ // give the user an opportunity to check out the file or make it writeable RepositoryProvider repositoryProvider = RepositoryProvider.getProvider(file.getProject()); if (repositoryProvider == null){ String message = MessageFormat.format(ActionMessages.ReadOnlyFileEncountered_message, new Object[] { file.getName() }); if (MessageDialog.openQuestion(shell, ActionMessages.ReadOnlyFileEncountered_title, message)){ ResourceAttributes attributes = file.getResourceAttributes(); if (attributes != null){ attributes.setReadOnly(false); try { file.setResourceAttributes(attributes); } catch (CoreException e) { } } } } else{ IFileModificationValidator fmv = repositoryProvider.getFileModificationValidator(); IFile iFiles[] = new IFile[1]; iFiles[0] = file; fmv.validateEdit(iFiles, shell); } } return !file.isReadOnly(); } private PartiallySynchronizedDocument getDocument(ModuleName name){ final IStorage istorage = calModelManager.getInputSourceFile(name); if (istorage instanceof IFile){ final IEditorInput input = new FileEditorInput((IFile) istorage); if (input != null) { return (PartiallySynchronizedDocument) documentProvider.getDocument(input); } } return null; } public void startUpdate(ModuleName moduleName){ PartiallySynchronizedDocument psd = getDocument(moduleName); if (psd.getActiveRewriteSession() == null){ documentRewriteSession = psd.startRewriteSession(DocumentRewriteSessionType.SEQUENTIAL); } } public void endUpdate(ModuleName moduleName){ if (documentRewriteSession != null){ PartiallySynchronizedDocument psd = getDocument(moduleName); psd.stopRewriteSession(documentRewriteSession); documentRewriteSession = null; } } public boolean canUpdateIncrementally(ModuleName moduleName){ return getDocument(moduleName) != null; } public String getSource(ModuleName name, CompilerMessageLogger messageLogger) { final IDocument document = getDocument(name); if (document != null){ return document.get(); } return CALModelManager.getCALModelManager().getModuleSource(name); } public boolean saveSource(final ModuleName name, final String moduleDefinition, org.openquark.cal.services.Status saveStatus){ if (!isWriteable(name)){ return false; } final IDocument document = getDocument(name); if (document != null){ document.set(moduleDefinition); return true; } // no document so hit the disk { IStorage storage = getStorage(name); if (storage == null){ return false; } if (! (storage instanceof IFile)) { return false; } IFile file = (IFile) storage; final StringReader contents = new StringReader(moduleDefinition); InputStream source = new InputStream(){ private Reader reader = contents; @Override public int read() throws IOException { return reader.read(); } }; try { file.setContents(source, false, true, null); } catch (CoreException e) { return false; } return true; } } public Pair<Integer, Integer> getLineAndColumn(final ModuleName name, final int offsetOneBased){ try { IDocument document = getDocument(name); final int line = document.getLineOfOffset(offsetOneBased - 1); final int col = CoreUtility.getColumn(line, offsetOneBased - 1, document); return new Pair<Integer, Integer>(line+1, col+1); } catch (BadLocationException e) { return null; } } public int getOffset(final ModuleName name, final SourcePosition position){ try { IDocument document = getDocument(name); return CoreUtility.toOffset(position, document) + 1; } catch (BadLocationException e) { return -1; } } public boolean saveSource(final ModuleName name, final int startIndex, final int endIndex, final String newText) { if (!isWriteable(name)){ return false; } final IDocument document = getDocument(name); if (document != null){ final int offset = startIndex; final int length = endIndex - startIndex; try { document.replace(offset - 1, length, newText); } catch (BadLocationException e) { assert false; return false; } return true; } // This only works for things open in the editor. For current use this is always true. assert false; return false; } } }