/* * Created on Mar 27, 2004 To change the template for this generated file go to * Window - Preferences - Java - Code Generation - Code and Comments */ package org.jactr.eclipse.ui.editor; import java.net.MalformedURLException; import java.net.URL; import org.antlr.runtime.tree.CommonTree; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.BadPositionCategoryException; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextListener; import org.eclipse.jface.text.ITextOperationTarget; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.TextEvent; import org.eclipse.jface.text.TextSelection; import org.eclipse.jface.text.contentassist.ContentAssistEvent; import org.eclipse.jface.text.contentassist.ICompletionListener; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; import org.eclipse.jface.text.source.IAnnotationModel; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.text.source.IVerticalRuler; import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel; import org.eclipse.jface.text.source.projection.ProjectionSupport; import org.eclipse.jface.text.source.projection.ProjectionViewer; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IEditorReference; import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.IURIEditorInput; import org.eclipse.ui.IWorkbenchActionConstants; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.ide.FileStoreEditorInput; import org.eclipse.ui.texteditor.AbstractDecoratedTextEditor; import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds; import org.eclipse.ui.texteditor.TextOperationAction; import org.eclipse.ui.views.contentoutline.IContentOutlinePage; import org.jactr.eclipse.core.comp.CompilationUnitManager; import org.jactr.eclipse.core.comp.ICompilationUnit; import org.jactr.eclipse.ui.UIPlugin; import org.jactr.eclipse.ui.editor.config.ACTRSourceViewerConfiguration; import org.jactr.eclipse.ui.editor.highlighter.HighlightAnnotations; import org.jactr.eclipse.ui.editor.highlighter.ReferenceHighlighter; import org.jactr.eclipse.ui.editor.markers.ASTPosition; import org.jactr.eclipse.ui.editor.markers.PositionMarker; import org.jactr.eclipse.ui.messages.JACTRMessages; import org.jactr.eclipse.ui.outline.ACTRContentOutline; import org.jactr.eclipse.ui.preferences.UIPreferences; /** * @author harrison To change the template for this generated type comment go to * Window - Preferences - Java - Code Generation - Code and Comments */ public abstract class ACTRModelEditor extends AbstractDecoratedTextEditor { /** * Logger definition */ static private final transient Log LOGGER = LogFactory .getLog(ACTRModelEditor.class); static private final String TEMPLATE_PROPOSALS = "org.jactr.eclipse.ui.template.action"; static private final String FORMAT_PREFIX = "org.jactr.eclipse.ui.format"; static public final String ACTR_CONTEXT_MENU = "org.jactr.eclipse.ui.editor.context"; static public final String ACTR_CONTEXT_GROUP = "jactr"; ACTRContentOutline _outliner; ACTRSourceViewerConfiguration _sourceViewer; ICompilationUnit _compilationUnit; private ProjectionSupport _projectionSupport; private ProjectionAnnotationModel _projectionAnnotationModel; private boolean _formatOnSave = false; private URL _baseURL; private boolean _highlightReferences = true; private ReferenceHighlighter _highlighter; private IResourceChangeListener _resourceListener = new IResourceChangeListener() { public void resourceChanged( IResourceChangeEvent event) { IResource resource = getResource(); if (resource == null) return; if (resource .equals(event .getResource())) if (event .getType() == IResourceChangeEvent.PRE_CLOSE || event .getType() == IResourceChangeEvent.PRE_DELETE) close(event .getType() == IResourceChangeEvent.PRE_CLOSE); } }; static public ACTRModelEditor getActiveEditor() { IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow() .getActivePage(); IEditorPart editor = page.getActiveEditor(); if (editor instanceof ACTRModelEditor) return (ACTRModelEditor) editor; for (IEditorReference ref : page.getEditorReferences()) { editor = ref.getEditor(false); if (editor instanceof ACTRModelEditor) return (ACTRModelEditor) editor; } return null; } /** * */ public ACTRModelEditor() { super(); _sourceViewer = createSourceViewerConfiguration(); setSourceViewerConfiguration(_sourceViewer); _outliner = new ACTRContentOutline(this, true); _formatOnSave = UIPlugin.getDefault().getPluginPreferences().getBoolean( UIPreferences.ENABLE_FORMAT_PREF); setEditorContextMenuId(ACTR_CONTEXT_MENU); } public IPreferenceStore getInternalPreferenceStore() { return super.getPreferenceStore(); } abstract protected ACTRSourceViewerConfiguration createSourceViewerConfiguration(); @Override protected void createActions() { super.createActions(); IAction action = new TextOperationAction(JACTRMessages.getResourceBundle(), TEMPLATE_PROPOSALS + ".", //$NON-NLS-1$ //$NON-NLS-2$ this, ISourceViewer.CONTENTASSIST_PROPOSALS); action .setActionDefinitionId(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS); setAction(TEMPLATE_PROPOSALS, action); markAsStateDependentAction(TEMPLATE_PROPOSALS, true); /* * format operation */ action = new TextOperationAction(JACTRMessages.getResourceBundle(), FORMAT_PREFIX + ".", //$NON-NLS-1$ //$NON-NLS-2$ this, ISourceViewer.FORMAT); action.setActionDefinitionId(FORMAT_PREFIX); setAction(FORMAT_PREFIX, action); // markAsStateDependentAction(FORMAT_PREFIX, true); /* * replace paste with retargeted.. Im not pleased with this impl as it * reformats the entire document on paste. I want to just format insertion */ // action = new FormattingTextOperationAction(JACTRMessages // .getResourceBundle(), "Editor.Paste.", this, ITextOperationTarget.PASTE); //$NON-NLS-1$ // action.setActionDefinitionId(IWorkbenchActionDefinitionIds.PASTE); // setAction(ITextEditorActionConstants.PASTE, action); } @Override protected void handleCursorPositionChanged() { super.handleCursorPositionChanged(); if (_highlightReferences) { if (_highlighter == null) _highlighter = new ReferenceHighlighter(this); ProjectionViewer viewer = (ProjectionViewer) getSourceViewer(); int caretPosition = viewer.getTextWidget().getCaretOffset(); caretPosition = viewer.widgetOffset2ModelOffset(caretPosition); _highlighter.highlight(caretPosition); } } public int getCursorOffset() { ISourceViewer sv = getSourceViewer(); StyledText styledText = sv.getTextWidget(); int caret = widgetOffset2ModelOffset(sv, styledText.getCaretOffset()); return caret; } @Override protected void editorContextMenuAboutToShow(IMenuManager menu) { super.editorContextMenuAboutToShow(menu); menu.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, new Separator( ACTR_CONTEXT_GROUP)); } /** * set the input and, if necessary, compile it into an ACTRCompilationUnit */ @Override protected void doSetInput(IEditorInput input) throws CoreException { super.doSetInput(input); if (input instanceof IURIEditorInput) { try { _baseURL = ((IURIEditorInput) input).getURI().toURL(); } catch (MalformedURLException e) { // TODO Auto-generated catch block LOGGER.error( "ACTRModelEditor.doSetInput threw MalformedURLException : ", e); } if (input instanceof IFileEditorInput) { IResource file = ((IFileEditorInput) input).getFile(); _compilationUnit = CompilationUnitManager.acquire(file); ResourcesPlugin.getWorkspace().addResourceChangeListener( _resourceListener, IResourceChangeEvent.PRE_CLOSE | IResourceChangeEvent.PRE_DELETE); } else if (input instanceof FileStoreEditorInput) try { _baseURL = FileLocator.toFileURL(_baseURL); IFileStore fileStore = EFS.getLocalFileSystem().getStore( new Path(_baseURL.getPath())); _compilationUnit = CompilationUnitManager.acquire(fileStore); } catch (Exception e) { UIPlugin.log("Could not acquire filestore location for " + _baseURL, e); } } } /** * return the resource being edited or null if the editor doesn't point to an * IResource. * * @return */ public IResource getResource() { IEditorInput input = getEditorInput(); if (input instanceof IFileEditorInput) return ((IFileEditorInput) input).getFile(); return null; } public ICompilationUnit getCompilationUnit() { return _compilationUnit; } /** * returns the production that is closest to the current cursor * * @return */ public CommonTree getNearestAST(int type) { IDocument document = getDocumentProvider().getDocument(getEditorInput()); TextSelection selection = (TextSelection) getSelectionProvider() .getSelection(); int cursor = selection.getOffset(); try { int distance = Integer.MAX_VALUE; CommonTree best = null; Position[] positions = document.getPositions(PositionMarker.POSITION_ID); for (Position position : positions) if (position != null && position instanceof ASTPosition) { ASTPosition astPos = (ASTPosition) position; CommonTree node = astPos.getNode(); if (node.getType() != type) continue; if (astPos.contains(cursor)) return node; int offsetDistance = Math.abs(astPos.getOffset() - cursor); int lenDistance = Math.abs(astPos.getOffset() + astPos.getLength() - cursor); if (offsetDistance < distance) { distance = offsetDistance; best = node; } else if (lenDistance < distance) { distance = lenDistance; best = node; } } return best; } catch (BadPositionCategoryException e) { // TODO Auto-generated catch block LOGGER .error( "ACTRModelEditor.getNearestProduction threw BadPositionCategoryException : ", e); } return null; } public URL getBase() { return _baseURL; } @Override public void dispose() { if (_compilationUnit != null) CompilationUnitManager.release(_compilationUnit); if (_outliner != null) { _outliner.dispose(); _outliner = null; } /* * remove listener */ ResourcesPlugin.getWorkspace().removeResourceChangeListener( _resourceListener); super.dispose(); } public ProjectionAnnotationModel getProjectionAnnotationModel() { return _projectionAnnotationModel; } /** * open up access for the highlighter * * @return */ public IAnnotationModel getAnnotationModel() { return getSourceViewer().getAnnotationModel(); } @Override public Object getAdapter(Class required) { if (IContentOutlinePage.class.equals(required)) { if (LOGGER.isDebugEnabled()) LOGGER.debug("Returning outliner " + _outliner); return _outliner; } else if (ACTRModelEditor.class.equals(required)) return this; return super.getAdapter(required); } /** * modification to enable folding in the editor * * @see http * ://www.eclipse.org/articles/Article-Folding-in-Eclipse-Text-Editors * /folding.html */ @Override public void createPartControl(Composite parent) { super.createPartControl(parent); ProjectionViewer viewer = (ProjectionViewer) getSourceViewer(); _projectionSupport = new ProjectionSupport(viewer, getAnnotationAccess(), getSharedColors()); _projectionSupport .addSummarizableAnnotationType("org.eclipse.ui.workbench.texteditor.error"); _projectionSupport .addSummarizableAnnotationType("org.eclipse.ui.workbench.texteditor.warning"); _projectionSupport .addSummarizableAnnotationType(HighlightAnnotations.VARIABLE_ID); _projectionSupport .addSummarizableAnnotationType(HighlightAnnotations.CHUNK_ID); _projectionSupport .addSummarizableAnnotationType(HighlightAnnotations.CHUNK_TYPE_ID); _projectionSupport.install(); // turn projection mode on viewer.doOperation(ProjectionViewer.TOGGLE); _projectionAnnotationModel = viewer.getProjectionAnnotationModel(); } /** * modification to enable folding in the editor * * @see http * ://www.eclipse.org/articles/Article-Folding-in-Eclipse-Text-Editors * /folding.html */ @Override protected ISourceViewer createSourceViewer(Composite parent, IVerticalRuler ruler, int styles) { /* * using some crazy mojo to format all edits.. */ ISourceViewer viewer = new ProjectionViewer(parent, ruler, getOverviewRuler(), isOverviewRulerVisible(), styles) { private volatile Point _spoofSelection = null; private volatile boolean _isFormatting = false; private volatile boolean _isProposing = false; private volatile boolean _wasProposed = false; private ICompletionListener fProposalListener = new ICompletionListener() { public void assistSessionEnded( ContentAssistEvent event) { _isProposing = false; _wasProposed = true; if (LOGGER .isDebugEnabled()) LOGGER.debug("ended"); } public void assistSessionStarted( ContentAssistEvent event) { _wasProposed = false; _isProposing = true; if (LOGGER .isDebugEnabled()) LOGGER .debug("started"); } public void selectionChanged( ICompletionProposal proposal, boolean smartToggle) { if (LOGGER .isDebugEnabled()) LOGGER .debug("changed"); } }; private ITextListener fTextListener = new ITextListener() { public void textChanged( TextEvent event) { if (!canDoOperation(ISourceViewer.FORMAT)) return; if (_isFormatting) return; if (_isProposing) return; if (_wasProposed) return; DocumentEvent dEvent = event .getDocumentEvent(); // actual event // if // (event.getViewerRedrawState() // && // dEvent == null) // return; // if(!event.getViewerRedrawState() // && // dEvent==null) return; if (dEvent == null) return; // delete, ignore if (event.getText() .length() == 0) return; String replaced = event .getReplacedText(); String text = event .getText(); if (text .equals(replaced)) return; // if // (replaced.trim().length() // == 0 // && // text.trim().length() // == // 0) // return; /* * let's format.. */ final Point fPoint = new Point( event.getOffset(), event.getLength()); Display.getDefault() .asyncExec( new Runnable() { public void run() { _spoofSelection = fPoint; ITextOperationTarget target = getSourceViewer() .getTextOperationTarget(); if (target == null) return; if (target .canDoOperation(ISourceViewer.FORMAT)) target .doOperation(ISourceViewer.FORMAT); _spoofSelection = null; } }); } }; @Override protected void inputChanged(Object newInput, Object oldInput) { removeTextListener(fTextListener); if (fContentAssistant instanceof IContentAssistantExtension2) ((IContentAssistantExtension2) fContentAssistant) .removeCompletionListener(fProposalListener); super.inputChanged(newInput, oldInput); addTextListener(fTextListener); if (fContentAssistant instanceof IContentAssistantExtension2) ((IContentAssistantExtension2) fContentAssistant) .addCompletionListener(fProposalListener); } @Override protected Point rememberSelection() { if (_spoofSelection != null) return _spoofSelection; return super.rememberSelection(); } @Override public void doOperation(int operation) { _isFormatting = operation == ISourceViewer.FORMAT; super.doOperation(operation); _isFormatting = false; } }; // ensure decoration support has been created and configured. getSourceViewerDecorationSupport(viewer); return viewer; } @Override public void doSave(IProgressMonitor progressMonitor) { /** * format first.. */ ITextOperationTarget tot = getSourceViewer().getTextOperationTarget(); if (tot.canDoOperation(ISourceViewer.FORMAT) && _formatOnSave) tot.doOperation(ISourceViewer.FORMAT); super.doSave(progressMonitor); } }