| /******************************************************************************* |
| * Copyright (c) 2005, 2007 ILOG and others. |
| * All rights reserved. This program and the accompanying materials |
| * are 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: |
| * Joel Cheuoua - Initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jet.internal.editor; |
| |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.ResourceBundle; |
| |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.runtime.IConfigurationElement; |
| import org.eclipse.core.runtime.IExtensionRegistry; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jet.core.parser.ast.Comment; |
| import org.eclipse.jet.core.parser.ast.IncludedContent; |
| import org.eclipse.jet.core.parser.ast.JETASTElement; |
| import org.eclipse.jet.core.parser.ast.JETASTVisitor; |
| import org.eclipse.jet.core.parser.ast.JETCompilationUnit; |
| import org.eclipse.jet.core.parser.ast.JETDirective; |
| import org.eclipse.jet.core.parser.ast.JavaDeclaration; |
| import org.eclipse.jet.core.parser.ast.JavaExpression; |
| import org.eclipse.jet.core.parser.ast.JavaScriptlet; |
| import org.eclipse.jet.core.parser.ast.TextElement; |
| import org.eclipse.jet.core.parser.ast.XMLBodyElement; |
| import org.eclipse.jet.core.parser.ast.XMLBodyElementEnd; |
| import org.eclipse.jet.core.parser.ast.XMLEmptyElement; |
| import org.eclipse.jet.internal.compiler.CompilationHelper; |
| import org.eclipse.jet.internal.editor.configuration.JETDocumentProvider; |
| import org.eclipse.jet.internal.editor.configuration.JETEditorPreferenceConstants; |
| import org.eclipse.jet.internal.editor.configuration.JETSourceViewerConfiguration; |
| import org.eclipse.jet.internal.editor.configuration.JETTokenStyleManager; |
| import org.eclipse.jet.internal.editor.configuration.delegates.IJETDefaultTextColorerDelegate; |
| import org.eclipse.jet.internal.editor.outline.JETOutlinePage; |
| import org.eclipse.jet.internal.editor.partition.JETDocumentPartitionScanner; |
| import org.eclipse.jface.action.Action; |
| import org.eclipse.jface.action.IMenuManager; |
| import org.eclipse.jface.preference.IPreferenceStore; |
| import org.eclipse.jface.text.DefaultInformationControl; |
| import org.eclipse.jface.text.DocumentEvent; |
| import org.eclipse.jface.text.IInformationControl; |
| import org.eclipse.jface.text.IInformationControlCreator; |
| import org.eclipse.jface.text.Position; |
| import org.eclipse.jface.text.source.ISourceViewer; |
| import org.eclipse.jface.text.source.IVerticalRuler; |
| import org.eclipse.jface.text.source.projection.ProjectionSupport; |
| import org.eclipse.jface.util.PropertyChangeEvent; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.ISelectionChangedListener; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.jface.viewers.SelectionChangedEvent; |
| import org.eclipse.swt.graphics.RGB; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.ui.IEditorInput; |
| import org.eclipse.ui.IEditorSite; |
| import org.eclipse.ui.IFileEditorInput; |
| import org.eclipse.ui.PartInitException; |
| import org.eclipse.ui.editors.text.IEncodingSupport; |
| import org.eclipse.ui.editors.text.TextEditor; |
| import org.eclipse.ui.texteditor.ContentAssistAction; |
| import org.eclipse.ui.views.contentoutline.IContentOutlinePage; |
| import org.osgi.framework.Bundle; |
| |
| /** |
| * The editor for JET templates files |
| * |
| * @author Joel |
| */ |
| public class JETTextEditor extends TextEditor { |
| |
| private JETTokenStyleManager tokenStyleManager; |
| |
| private JETSourceViewer sourceViewer; |
| |
| private JETDocumentPartitionScanner partitionScanner; |
| |
| private ProjectionSupport projectionSupport; |
| |
| private IContentOutlinePage outlinePage; |
| |
| private JETCompilationUnit cUnit; |
| |
| private IProject project; |
| |
| private Map positions; |
| |
| private boolean cUnitInvalidated; |
| |
| private ArrayList defaultContentColorerDelegates; |
| |
| private IJETDefaultTextColorerDelegate currentDelegate; |
| |
| /** |
| * Creates a new JET editor |
| */ |
| public JETTextEditor() { |
| positions = Collections.EMPTY_MAP; |
| IPreferenceStore preferenceStore = Activator.getDefault() |
| .getPreferenceStore(); |
| JETEditorPreferenceConstants.initializeDefaultValues(preferenceStore); |
| setPreferenceStore(preferenceStore); |
| tokenStyleManager = new JETTokenStyleManager(preferenceStore); |
| partitionScanner = new JETDocumentPartitionScanner(this); |
| setSourceViewerConfiguration(new JETSourceViewerConfiguration(this)); |
| setDocumentProvider(new JETDocumentProvider(this)); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ui.editors.text.TextEditor#dispose() |
| */ |
| public void dispose() { |
| tokenStyleManager.dispose(); |
| super.dispose(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see |
| * org.eclipse.ui.texteditor.AbstractTextEditor#init(org.eclipse.ui.IEditorSite |
| * , org.eclipse.ui.IEditorInput) |
| */ |
| public void init(IEditorSite site, IEditorInput input) |
| throws PartInitException { |
| if (input instanceof IFileEditorInput) |
| project = ((IFileEditorInput) input).getFile().getProject(); |
| super.init(site, input); |
| initDefaultContentColorerDelegetes(); |
| if (!defaultContentColorerDelegates.isEmpty()) |
| currentDelegate = (IJETDefaultTextColorerDelegate) defaultContentColorerDelegates |
| .get(0); |
| } |
| |
| /** |
| * Set the content colorer delegate (used for syntax coloring of non JET |
| * text) to use. |
| * |
| * @param delegate |
| */ |
| public void setCurrentColorerDelegate( |
| IJETDefaultTextColorerDelegate delegate) { |
| currentDelegate = delegate; |
| // notify the damager repairers so that they recompute the partitions |
| // and the tokens to colorize |
| partitionScanner.updateJETTextContentDamagerRepairer(); |
| // reset the text display |
| if (isDirty()) |
| getSourceViewer().getDocument().set( |
| getSourceViewer().getDocument().get()); |
| else |
| setInput(getEditorInput()); |
| } |
| |
| /** |
| * Returns the content colorer delegate (used for syntax coloring of non JET |
| * text) in use. |
| * |
| * @return the content colorer delegate |
| */ |
| public IJETDefaultTextColorerDelegate getCurrentColorerDelegate() { |
| return currentDelegate; |
| } |
| |
| /** |
| * Returns the list of all the available (registered) content colorer |
| * delegates. |
| * |
| * @return |
| */ |
| public IJETDefaultTextColorerDelegate[] getColorerDelegates() { |
| IJETDefaultTextColorerDelegate results[] = new IJETDefaultTextColorerDelegate[defaultContentColorerDelegates |
| .size()]; |
| defaultContentColorerDelegates.toArray(results); |
| return results; |
| } |
| |
| /** |
| * Compute the list of the registered content colorer delegates. |
| */ |
| private void initDefaultContentColorerDelegetes() { |
| defaultContentColorerDelegates = new ArrayList(); |
| IExtensionRegistry registry = Platform.getExtensionRegistry(); |
| IConfigurationElement elements[] = registry |
| .getConfigurationElementsFor("org.eclipse.jet.internal.editor", |
| "defaultContentColorerDelegates"); |
| for (int i = 0; i < elements.length; i++) { |
| IConfigurationElement element = elements[i]; |
| Bundle bundle = Platform |
| .getBundle(element.getNamespaceIdentifier()); |
| String delegateClass = element.getAttribute("delegateClass"); |
| IJETDefaultTextColorerDelegate delegate = null; |
| try { |
| Class clazz = bundle.loadClass(delegateClass); |
| Class argTypes[] = { JETTextEditor.class }; |
| Constructor ctorWithTextEditor = null; |
| try { |
| ctorWithTextEditor = clazz.getConstructor(argTypes); |
| } catch (SecurityException e) { |
| Activator.log(e); |
| } catch (NoSuchMethodException e) { |
| Activator.log(e); |
| } |
| if (ctorWithTextEditor != null) |
| delegate = (IJETDefaultTextColorerDelegate) ctorWithTextEditor |
| .newInstance(new Object[] { this }); |
| else |
| delegate = (IJETDefaultTextColorerDelegate) clazz |
| .newInstance(); |
| defaultContentColorerDelegates.add(delegate); |
| } catch (ClassNotFoundException e) { |
| Activator.log(e); |
| } catch (InstantiationException e) { |
| Activator.log(e); |
| } catch (IllegalAccessException e) { |
| Activator.log(e); |
| } catch (IllegalArgumentException e) { |
| Activator.log(e); |
| } catch (InvocationTargetException e) { |
| Activator.log(e); |
| } |
| } |
| |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see |
| * org.eclipse.ui.texteditor.AbstractDecoratedTextEditor#createSourceViewer |
| * (org.eclipse.swt.widgets.Composite, |
| * org.eclipse.jface.text.source.IVerticalRuler, int) |
| */ |
| protected ISourceViewer createSourceViewer(Composite parent, |
| IVerticalRuler ruler, int styles) { |
| fAnnotationAccess = getAnnotationAccess(); |
| fOverviewRuler = createOverviewRuler(getSharedColors()); |
| sourceViewer = new JETSourceViewer(parent, ruler, getOverviewRuler(), |
| isOverviewRulerVisible(), styles, getPreferenceStore()); |
| getSourceViewerDecorationSupport(sourceViewer); |
| projectionSupport = new ProjectionSupport(sourceViewer, |
| getAnnotationAccess(), getSharedColors()); |
| projectionSupport |
| .addSummarizableAnnotationType("org.eclipse.ui.workbench.texteditor.error"); |
| projectionSupport |
| .addSummarizableAnnotationType("org.eclipse.ui.workbench.texteditor.warning"); |
| projectionSupport |
| .setHoverControlCreator(new IInformationControlCreator() { |
| public IInformationControl createInformationControl( |
| Shell shell) { |
| return new DefaultInformationControl(shell, true); |
| // return new DefaultInformationControl(shell, |
| // getOrientation(), 0, new HTMLTextPresenter()); |
| } |
| }); |
| projectionSupport.install(); |
| return sourceViewer; |
| } |
| |
| /** |
| * Return the editor's source viewer. This method exist to make this viewer |
| * accessible to other classes. |
| * |
| * @return the editor's source viewer |
| */ |
| public ISourceViewer getEditorSourceViewer() { |
| return sourceViewer; |
| } |
| |
| /** |
| * Returns the editor's preference store. This method exist to make the |
| * preference store accessible to other classes. |
| * |
| * @return the editor's preference store |
| */ |
| public IPreferenceStore getEditorPreferenceStore() { |
| return super.getPreferenceStore(); |
| } |
| |
| public JETDocumentPartitionScanner getPartitionScanner() { |
| return partitionScanner; |
| } |
| |
| public JETTokenStyleManager getTokenStyleManager() { |
| return tokenStyleManager; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see |
| * org.eclipse.ui.texteditor.StatusTextEditor#handleEditorInputChanged() |
| */ |
| protected void handleEditorInputChanged() { |
| super.handleEditorInputChanged(); |
| handleDocumentChange(null); |
| } |
| |
| public void handleDocumentChange(DocumentEvent e) { |
| cUnitInvalidated = true; |
| if (outlinePage != null && outlinePage.getControl().isVisible()) |
| ((JETOutlinePage) outlinePage).setInput(); |
| } |
| |
| public boolean isCUnitInvalidated() { |
| return cUnitInvalidated; |
| } |
| |
| public CompilationHelper compilationHelper() { |
| CompilationHelper jetCompilationHelper = new CompilationHelper(project); |
| return jetCompilationHelper; |
| } |
| |
| /** |
| * Compute and retrun the compilation unit of this document |
| * |
| * @return the computed (or recomputed) compilation unit for this template |
| */ |
| public JETCompilationUnit requestCompilationUnit() { |
| if (cUnit == null || cUnitInvalidated) { |
| IEditorInput editorInput = getEditorInput(); |
| String templatePath = editorInput.getName(); |
| if (editorInput instanceof IFileEditorInput) |
| templatePath = ((IFileEditorInput) editorInput).getFile() |
| .getProjectRelativePath().toString(); |
| if (sourceViewer == null) { // sourceViewer is null ... we can parse |
| // from the file directly |
| cUnit = compilationHelper().getAST(templatePath); |
| } else { |
| cUnit = compilationHelper().getASTFromSource( |
| sourceViewer.getDocument().get(), templatePath); |
| } |
| PositionsCollector collector = new PositionsCollector(); |
| cUnit.accept(collector); |
| positions = collector.getPositions(); |
| cUnitInvalidated = false; |
| } |
| return cUnit; |
| } |
| |
| public JETASTElement getASTElement(int offset) { |
| requestCompilationUnit(); |
| return getCachedASTElement(offset); |
| } |
| |
| public JETASTElement getCachedASTElement(int offset) { |
| for (Iterator iterator = positions.keySet().iterator(); iterator |
| .hasNext();) { |
| Position pos = (Position) iterator.next(); |
| if (pos.offset <= offset && pos.length + pos.offset > offset) |
| return (JETASTElement) positions.get(pos); |
| } |
| return null; |
| } |
| |
| public IJavaProject getJavaProject() { |
| return JavaCore.create(project); |
| } |
| |
| protected void createActions() { |
| super.createActions(); |
| ResourceBundle resourceBundle = ResourceBundle |
| .getBundle("org.eclipse.jet.internal.editor.i18n.TextEditorMessages"); |
| Action action = new ContentAssistAction(resourceBundle, |
| "ContentAssistProposal.", this); |
| action |
| .setActionDefinitionId("org.eclipse.ui.edit.text.contentAssist.proposals"); |
| setAction("ContentAssistProposal", action); |
| markAsStateDependentAction("ContentAssistProposal", true); |
| } |
| |
| protected void handlePreferenceStoreChanged(PropertyChangeEvent event) { |
| super.handlePreferenceStoreChanged(event); |
| if (event.getNewValue() instanceof RGB) |
| getTokenStyleManager().bindColor(event.getProperty(), |
| (RGB) event.getNewValue()); |
| } |
| |
| public Object getAdapter(Class required) { |
| if (IContentOutlinePage.class.equals(required)) { |
| if (outlinePage == null) |
| outlinePage = createOutlinePage(); |
| return outlinePage; |
| } |
| if (IEncodingSupport.class.equals(required)) |
| return fEncodingSupport; |
| if (projectionSupport != null) { |
| Object adapter = projectionSupport.getAdapter(getSourceViewer(), |
| required); |
| if (adapter != null) |
| return adapter; |
| } |
| return super.getAdapter(required); |
| } |
| |
| public void editorContextMenuAboutToShow(IMenuManager menu) { |
| super.editorContextMenuAboutToShow(menu); |
| addAction(menu, "settings", "setDelegateColorerActionID"); |
| } |
| |
| protected IContentOutlinePage createOutlinePage() { |
| JETOutlinePage page = new JETOutlinePage(this); |
| page.addSelectionChangedListener(new ISelectionChangedListener() { |
| public void selectionChanged(SelectionChangedEvent event) { |
| handleContentOutlineSelection(event.getSelection()); |
| } |
| }); |
| return page; |
| } |
| |
| protected void handleContentOutlineSelection(ISelection selection) { |
| IStructuredSelection ssel = (IStructuredSelection) selection; |
| Object firstElement = ssel.getFirstElement(); |
| if (firstElement instanceof JETASTElement) { |
| JETASTElement astElement = (JETASTElement) firstElement; |
| int start = astElement.getStart(); |
| int end = astElement.getEnd(); |
| int length = end - start; |
| if (astElement instanceof TextElement) { |
| if (astElement.getPrevElement() != null) |
| start = astElement.getPrevElement().getEnd(); |
| else |
| start = 0; |
| length = ((TextElement) astElement).getText().length; |
| } |
| sourceViewer.setSelectedRange(start, length); |
| sourceViewer.revealRange(start, length); |
| } |
| } |
| |
| private static class PositionsCollector extends JETASTVisitor { |
| private Map positions; |
| |
| private PositionsCollector() { |
| positions = new HashMap(); |
| } |
| |
| public Map getPositions() { |
| return positions; |
| } |
| |
| public boolean visit(JavaDeclaration declaration) { |
| return matchOffset(declaration); |
| } |
| |
| public boolean visit(JETDirective directive) { |
| return matchOffset(directive); |
| } |
| |
| public boolean visit(JavaExpression expression) { |
| return matchOffset(expression); |
| } |
| |
| public boolean visit(IncludedContent content) { |
| // don't process include content |
| return false; |
| } |
| |
| public boolean visit(JavaScriptlet scriptlet) { |
| return matchOffset(scriptlet); |
| } |
| |
| public boolean visit(XMLEmptyElement xmlEmptyElement) { |
| return matchOffset(xmlEmptyElement); |
| } |
| |
| public boolean visit(XMLBodyElement xmlBodyElement) { |
| return matchOffset(xmlBodyElement); |
| } |
| |
| public boolean visit(XMLBodyElementEnd xmlBodyElementEnd) { |
| return matchOffset(xmlBodyElementEnd); |
| } |
| |
| public boolean visit(Comment comment) { |
| return matchOffset(comment); |
| } |
| |
| public boolean matchOffset(JETASTElement astElement) { |
| int start = astElement.getStart(); |
| if (start < 0) { |
| return false; |
| } |
| int end = astElement.getEnd(); |
| int length = end - Math.abs(start); |
| positions.put(new Position(start, length), astElement); |
| return true; |
| } |
| } |
| } |