package com.redhat.ceylon.eclipse.code.editor; import static com.redhat.ceylon.eclipse.code.preferences.CeylonPreferenceInitializer.AUTO_ACTIVATION; import static com.redhat.ceylon.eclipse.code.preferences.CeylonPreferenceInitializer.AUTO_ACTIVATION_DELAY; import static com.redhat.ceylon.eclipse.code.preferences.CeylonPreferenceInitializer.AUTO_INSERT; import static com.redhat.ceylon.eclipse.code.preferences.CeylonPreferenceInitializer.AUTO_INSERT_PREFIX; import static com.redhat.ceylon.eclipse.java2ceylon.Java2CeylonProxies.completionJ2C; import static com.redhat.ceylon.eclipse.util.EditorUtil.createColor; import static com.redhat.ceylon.eclipse.util.EditorUtil.getPopupStyle; import static com.redhat.ceylon.eclipse.util.Highlights.DOC_BACKGROUND; import static com.redhat.ceylon.eclipse.util.Highlights.getCurrentThemeColor; import static org.eclipse.jdt.ui.PreferenceConstants.APPEARANCE_JAVADOC_FONT; import static org.eclipse.jface.dialogs.DialogSettings.getOrCreateSection; import static org.eclipse.jface.text.AbstractInformationControlManager.ANCHOR_GLOBAL; import static org.eclipse.jface.text.IDocument.DEFAULT_CONTENT_TYPE; import static org.eclipse.ui.texteditor.AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT; import static org.eclipse.ui.texteditor.AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND; import static org.eclipse.ui.texteditor.AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT; import org.eclipse.jface.bindings.keys.KeySequence; import org.eclipse.jface.bindings.keys.KeyStroke; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.IAutoEditStrategy; import org.eclipse.jface.text.IInformationControl; import org.eclipse.jface.text.IInformationControlCreator; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextDoubleClickStrategy; import org.eclipse.jface.text.ITextHover; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.ITextViewerExtension2; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.contentassist.ContentAssistEvent; import org.eclipse.jface.text.contentassist.ContentAssistant; import org.eclipse.jface.text.contentassist.ICompletionListener; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.text.contentassist.IContentAssistant; import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; import org.eclipse.jface.text.hyperlink.IHyperlinkDetector; import org.eclipse.jface.text.information.IInformationPresenter; import org.eclipse.jface.text.information.IInformationProvider; import org.eclipse.jface.text.information.IInformationProviderExtension; import org.eclipse.jface.text.information.InformationPresenter; import org.eclipse.jface.text.presentation.PresentationReconciler; import org.eclipse.jface.text.quickassist.IQuickAssistAssistant; import org.eclipse.jface.text.reconciler.IReconciler; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.editors.text.EditorsUI; import org.eclipse.ui.editors.text.TextSourceViewerConfiguration; import org.eclipse.ui.internal.editors.text.EditorsPlugin; import com.redhat.ceylon.eclipse.code.browser.BrowserInformationControl; import com.redhat.ceylon.eclipse.code.complete.EclipseCompletionProcessor; import com.redhat.ceylon.eclipse.code.correct.CeylonCorrectionProcessor; import com.redhat.ceylon.eclipse.code.hover.AnnotationHover; import com.redhat.ceylon.eclipse.code.hover.BestMatchHover; import com.redhat.ceylon.eclipse.code.hover.CeylonInformationControlCreator; import com.redhat.ceylon.eclipse.code.hover.CeylonInformationProvider; import com.redhat.ceylon.eclipse.code.hover.CeylonSourceHover; import com.redhat.ceylon.eclipse.code.outline.HierarchyPopup; import com.redhat.ceylon.eclipse.code.outline.OutlinePopup; import com.redhat.ceylon.eclipse.code.parse.CeylonParseController; import com.redhat.ceylon.eclipse.code.resolve.CeylonHyperlinkDetector; import com.redhat.ceylon.eclipse.code.resolve.CeylonJavaBackendHyperlinkDetector; import com.redhat.ceylon.eclipse.code.resolve.CeylonJavascriptBackendHyperlinkDetector; import com.redhat.ceylon.eclipse.code.resolve.CeylonNativeHeaderHyperlinkDetector; import com.redhat.ceylon.eclipse.code.resolve.JavaHyperlinkDetector; import com.redhat.ceylon.eclipse.code.resolve.ReferencesHyperlinkDetector; import com.redhat.ceylon.eclipse.code.search.ReferencesPopup; import com.redhat.ceylon.eclipse.ui.CeylonPlugin; public class CeylonSourceViewerConfiguration extends TextSourceViewerConfiguration { protected final CeylonEditor editor; public CeylonSourceViewerConfiguration(CeylonEditor editor) { super(EditorsUI.getPreferenceStore()); this.editor = editor; } public PresentationReconciler getPresentationReconciler( ISourceViewer sourceViewer) { PresentationReconciler reconciler = new PresentationReconciler(); //make sure we pass the sourceViewer we get as an argument here //otherwise it breaks syntax highlighting in Code popup PresentationDamageRepairer damageRepairer = new PresentationDamageRepairer(sourceViewer, editor); reconciler.setRepairer(damageRepairer, DEFAULT_CONTENT_TYPE); reconciler.setDamager(damageRepairer, DEFAULT_CONTENT_TYPE); return reconciler; } private static final class CompletionListener implements ICompletionListener { private CeylonEditor editor; private EclipseCompletionProcessor processor; // private CeylonCompletionProcessor processor; private CompletionListener(CeylonEditor editor, // CeylonCompletionProcessor processor) { EclipseCompletionProcessor processor) { this.editor = editor; this.processor = processor; } @Override public void selectionChanged( ICompletionProposal proposal, boolean smartToggle) {} @Override public void assistSessionStarted( ContentAssistEvent event) { if (editor!=null) { editor.pauseBackgroundParsing(); } if (event.assistant instanceof IContentAssistantExtension2) { ((IContentAssistantExtension2)event.assistant).setStatusMessage(CeylonContentAssistant.secondLevelStatusMessage); } processor.sessionStarted(event.isAutoActivated); /*try { editor.getSite().getWorkbenchWindow().run(true, true, new Warmup()); } catch (Exception e) {}*/ } @Override public void assistSessionEnded( ContentAssistEvent event) { if (editor!=null) { editor.unpauseBackgroundParsing(); editor.scheduleParsing(false); } } } public ContentAssistant getContentAssistant( ISourceViewer sourceViewer) { if (editor==null) return null; ContentAssistant contentAssistant = new CeylonContentAssistant(); contentAssistant.setRestoreCompletionProposalSize( getOrCreateSection(getSettings(), "completion_proposal_popup")); EclipseCompletionProcessor completionProcessor = completionJ2C().newCompletionProcessor(editor); // CeylonCompletionProcessor completionProcessor = // new CeylonCompletionProcessor(editor); CompletionListener listener = new CompletionListener(editor, completionProcessor); contentAssistant.addCompletionListener(listener); contentAssistant.setContentAssistProcessor( completionProcessor, DEFAULT_CONTENT_TYPE); configCompletionPopup(contentAssistant); contentAssistant.enableColoredLabels(true); contentAssistant.setRepeatedInvocationMode(true); KeyStroke key = KeyStroke.getInstance(SWT.CTRL, SWT.SPACE); contentAssistant.setRepeatedInvocationTrigger( KeySequence.getInstance(key)); CeylonContentAssistant.secondLevelStatusMessage = key.format() + " to toggle second-level completions"; contentAssistant.setStatusMessage(CeylonContentAssistant.secondLevelStatusMessage); CeylonContentAssistant.retrieveCompleteResultsStatusMessage = key.format() + " to retrieve all results"; contentAssistant.setStatusLineVisible(true); contentAssistant.setInformationControlCreator( new CeylonInformationControlCreator(editor, "Tab or click for focus")); contentAssistant.setContextInformationPopupOrientation( IContentAssistant.CONTEXT_INFO_ABOVE); contentAssistant.setShowEmptyList(true); return contentAssistant; } static void configCompletionPopup( ContentAssistant contentAssistant) { IPreferenceStore preferenceStore = CeylonPlugin.getPreferences(); if (preferenceStore!=null) { contentAssistant.enableAutoInsert( preferenceStore.getBoolean(AUTO_INSERT)); contentAssistant.enableAutoActivation( preferenceStore.getBoolean(AUTO_ACTIVATION)); contentAssistant.setAutoActivationDelay( preferenceStore.getInt(AUTO_ACTIVATION_DELAY)); contentAssistant.enablePrefixCompletion( preferenceStore.getBoolean(AUTO_INSERT_PREFIX)); } } @Override public IQuickAssistAssistant getQuickAssistAssistant( ISourceViewer sourceViewer) { if (editor==null) return null; CeylonCorrectionProcessor quickAssist = new CeylonCorrectionProcessor(editor); quickAssist.setRestoreCompletionProposalSize( getOrCreateSection(getSettings(), "quickassist_proposal_popup")); quickAssist.enableColoredLabels(true); return quickAssist; } public AnnotationHover getAnnotationHover( ISourceViewer sourceViewer) { return new AnnotationHover(editor, true); } public AnnotationHover getOverviewRulerAnnotationHover( ISourceViewer sourceViewer) { return new AnnotationHover(editor, true); } public IAutoEditStrategy[] getAutoEditStrategies( ISourceViewer sourceViewer, String contentType) { return new IAutoEditStrategy[] { new CeylonAutoEditStrategy() }; } public ITextDoubleClickStrategy getDoubleClickStrategy( ISourceViewer sourceViewer, String contentType) { return new DoubleClickStrategy(); } public IHyperlinkDetector[] getHyperlinkDetectors( ISourceViewer sourceViewer) { CeylonParseController controller = getParseController(); if (controller==null) { return new IHyperlinkDetector[0]; } else { return new IHyperlinkDetector[] { new CeylonHyperlinkDetector( editor, controller), new CeylonNativeHeaderHyperlinkDetector( editor, controller), new CeylonJavaBackendHyperlinkDetector( editor, controller), new CeylonJavascriptBackendHyperlinkDetector( editor, controller), new JavaHyperlinkDetector(controller), new ReferencesHyperlinkDetector( editor, controller) }; } } //TODO: We need a CeylonParseControllerProvider // which CeylonEditor implements - since // having to extend this class and override // is just sucky. protected CeylonParseController getParseController() { return editor==null ? null : editor.getParseController(); } @Override public IInformationPresenter getInformationPresenter( ISourceViewer sourceViewer) { InformationPresenter presenter = new InformationPresenter( new CeylonInformationControlCreator( editor, "F2 for focus")); presenter.setDocumentPartitioning( getConfiguredDocumentPartitioning(sourceViewer)); presenter.setInformationProvider( new CeylonInformationProvider(editor), DEFAULT_CONTENT_TYPE); // sizes: see org.eclipse.jface.text.TextViewer.TEXT_HOVER_*_CHARS presenter.setSizeConstraints(100, 40, false, true); return presenter; } /** * Used to present hover help (anything else?) */ public IInformationControlCreator getInformationControlCreator(ISourceViewer sourceViewer) { return new IInformationControlCreator() { @Override public IInformationControl createInformationControl(Shell parent) { BrowserInformationControl control = new BrowserInformationControl(parent, APPEARANCE_JAVADOC_FONT, false); IPreferenceStore editorPreferenceStore = EditorsPlugin.getDefault().getPreferenceStore(); if (!editorPreferenceStore.getBoolean( PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT)) { control.setBackgroundColor( getCurrentThemeColor(DOC_BACKGROUND)); // createColor(CeylonPlugin.getPreferences(), // PREFERENCE_COLOR_BACKGROUND)); } if (!editorPreferenceStore.getBoolean( PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT)) { control.setForegroundColor( createColor(editorPreferenceStore, PREFERENCE_COLOR_FOREGROUND)); } return control; } }; } private static final int[] STATE_MASKS = new int[] { ITextViewerExtension2.DEFAULT_HOVER_STATE_MASK, SWT.SHIFT }; @Override public int[] getConfiguredTextHoverStateMasks( ISourceViewer sourceViewer, String contentType) { return STATE_MASKS; } @Override public ITextHover getTextHover( ISourceViewer sourceViewer, String contentType, int stateMask) { if (stateMask==SWT.SHIFT) { return new CeylonSourceHover(editor); } else { return getTextHover(sourceViewer, contentType); } } public ITextHover getTextHover( ISourceViewer sourceViewer, String contentType) { if (editor==null) return null; return new BestMatchHover(editor); } private IDialogSettings getSettings() { return CeylonPlugin.getInstance() .getDialogSettings(); } private InformationPresenter createPresenter( ISourceViewer sourceViewer, IInformationControlCreator creator) { InformationPresenter presenter = new InformationPresenter(creator); String partitioning = getConfiguredDocumentPartitioning(sourceViewer); presenter.setDocumentPartitioning(partitioning); presenter.setAnchor(ANCHOR_GLOBAL); presenter.setInformationProvider( new DummyInformationProvider(), DEFAULT_CONTENT_TYPE); return presenter; } public IInformationPresenter getOutlinePresenter( ISourceViewer sourceViewer) { if (editor==null) { return null; } OutlinePresenterControlCreator creator = new OutlinePresenterControlCreator(editor); InformationPresenter presenter = createPresenter(sourceViewer, creator); presenter.setSizeConstraints(60, 20, true, false); presenter.setRestoreInformationControlBounds( getOrCreateSection(getSettings(), "outline_presenter_bounds"), true, true); return presenter; } public IInformationPresenter getHierarchyPresenter( ISourceViewer sourceViewer) { if (editor==null) { return null; } HierarchyPresenterControlCreator creator = new HierarchyPresenterControlCreator(editor); InformationPresenter presenter = createPresenter(sourceViewer, creator); presenter.setSizeConstraints(80, 20, true, false); presenter.setRestoreInformationControlBounds( getOrCreateSection(getSettings(), "hierarchy_presenter_bounds"), true, true); return presenter; } public IInformationPresenter getDefinitionPresenter( ISourceViewer sourceViewer) { if (editor==null) { return null; } DefinitionPresenterControlCreator creator = new DefinitionPresenterControlCreator(editor); InformationPresenter presenter = createPresenter(sourceViewer, creator); presenter.setSizeConstraints(80, 25, true, false); presenter.setRestoreInformationControlBounds( getOrCreateSection(getSettings(), "code_presenter_bounds"), true, true); return presenter; } public IInformationPresenter getReferencesPresenter( ISourceViewer sourceViewer) { if (editor==null) { return null; } ReferencesPresenterControlCreator creator = new ReferencesPresenterControlCreator(editor); InformationPresenter presenter = createPresenter(sourceViewer, creator); presenter.setSizeConstraints(80, 20, true, false); presenter.setRestoreInformationControlBounds( getOrCreateSection(getSettings(), "refs_presenter_bounds"), true, true); return presenter; } private static final class OutlinePresenterControlCreator implements IInformationControlCreator { private CeylonEditor editor; private OutlinePresenterControlCreator( CeylonEditor editor) { this.editor = editor; } @Override public IInformationControl createInformationControl( Shell parent) { return new OutlinePopup(editor, parent, getPopupStyle()); } } private static final class HierarchyPresenterControlCreator implements IInformationControlCreator { private CeylonEditor editor; private HierarchyPresenterControlCreator( CeylonEditor editor) { this.editor = editor; } @Override public IInformationControl createInformationControl( Shell parent) { return new HierarchyPopup(editor, parent, getPopupStyle()); } } private static final class DefinitionPresenterControlCreator implements IInformationControlCreator { private CeylonEditor editor; private DefinitionPresenterControlCreator( CeylonEditor editor) { this.editor = editor; } @Override public IInformationControl createInformationControl( Shell parent) { return new PeekDefinitionPopup(parent, getPopupStyle(), editor); } } private static final class ReferencesPresenterControlCreator implements IInformationControlCreator { private CeylonEditor editor; private ReferencesPresenterControlCreator( CeylonEditor editor) { this.editor = editor; } @Override public IInformationControl createInformationControl( Shell parent) { return new ReferencesPopup(parent, getPopupStyle(), editor); } } private static final class DummyInformationProvider implements IInformationProvider, IInformationProviderExtension { @Override public IRegion getSubject( ITextViewer textViewer, int offset) { return new Region(offset, 0); // Could be anything, since it's ignored below in getInformation2()... } @Override public String getInformation( ITextViewer textViewer, IRegion subject) { // shouldn't be called, given IInformationProviderExtension??? throw new UnsupportedOperationException(); } @Override public Object getInformation2( ITextViewer textViewer, IRegion subject) { return new Object(); } } @Override public IReconciler getReconciler(ISourceViewer sourceViewer) { //don't spell-check! return null; } }