/*
* Copyright 2009-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.groovy.eclipse.editor;
import static org.codehaus.groovy.eclipse.core.preferences.PreferenceConstants.GROOVY_EDITOR_HIGHLIGHT_STRINGS_COLOR;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import org.codehaus.groovy.eclipse.GroovyPlugin;
import org.codehaus.groovy.eclipse.core.GroovyCore;
import org.codehaus.groovy.eclipse.editor.highlighting.HighlightingExtenderRegistry;
import org.eclipse.core.resources.IProject;
import org.eclipse.jdt.groovy.core.util.ReflectionUtils;
import org.eclipse.jdt.internal.ui.text.AbstractJavaScanner;
import org.eclipse.jdt.internal.ui.text.SingleTokenJavaScanner;
import org.eclipse.jdt.internal.ui.text.java.CompletionProposalCategory;
import org.eclipse.jdt.internal.ui.text.java.ContentAssistProcessor;
import org.eclipse.jdt.internal.ui.text.java.JavaAutoIndentStrategy;
import org.eclipse.jdt.internal.ui.text.java.JavaCompletionProcessor;
import org.eclipse.jdt.internal.ui.text.java.hover.JavaInformationProvider;
import org.eclipse.jdt.ui.text.IJavaPartitions;
import org.eclipse.jdt.ui.text.JavaSourceViewerConfiguration;
import org.eclipse.jdt.ui.text.java.hover.IJavaEditorTextHover;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.IAutoEditStrategy;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.contentassist.ContentAssistant;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.text.contentassist.IContentAssistant;
import org.eclipse.jface.text.information.IInformationPresenter;
import org.eclipse.jface.text.information.IInformationProvider;
import org.eclipse.jface.text.information.InformationPresenter;
import org.eclipse.jface.text.presentation.IPresentationReconciler;
import org.eclipse.jface.text.presentation.PresentationReconciler;
import org.eclipse.jface.text.rules.DefaultDamagerRepairer;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.texteditor.ITextEditor;
public class GroovyConfiguration extends JavaSourceViewerConfiguration {
public GroovyConfiguration(GroovyColorManager colorManager, IPreferenceStore preferenceStore, ITextEditor editor) {
super(colorManager, preferenceStore, editor, IJavaPartitions.JAVA_PARTITIONING);
// replace Java's string scanner to enable Groovy's color choice
AbstractJavaScanner stringScanner = new SingleTokenJavaScanner(colorManager, preferenceStore, GROOVY_EDITOR_HIGHLIGHT_STRINGS_COLOR);
ReflectionUtils.setPrivateField(JavaSourceViewerConfiguration.class, "fStringScanner", this, stringScanner);
// replace Java's code scanner to enable Groovy's token rules
try {
IProject project = null;
if (editor != null && editor instanceof GroovyEditor) {
if (editor.getEditorInput() instanceof FileEditorInput) {
project = ((FileEditorInput) editor.getEditorInput()).getFile().getProject();
}
}
HighlightingExtenderRegistry registry = GroovyPlugin.getDefault().getTextTools().getHighlightingExtenderRegistry();
AbstractJavaScanner codeScanner = new GroovyTagScanner(colorManager,
registry.getInitialAdditionalRulesForProject(project),
registry.getAdditionalRulesForProject(project),
registry.getExtraGroovyKeywordsForProject(project),
registry.getExtraGJDKKeywordsForProject(project));
ReflectionUtils.setPrivateField(JavaSourceViewerConfiguration.class, "fCodeScanner", this, codeScanner);
} catch (Exception e) {
GroovyCore.logException("Error creating and registering GroovyTagScanner", e);
}
}
@Override
public IAutoEditStrategy[] getAutoEditStrategies(ISourceViewer sourceViewer, String contentType) {
IAutoEditStrategy[] strategies;
if (GroovyPartitionScanner.GROOVY_MULTILINE_STRINGS.equals(contentType) || IJavaPartitions.JAVA_STRING.equals(contentType)) {
// TODO: Should GroovyMultilineStringAutoEditStrategy delegate to JavaStringAutoIndentStrategy instead of DefaultIndentLineAutoEditStrategy?
// TODO: GroovyMultilineStringAutoEditStrategy does nothing; was there some intended behavior that is incomplete?
strategies = new IAutoEditStrategy[] { new GroovyMultilineStringAutoEditStrategy(contentType) };
} else {
strategies = super.getAutoEditStrategies(sourceViewer, contentType);
for (int i = 0, n = strategies.length; i < n; i += 1) {
if (strategies[i] instanceof JavaAutoIndentStrategy) {
strategies[i] = new GroovyAutoIndentStrategy((JavaAutoIndentStrategy) strategies[i]);
}
}
}
return strategies;
}
@Override
public String[] getConfiguredContentTypes(ISourceViewer sourceViewer) {
// TODO: Copy from GroovyPartitionScanner.LEGAL_CONTENT_TYPES?
return new String[] {
IDocument.DEFAULT_CONTENT_TYPE,
IJavaPartitions.JAVA_DOC,
IJavaPartitions.JAVA_MULTI_LINE_COMMENT,
IJavaPartitions.JAVA_SINGLE_LINE_COMMENT,
IJavaPartitions.JAVA_STRING,
IJavaPartitions.JAVA_CHARACTER,
GroovyPartitionScanner.GROOVY_MULTILINE_STRINGS
};
}
@Override @SuppressWarnings("unchecked")
public IContentAssistant getContentAssistant(ISourceViewer sourceViewer) {
ContentAssistant assistant = (ContentAssistant) super.getContentAssistant(sourceViewer);
String contentType = GroovyPartitionScanner.GROOVY_MULTILINE_STRINGS;
assistant.setContentAssistProcessor(new JavaCompletionProcessor(getEditor(), assistant, contentType), contentType);
// retain only Groovy-approved completion proposal categories
IContentAssistProcessor processor = assistant.getContentAssistProcessor(IDocument.DEFAULT_CONTENT_TYPE);
List<CompletionProposalCategory> categories = (List<CompletionProposalCategory>)
ReflectionUtils.getPrivateField(ContentAssistProcessor.class, "fCategories", processor);
List<CompletionProposalCategory> newCategories = new ArrayList<CompletionProposalCategory>();
for (CompletionProposalCategory category : categories) {
if (GROOVY_CONTENT_ASSIST.matcher(category.getId()).matches()) {
newCategories.add(category);
}
}
ReflectionUtils.setPrivateField(ContentAssistProcessor.class, "fCategories", processor, newCategories);
return assistant;
}
private static final Pattern GROOVY_CONTENT_ASSIST = Pattern.compile("org.codehaus.groovy.+|org.eclipse.jdt.ui.(default|text)ProposalCategory");
/*
* Type parameters have changed between 3.7 and 4.2. So just remove them
* Otherwise compile errors
*/
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
protected Map getHyperlinkDetectorTargets(ISourceViewer sourceViewer) {
Map targets = super.getHyperlinkDetectorTargets(sourceViewer);
targets.put("org.codehaus.groovy.eclipse.groovyCode", getEditor()); //$NON-NLS-1$
return targets;
}
/**
* Use our {@link GroovyExtraInformationHover} instead of a
* {@link JavadocHover}. Shows extra information provided
* by DSLs
*/
@Override
public IInformationPresenter getInformationPresenter(ISourceViewer sourceViewer) {
IInformationPresenter informationPresenter = super.getInformationPresenter(sourceViewer);
// the org.eclipse.jdt.internal.ui.text.java.hover.JavaTypeHover was removed in 4.2.M7
// if this class doesn't exist then we don't need to do anything with it.
try {
Class<?> clazz = Class.forName("org.eclipse.jdt.internal.ui.text.java.hover.JavaTypeHover");
JavaInformationProvider provider = (JavaInformationProvider) informationPresenter.getInformationProvider(IDocument.DEFAULT_CONTENT_TYPE);
IJavaEditorTextHover implementation = (IJavaEditorTextHover) ReflectionUtils.getPrivateField(JavaInformationProvider.class, "fImplementation", provider);
// when the extra information is invoked from this way, always return
// some information since there is no BestMatchHover to fall back on
// This hover is typically invoked when pressing F2.
// Hovers that are invoked through a mouse, use a BestMatchHover.
GroovyExtraInformationHover hover = new GroovyExtraInformationHover(true);
hover.setEditor(this.getEditor());
ReflectionUtils.setPrivateField(clazz, "fJavadocHover", implementation, hover);
} catch (ClassNotFoundException e) {
// can ignore. Will happen if on 4.2 or later
}
return informationPresenter;
}
@Override
public IInformationPresenter getOutlinePresenter(ISourceViewer sourceViewer, boolean doCodeResolve) {
IInformationPresenter presenter = super.getOutlinePresenter(sourceViewer, doCodeResolve);
if (presenter instanceof InformationPresenter) {
IInformationProvider provider = presenter.getInformationProvider(IDocument.DEFAULT_CONTENT_TYPE);
((InformationPresenter) presenter).setInformationProvider(provider, GroovyPartitionScanner.GROOVY_MULTILINE_STRINGS);
}
return presenter;
}
@Override
public IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer) {
PresentationReconciler reconciler = (PresentationReconciler) super.getPresentationReconciler(sourceViewer);
DefaultDamagerRepairer dr = new DefaultDamagerRepairer(getStringScanner());
reconciler.setDamager(dr, GroovyPartitionScanner.GROOVY_MULTILINE_STRINGS);
reconciler.setRepairer(dr, GroovyPartitionScanner.GROOVY_MULTILINE_STRINGS);
return reconciler;
}
}