/*
* 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 java.lang.reflect.Array.getLength;
import static java.util.regex.Pattern.compile;
import static org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_ASSIGNMENT_OPERATOR;
import static org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_ASSIGNMENT_OPERATOR;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.regex.Matcher;
import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.ModuleNode;
import org.codehaus.groovy.ast.expr.ArgumentListExpression;
import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
import org.codehaus.groovy.eclipse.GroovyPlugin;
import org.codehaus.groovy.eclipse.codebrowsing.fragments.ASTFragmentKind;
import org.codehaus.groovy.eclipse.codebrowsing.fragments.IASTFragment;
import org.codehaus.groovy.eclipse.codebrowsing.requestor.ASTNodeFinder;
import org.codehaus.groovy.eclipse.codebrowsing.selection.FindSurroundingNode;
import org.codehaus.groovy.eclipse.editor.actions.ExpandSelectionAction;
import org.codehaus.groovy.eclipse.editor.actions.GroovyConvertLocalToFieldAction;
import org.codehaus.groovy.eclipse.editor.actions.GroovyExtractConstantAction;
import org.codehaus.groovy.eclipse.editor.actions.GroovyExtractLocalAction;
import org.codehaus.groovy.eclipse.editor.actions.GroovyExtractMethodAction;
import org.codehaus.groovy.eclipse.editor.actions.GroovyTabAction;
import org.codehaus.groovy.eclipse.editor.actions.IGroovyEditorActionDefinitionIds;
import org.codehaus.groovy.eclipse.editor.highlighting.GroovySemanticReconciler;
import org.codehaus.groovy.eclipse.editor.outline.GroovyOutlinePage;
import org.codehaus.groovy.eclipse.editor.outline.OutlineExtenderRegistry;
import org.codehaus.groovy.eclipse.refactoring.actions.AddImportOnSelectionAction;
import org.codehaus.groovy.eclipse.refactoring.actions.FormatAllGroovyAction;
import org.codehaus.groovy.eclipse.refactoring.actions.FormatGroovyAction;
import org.codehaus.groovy.eclipse.refactoring.actions.FormatKind;
import org.codehaus.groovy.eclipse.refactoring.actions.GroovyRenameAction;
import org.codehaus.groovy.eclipse.refactoring.actions.OrganizeGroovyImportsAction;
import org.codehaus.groovy.eclipse.refactoring.core.utils.StringUtils;
import org.codehaus.groovy.eclipse.search.GroovyOccurrencesFinder;
import org.codehaus.groovy.eclipse.ui.decorators.GroovyImageDecorator;
import org.codehaus.jdt.groovy.model.GroovyCompilationUnit;
import org.codehaus.jdt.groovy.model.ModuleNodeMapper.ModuleNodeInfo;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IImportDeclaration;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.ISourceReference;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.groovy.core.util.ReflectionUtils;
import org.eclipse.jdt.internal.core.CompilationUnit;
import org.eclipse.jdt.internal.debug.ui.BreakpointMarkerUpdater;
import org.eclipse.jdt.internal.ui.IJavaHelpContextIds;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.actions.ActionUtil;
import org.eclipse.jdt.internal.ui.actions.AllCleanUpsAction;
import org.eclipse.jdt.internal.ui.actions.CompositeActionGroup;
import org.eclipse.jdt.internal.ui.actions.SurroundWithActionGroup;
import org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitEditor;
import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor;
import org.eclipse.jdt.internal.ui.javaeditor.JavaOutlinePage;
import org.eclipse.jdt.internal.ui.javaeditor.JavaSourceViewer;
import org.eclipse.jdt.internal.ui.javaeditor.selectionactions.SelectionHistory;
import org.eclipse.jdt.internal.ui.javaeditor.selectionactions.StructureSelectionAction;
import org.eclipse.jdt.internal.ui.text.JavaHeuristicScanner;
import org.eclipse.jdt.internal.ui.text.JavaWordFinder;
import org.eclipse.jdt.internal.ui.text.Symbols;
import org.eclipse.jdt.internal.ui.text.java.IJavaReconcilingListener;
import org.eclipse.jdt.internal.ui.util.ElementValidator;
import org.eclipse.jdt.ui.PreferenceConstants;
import org.eclipse.jdt.ui.actions.GenerateActionGroup;
import org.eclipse.jdt.ui.actions.IJavaEditorActionDefinitionIds;
import org.eclipse.jdt.ui.actions.RefactorActionGroup;
import org.eclipse.jdt.ui.actions.SelectionDispatchAction;
import org.eclipse.jdt.ui.text.IJavaPartitions;
import org.eclipse.jdt.ui.text.JavaSourceViewerConfiguration;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.BadPositionCategoryException;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentExtension;
import org.eclipse.jface.text.IDocumentExtension4;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.IPositionUpdater;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.ITextViewerExtension;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.TextUtilities;
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.LinkedModeUI.ExitFlags;
import org.eclipse.jface.text.link.LinkedModeUI.IExitPolicy;
import org.eclipse.jface.text.link.LinkedPosition;
import org.eclipse.jface.text.link.LinkedPositionGroup;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.VerifyKeyListener;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.text.edits.DeleteEdit;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.ReplaceEdit;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ActionGroup;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.texteditor.AbstractMarkerAnnotationModel;
import org.eclipse.ui.texteditor.ChainedPreferenceStore;
import org.eclipse.ui.texteditor.ITextEditorActionConstants;
import org.eclipse.ui.texteditor.link.EditorLinkedModeUI;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
public class GroovyEditor extends CompilationUnitEditor {
public static final String EDITOR_ID = "org.codehaus.groovy.eclipse.editor.GroovyEditor";
/**
* Borrowed from {@link CompilationUnitEditor.ExclusivePositionUpdater}
* Position updater that takes any changes at the borders of a position to not belong to the position.
*
* @since 3.0
*/
private static class GroovyExclusivePositionUpdater 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 GroovyExclusivePositionUpdater(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
}
}
}
/**
* Borrowed from {@link CompilationUnitEditor.ExitPolicy}
*/
private class GroovyExitPolicy implements IExitPolicy {
final char fExitCharacter;
final char fEscapeCharacter;
final Stack<GroovyBracketLevel> fStack;
final int fSize;
public GroovyExitPolicy(char exitCharacter, char escapeCharacter, Stack<GroovyBracketLevel> 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) {
GroovyBracketLevel 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;
}
}
/**
* Borrowed from {@link CompilationUnitEditor.BracketLevel}
*/
private static class GroovyBracketLevel {
LinkedModeUI fUI;
Position fFirstPosition;
Position fSecondPosition;
}
/**
* Borrowed from {@link CompilationUnitEditor.BracketInserter}
*
* Changes marked with // GROOVY
*/
private class GroovyBracketInserter implements VerifyKeyListener, ILinkedModeListener {
private boolean fCloseBrackets= true;
private boolean fCloseBraces = true; // GROOVY change
private boolean fCloseStrings= true;
private boolean fCloseAngularBrackets= true;
private final String CATEGORY= toString();
private final IPositionUpdater fUpdater= new GroovyExclusivePositionUpdater(CATEGORY);
private final Stack<GroovyBracketLevel> fBracketLevelStack= new Stack<GroovyBracketLevel>();
public void setCloseBracketsEnabled(boolean enabled) {
fCloseBrackets= enabled;
}
public void setCloseStringsEnabled(boolean enabled) {
fCloseStrings= enabled;
}
public void setCloseBracesEnabled(boolean enabled) {
fCloseBraces = 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") ||
identifier.startsWith("public") ||
identifier.startsWith("public") ||
identifier.startsWith("protected") ||
identifier.startsWith("private"));
}
private boolean isMultilineSelection() {
ISelection selection= getSelectionProvider().getSelection();
if (selection instanceof ITextSelection) {
ITextSelection ts= (ITextSelection) selection;
return ts.getStartLine() != ts.getEndLine();
}
return false;
}
/*
* @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 || isBlockSelectionModeEnabled() && isMultilineSelection())
return;
switch (event.character) {
case '(':
case '<':
case '[':
case '\'':
case '\"':
// GROOVY change. Allow autoclosing of curly braces in
// GStrings
case '{':
// GROOVy end change
break;
default:
return;
}
final ISourceViewer sourceViewer= getSourceViewer();
IDocument document= sourceViewer.getDocument();
final Point selection= sourceViewer.getSelectedRange();
int offset= selection.x;
final int length= selection.y;
try {
IRegion startLine= document.getLineInformationOfOffset(offset);
IRegion endLine= document.getLineInformationOfOffset(offset + length);
JavaHeuristicScanner scanner= new JavaHeuristicScanner(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 (!fCloseBrackets
|| nextToken == Symbols.TokenLPAREN
|| nextToken == Symbols.TokenIDENT
|| 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.TokenIDENT
|| next != null && next.length() > 1)
return;
break;
case '\'':
case '"':
// GROOVY change, allow quote closing when there are no parens
if (!fCloseStrings
|| nextToken == Symbols.TokenIDENT
// || prevToken == Symbols.TokenIDENT
|| next != null && next.length() > 1
// || previous != null && previous.length() > 1
)
// GROOVY end change
return;
break;
// GROOVY change, allow curly braces closing in GStrings
case '{':
if (!fCloseBraces || nextToken == Symbols.TokenIDENT || next != null && next.length() > 1)
return;
break;
// GROOVY end change
default:
return;
}
ITypedRegion partition= TextUtilities.getPartition(document, IJavaPartitions.JAVA_PARTITIONING, offset, true);
if (event.character != '{' && !IDocument.DEFAULT_CONTENT_TYPE.equals(partition.getType()) && // original
// GROOVY change autoclose triple quotes
!shouldCloseTripleQuotes(document, offset, partition, getPeerCharacter(event.character))) { // GROOVY
// change
return;
}
// GROOVY change check for autoclose curly braces
if (event.character == '{' && !shouldCloseCurly(document, offset, partition, getPeerCharacter(event.character))) {
return;
}
if (!validateEditorInputState())
return;
final char character= event.character;
final char closingCharacter= getPeerCharacter(character);
final StringBuffer buffer= new StringBuffer();
buffer.append(character);
buffer.append(closingCharacter);
// GROOVY special case: multiline strings
// Insert the closing of a triple quoted string whenever
// there is a "" or a """ before
int insertedLength = 1;
if (fCloseStrings && offset > 1) {
String start = document.get(offset-2, 2);
boolean doit = false;
if (event.character == closingCharacter) {
doit = start.equals(Character.toString(closingCharacter) + closingCharacter);
}
if (doit) {
buffer.append(closingCharacter);
insertedLength ++;
// now check for a preexisting third quote
insertedLength ++;
if (offset > 2 && document.getChar(offset-3) == closingCharacter) {
offset--;
} else {
// if no third quote already, must add another
buffer.append(closingCharacter);
}
}
}
// GROOVY end
document.replace(offset, length, buffer.toString());
GroovyBracketLevel level= new GroovyBracketLevel();
fBracketLevelStack.push(level);
LinkedPositionGroup group= new LinkedPositionGroup();
// group.addPosition(new LinkedPosition(document, offset + 1, 0, LinkedPositionGroup.NO_STOP));
group.addPosition(new LinkedPosition(document, offset + insertedLength, 0, LinkedPositionGroup.NO_STOP)); // GROOVY change
LinkedModeModel model= new LinkedModeModel();
model.addLinkingListener(this);
model.addGroup(group);
model.forceInstall();
// 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);
level.fSecondPosition= new Position(offset + insertedLength, 1); // GROOVY change
document.addPosition(CATEGORY, level.fFirstPosition);
document.addPosition(CATEGORY, level.fSecondPosition);
level.fUI= new EditorLinkedModeUI(model, sourceViewer);
level.fUI.setSimpleMode(true);
level.fUI.setExitPolicy(new GroovyExitPolicy(closingCharacter, getEscapeCharacter(closingCharacter), fBracketLevelStack));
// level.fUI.setExitPosition(sourceViewer, offset + 2, 0, Integer.MAX_VALUE);
level.fUI.setExitPosition(sourceViewer, offset + 1 + insertedLength, 0, Integer.MAX_VALUE); // GROOVY change
level.fUI.setCyclingMode(LinkedModeUI.CYCLE_NEVER);
level.fUI.enter();
IRegion newSelection= level.fUI.getSelectedRegion();
// sourceViewer.setSelectedRange(newSelection.getOffset(), newSelection.getLength());
sourceViewer.setSelectedRange(newSelection.getOffset() - insertedLength + 1, newSelection.getLength()); // GROOVY change
event.doit= false;
} catch (BadLocationException e) {
JavaPlugin.log(e);
} catch (BadPositionCategoryException e) {
JavaPlugin.log(e);
}
}
/**
* GROOVY change
*
* @param document
* @param offset
* @param partition
* @param peer
* @return true iff we should be closing a curly bracket. Only happens
* as part of a GString
* @throws BadLocationException
*/
private boolean shouldCloseCurly(IDocument document, int offset, ITypedRegion partition, char peer)
throws BadLocationException {
if (offset < 2 || !(IJavaPartitions.JAVA_STRING.equals(partition.getType()) ||
GroovyPartitionScanner.GROOVY_MULTILINE_STRINGS.equals(partition.getType()))) {
return false;
}
char maybeOpen = document.getChar(offset - 1);
if (maybeOpen != '$') {
return false;
}
char maybeNext = document.getChar(offset);
return Character.isWhitespace(maybeNext) || maybeNext == '\"' || maybeNext == '\'';
}
/**
* GROOVY change
*
* @param document
* @param offset
* @param partition
* @param quote
* @return true if we are at a position of triple quotes
* @throws BadLocationException
*/
private boolean shouldCloseTripleQuotes(IDocument document, int offset, ITypedRegion partition, char quote) throws BadLocationException {
if (offset < 3 || GroovyPartitionScanner.GROOVY_MULTILINE_STRINGS.equals(partition.getType())) {
return false;
}
String maybequotes = document.get(offset-3, 3);
return maybequotes.equals(Character.toString(quote)+quote+quote);
}
/*
* @see org.eclipse.jface.text.link.ILinkedModeListener#left(org.eclipse.jface.text.link.LinkedModeModel, int)
*/
public void left(LinkedModeModel environment, int flags) {
final GroovyBracketLevel 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,
"");
} catch (BadLocationException e) {
JavaPlugin.log(e);
}
}
if (fBracketLevelStack.size() == 0) {
document.removePositionUpdater(fUpdater);
try {
document.removePositionCategory(CATEGORY);
} catch (BadPositionCategoryException e) {
JavaPlugin.log(e);
}
}
}
});
}
}
public void suspend(LinkedModeModel environment) {
}
public void resume(LinkedModeModel environment, int flags) {
}
}
private GroovyImageDecorator decorator = new GroovyImageDecorator();
private GroovySemanticReconciler semanticReconciler;
private final GroovyBracketInserter groovyBracketInserter = new GroovyBracketInserter();
/* visible only for testing! */ public VerifyKeyListener getGroovyBracketInserter() {
return groovyBracketInserter;
}
public GroovyEditor() {
super();
setRulerContextMenuId("#GroovyCompilationUnitRulerContext");
setEditorContextMenuId("#GroovyCompilationUnitEditorContext");
}
@Override
protected void setPreferenceStore(IPreferenceStore store) {
super.setPreferenceStore(new ChainedPreferenceStore(
new IPreferenceStore[] {store, GroovyPlugin.getDefault().getPreferenceStore()}));
// now create a new configuration to overwrite the Java-centric one
setSourceViewerConfiguration(createJavaSourceViewerConfiguration());
}
public GroovyConfiguration getGroovyConfiguration() {
return (GroovyConfiguration) getSourceViewerConfiguration();
}
private void installGroovySemanticHighlighting() {
try {
fSemanticManager.uninstall();
semanticReconciler = new GroovySemanticReconciler();
semanticReconciler.install(this, (JavaSourceViewer) getSourceViewer());
ReflectionUtils.executePrivateMethod(CompilationUnitEditor.class, "addReconcileListener",
new Class[] {IJavaReconcilingListener.class}, this, new Object[] {semanticReconciler});
} catch (RuntimeException e) {
GroovyPlugin.getDefault().logError("GroovyEditor: failed to install semantic reconciler", e);
}
}
private void uninstallGroovySemanticHighlighting() {
if (semanticHighlightingInstalled()) {
try {
semanticReconciler.uninstall();
ReflectionUtils.executePrivateMethod(CompilationUnitEditor.class, "removeReconcileListener",
new Class[] {IJavaReconcilingListener.class}, this, new Object[] {semanticReconciler});
semanticReconciler = null;
} catch (RuntimeException e) {
GroovyPlugin.getDefault().logError("GroovyEditor: failed to uninstall semantic reconciler", e);
}
}
}
private boolean semanticHighlightingInstalled() {
return semanticReconciler != null;
}
@Override
public void dispose() {
super.dispose();
uninstallGroovySemanticHighlighting();
ISourceViewer sourceViewer = getSourceViewer();
if (sourceViewer instanceof ITextViewerExtension) {
((ITextViewerExtension) sourceViewer).removeVerifyKeyListener(groovyBracketInserter);
}
}
public int getCaretOffset() {
ISourceViewer viewer = getSourceViewer();
return viewer.getTextWidget().getCaretOffset();
}
@Override
public Image getTitleImage() {
Object element = getEditorInput().getAdapter(IFile.class);
if (element == null) {
// null if coming from a code repository such as cvs, git, or svn
element = getEditorInput().getName();
}
Image image = decorator.decorateImage(null, element);
// cannot return null GRECLIPSE-257
return image != null ? image : super.getTitleImage();
}
@Override
protected void setSelection(ISourceReference reference, boolean moveCursor) {
super.setSelection(reference, moveCursor);
// must override functionality because JavaEditor expects that there is a ';' at end of declaration
try {
if (reference instanceof IImportDeclaration && moveCursor) {
int offset;
int length;
ISourceRange range = reference.getSourceRange();
String content = reference.getSource();
if (content != null) {
int start = Math.max(content.indexOf("import") + 6, 7);
while (start < content.length() && content.charAt(start) == ' ')
start++;
int end = content.trim().length();
do {
end--;
} while (end >= 0 && (content.charAt(end) == ' ' || content.charAt(end) == ';'));
offset = range.getOffset() + start;
length = end - start + 1; // Note, original JDT code has 8 here
// just in case...
int docLength = ((IImportDeclaration) reference).getOpenable().getBuffer().getLength();
if (docLength < offset + length) {
offset = docLength;
}
} else {
// fallback
offset = range.getOffset() + 1;
length = range.getLength() - 2;
}
if (offset > -1 && length > 0) {
try {
getSourceViewer().getTextWidget().setRedraw(false);
getSourceViewer().revealRange(offset, length);
getSourceViewer().setSelectedRange(offset, length);
} finally {
getSourceViewer().getTextWidget().setRedraw(true);
}
markInNavigationHistory();
}
}
} catch (JavaModelException e) {
GroovyPlugin.getDefault().logError("Error selecting import statement", e);
}
}
private IFile getFile() {
IEditorInput input = getEditorInput();
if (input instanceof FileEditorInput) {
return ((FileEditorInput) input).getFile();
} else {
return null;
}
}
/**
* @return the {@link GroovyCompilationUnit} associated with this editor,
* or returns null if the input is not a {@link GroovyCompilationUnit}.
*/
public GroovyCompilationUnit getGroovyCompilationUnit() {
ITypeRoot root = super.getInputJavaElement();
if (root instanceof GroovyCompilationUnit) {
return (GroovyCompilationUnit) root;
} else {
return null;
}
}
public ModuleNode getModuleNode() {
GroovyCompilationUnit unit = getGroovyCompilationUnit();
if (unit != null) {
return unit.getModuleNode();
} else {
return null;
}
}
@Override @SuppressWarnings({"rawtypes", "unchecked"})
public Object getAdapter(Class required) {
if (IResource.class == required || IFile.class == required) {
return this.getFile();
}
if (GroovyCompilationUnit.class == required || ICompilationUnit.class == required || CompilationUnit.class == required) {
return super.getInputJavaElement();
}
if (ModuleNode.class == required) {
return this.getModuleNode();
}
// new variant test in e43 which addresses bug 391253 means groovy doesn't get an outline
// (it must fail the isCalledByOutline() test but I haven't investigated deeply)
if (IContentOutlinePage.class.equals(required)) {
if (fOutlinePage == null && getSourceViewer() != null)
fOutlinePage= createOutlinePage();
return fOutlinePage;
}
return super.getAdapter(required);
}
@Override
public void createPartControl(Composite parent) {
super.createPartControl(parent);
unsetJavaBreakpointUpdater();
installGroovySemanticHighlighting();
IPreferenceStore preferenceStore = getPreferenceStore();
groovyBracketInserter.setCloseBracesEnabled(preferenceStore.getBoolean(CLOSE_BRACES));
groovyBracketInserter.setCloseBracketsEnabled(preferenceStore.getBoolean(CLOSE_BRACKETS));
groovyBracketInserter.setCloseStringsEnabled(preferenceStore.getBoolean(CLOSE_STRINGS));
groovyBracketInserter.setCloseAngularBracketsEnabled(preferenceStore.getString(JavaCore.COMPILER_SOURCE).compareTo(JavaCore.VERSION_1_5) >= 0);
ISourceViewer sourceViewer = getSourceViewer();
if (sourceViewer instanceof ITextViewerExtension) {
((ITextViewerExtension) sourceViewer).prependVerifyKeyListener(groovyBracketInserter);
}
// ensure the bracket inserter from the superclass is disabled
disableBracketInserter();
}
private void disableBracketInserter() {
Object fBracketInserterField = ReflectionUtils.getPrivateField(CompilationUnitEditor.class, "fBracketInserter", this);
Class<?> fBracketInserterClass = fBracketInserterField.getClass();
Class<?>[] bool = {boolean.class};
Object[] disabled = {Boolean.FALSE};
ReflectionUtils.executePrivateMethod(fBracketInserterClass, "setCloseBracketsEnabled", bool, fBracketInserterField, disabled);
ReflectionUtils.executePrivateMethod(fBracketInserterClass, "setCloseStringsEnabled", bool, fBracketInserterField, disabled);
ReflectionUtils.executePrivateMethod(fBracketInserterClass, "setCloseStringsEnabled", bool, fBracketInserterField, disabled);
ReflectionUtils.executePrivateMethod(fBracketInserterClass, "setCloseAngularBracketsEnabled", bool, fBracketInserterField, disabled);
}
@Override
protected void doSetInput(IEditorInput input) throws CoreException {
final boolean installed = semanticHighlightingInstalled();
if (installed) {
uninstallGroovySemanticHighlighting();
}
super.doSetInput(input);
unsetJavaBreakpointUpdater();
if (installed) {
installGroovySemanticHighlighting();
}
}
@Override
public void doSave(IProgressMonitor progressMonitor) {
try {
super.doSave(progressMonitor);
} catch (RuntimeException e) {
GroovyPlugin.getDefault().logError("GroovyEditor: error saving document", e);
throw e;
} catch (Error e) {
GroovyPlugin.getDefault().logError("GroovyEditor: error saving document", e);
throw e;
}
}
@Override
protected void handleExceptionOnSave(CoreException exception, IProgressMonitor progressMonitor) {
GroovyPlugin.getDefault().logError("GroovyEditor: error saving document", exception);
super.handleExceptionOnSave(exception, progressMonitor);
}
@Override
protected void initializeKeyBindingScopes() {
setKeyBindingScopes(new String[] {"org.codehaus.groovy.eclipse.editor.groovyEditorScope"});
}
@Override
public JavaSourceViewerConfiguration createJavaSourceViewerConfiguration() {
GroovyTextTools textTools = GroovyPlugin.getDefault().getTextTools();
return new GroovyConfiguration(textTools.getColorManager(), getPreferenceStore(), this);
}
/**
* Ensures that the Java breakpoint updater is removed because we need to use
* Groovy's breakpoint updater instead.
*/
private void unsetJavaBreakpointUpdater() {
try {
ISourceViewer viewer = getSourceViewer();
if (viewer != null) {
IAnnotationModel model = viewer.getAnnotationModel();
if (model instanceof AbstractMarkerAnnotationModel) {
if (ReflectionUtils.getPrivateField(AbstractMarkerAnnotationModel.class, "fMarkerUpdaterSpecifications", model) == null) {
// force instantiation of the extension points
ReflectionUtils.executeNoArgPrivateMethod(AbstractMarkerAnnotationModel.class, "installMarkerUpdaters", model);
}
@SuppressWarnings("unchecked")
List<IConfigurationElement> updaterSpecs = (List<IConfigurationElement>)
ReflectionUtils.getPrivateField(AbstractMarkerAnnotationModel.class, "fMarkerUpdaterSpecifications", model);
// remove the marker updater for Java breakpoints; the Groovy one will be used instead
for (Iterator<IConfigurationElement> specIter = updaterSpecs.iterator(); specIter.hasNext();) {
IConfigurationElement spec = specIter.next();
if (spec.getAttribute("class").equals(BreakpointMarkerUpdater.class.getCanonicalName())) {
specIter.remove();
break;
}
}
}
}
} catch (RuntimeException e) {
GroovyPlugin.getDefault().logError("GroovyEditor: failed to remove Java breakpoint updater", e);
}
}
/** Preference key for automatically closing strings */
private static final String CLOSE_STRINGS = PreferenceConstants.EDITOR_CLOSE_STRINGS;
/** Preference key for automatically closing brackets and parenthesis */
private static final String CLOSE_BRACKETS = PreferenceConstants.EDITOR_CLOSE_BRACKETS;
/** Preference key for automatically closing curly braces */
private static final String CLOSE_BRACES = PreferenceConstants.EDITOR_CLOSE_BRACES;
@Override
protected void handlePreferenceStoreChanged(PropertyChangeEvent event) {
super.handlePreferenceStoreChanged(event);
ISourceViewer sv = getSourceViewer();
if (sv != null) {
String p = event.getProperty();
if (CLOSE_BRACKETS.equals(p)) {
groovyBracketInserter.setCloseBracketsEnabled(getPreferenceStore().getBoolean(p));
disableBracketInserter();
return;
}
if (CLOSE_STRINGS.equals(p)) {
groovyBracketInserter.setCloseStringsEnabled(getPreferenceStore().getBoolean(p));
disableBracketInserter();
return;
}
if (CLOSE_BRACES.equals(p)) {
groovyBracketInserter.setCloseBracesEnabled(getPreferenceStore().getBoolean(p));
disableBracketInserter();
return;
}
}
}
// copied to make accessible from super-class
private static char getEscapeCharacter(char character) {
switch (character) {
case '"':
case '\'':
return '\\';
default:
return 0;
}
}
// copied to make accessible from super-class
private static char getPeerCharacter(char character) {
switch (character) {
case '(':
return ')';
case ')':
return '(';
case '<':
return '>';
case '>':
return '<';
case '[':
return ']';
case ']':
return '[';
case '"':
return character;
case '\'':
return character;
// GROOVY change
case '{':
return '}';
// GROOVY change end
default:
throw new IllegalArgumentException();
}
}
/**
* outline management
*/
private GroovyOutlinePage page;
/**
* Gets the outline page for this editor only if the outline page is an
* augmented {@link GroovyOutlinePage}.
*
* Otherwise returns null
*
* @return the {@link GroovyOutlinePage} or null
*/
public GroovyOutlinePage getOutlinePage() {
if (page == null) {
IContentOutlinePage outlinePage = (IContentOutlinePage) getAdapter(IContentOutlinePage.class);
if (outlinePage instanceof GroovyOutlinePage) {
page = (GroovyOutlinePage) outlinePage;
}
}
return page;
}
@Override
protected void synchronizeOutlinePage(ISourceReference element, boolean checkIfOutlinePageActive) {
if (page != null) {
page.refresh();
}
super.synchronizeOutlinePage(element, checkIfOutlinePageActive);
}
@Override
protected ISourceReference computeHighlightRangeSourceReference() {
return page != null ? page.getOutlineElmenetAt(getCaretOffset()) : super.computeHighlightRangeSourceReference();
}
@Override
protected JavaOutlinePage createOutlinePage() {
OutlineExtenderRegistry outlineExtenderRegistry = GroovyPlugin.getDefault().getOutlineTools().getOutlineExtenderRegistry();
GroovyCompilationUnit unit = getGroovyCompilationUnit();
if (unit != null) {
try {
page = outlineExtenderRegistry.getGroovyOutlinePageForEditor(unit.getJavaProject().getProject(), fOutlinerContextMenuId, this);
} catch (CoreException e) {
GroovyPlugin.getDefault().logError("GroovyEditor: failed to create Outline page", e);
}
if (page != null) {
// don't call this since it will grab the GroovyCompilationUnit
// instead of the OCompilationUnit
// setOutlinePageInput(page, getEditorInput());
// FIXADE do we need to call
// page.getOutlineCompilationUnit().exists()?
page.setInput(page.getOutlineCompilationUnit());
return page;
}
}
return super.createOutlinePage();
}
//--------------------------------------------------------------------------
private Action toPropertyAction;
@Override
protected void createActions() {
super.createActions();
updateSourceActions();
updateRefactorActions();
// indent on tab action
setAction("IndentOnTab", new GroovyTabAction(this));
markAsSelectionDependentAction("IndentOnTab", true);
markAsStateDependentAction("IndentOnTab", true);
// selection history actions
ExpandSelectionAction selectionAction = new ExpandSelectionAction(this,
(SelectionHistory) ReflectionUtils.getPrivateField(JavaEditor.class, "fSelectionHistory", this));
selectionAction.setActionDefinitionId(IJavaEditorActionDefinitionIds.SELECT_ENCLOSING);
setAction(StructureSelectionAction.ENCLOSING, selectionAction);
setAction(StructureSelectionAction.PREVIOUS, null);
setAction(StructureSelectionAction.NEXT, null);
// surround with actions
// TODO: How can we avoid using full-qualified name in a string here?
ISurroundWithFactory surroundWithFactory = (ISurroundWithFactory) Platform.getAdapterManager()
.loadAdapter(this, "org.codehaus.groovy.eclipse.quickfix.templates.SurroundWithAdapterFactory");
if (surroundWithFactory != null) {
CompositeActionGroup compositActions = (CompositeActionGroup) ReflectionUtils.getPrivateField(CompilationUnitEditor.class, "fContextMenuGroup", this);
ActionGroup[] groups = (ActionGroup[]) ReflectionUtils.getPrivateField(CompositeActionGroup.class, "fGroups", compositActions);
boolean found = false;
ActionGroup surroundWithGroup = surroundWithFactory.createSurrundWithGroup(this, ITextEditorActionConstants.GROUP_EDIT);
for (int i = 0, n = groups.length; i < n; i += 1) {
if (groups[i] instanceof SurroundWithActionGroup) {
found = true;
groups[i] = surroundWithGroup;
break;
}
}
if (!found) {
GroovyPlugin.trace("Oops...surroundWithActionGroup not found in context menus");
}
found = false;
groups = (ActionGroup[]) ReflectionUtils.getPrivateField(CompositeActionGroup.class, "fGroups", fActionGroups);
for (int i = 0, n = groups.length; i < n; i += 1) {
if (groups[i] instanceof SurroundWithActionGroup) {
found = true;
groups[i] = surroundWithGroup;
break;
}
}
if (!found) {
GroovyPlugin.trace("Oops...surroundWithActionGroup not found");
}
} else {
GroovyPlugin.trace("Oops...surroundWithFactory not initialized");
}
// to property action
toPropertyAction = new Action("Replace Accessor call with Property read/write") {{
setActionDefinitionId("org.codehaus.groovy.eclipse.ui.convertToProperty");
}
@Override
public void run() {
if (!ActionUtil.isEditable(GroovyEditor.this))
return;
ISelection selection = getSelectionProvider().getSelection();
if (!(selection instanceof ITextSelection))
return;
GroovyCompilationUnit gcu = getGroovyCompilationUnit();
if (!ElementValidator.checkValidateEdit(gcu, getSite().getShell(), "Convert to Property"))
return;
try {
ModuleNodeInfo info = gcu.getModuleInfo(true);
if (info.isEmpty())
return;
org.codehaus.groovy.eclipse.codebrowsing.requestor.Region selectRegion =
new org.codehaus.groovy.eclipse.codebrowsing.requestor.Region(((ITextSelection) selection).getOffset(), ((ITextSelection) selection).getLength());
ASTNodeFinder nodeFinder = new ASTNodeFinder(selectRegion);
ASTNode node = nodeFinder.doVisit(info.module);
if (node instanceof ConstantExpression) {
IASTFragment fragment = new FindSurroundingNode(new org.codehaus.groovy.eclipse.codebrowsing.requestor.Region(node)).doVisitSurroundingNode(info.module);
if (fragment.kind() == ASTFragmentKind.METHOD_CALL) {
MethodCallExpression call = (MethodCallExpression) fragment.getAssociatedNode();
if (call != null && !call.isUsingGenerics() && call.getArguments() instanceof ArgumentListExpression) {
ArgumentListExpression args = (ArgumentListExpression) call.getArguments();
Matcher match; // check for accessor or mutator
if (args.getExpressions().isEmpty() && (match = compile("(?:get|is)(\\w+)").matcher(call.getMethodAsString())).matches()) {
int offset = node.getStart(),
length = (args.getEnd() + 1) - offset;
String propertyName = match.group(1);
// replace "getPropertyName()" with "propertyName"
gcu.applyTextEdit(new ReplaceEdit(offset, length, StringUtils.uncapitalize(propertyName)), null);
} else if (args.getExpressions().size() == 1 && (match = compile("set(\\w+)").matcher(call.getMethodAsString())).matches()) {
int offset = node.getStart(),
length = args.getStart() - offset;
String propertyName = match.group(1);
// replace "setPropertyName(value_expression)" or "setPropertyName value_expression"
// with "propertyName = value_expression" (check prefs for spaces around assignment)
MultiTextEdit edits = new MultiTextEdit();
Map<String, String> options = gcu.getJavaProject().getOptions(true);
StringBuilder replacement = new StringBuilder(StringUtils.uncapitalize(propertyName));
if (JavaCore.INSERT.equals(options.get(FORMATTER_INSERT_SPACE_BEFORE_ASSIGNMENT_OPERATOR)))
replacement.append(' ');
replacement.append('=');
if (JavaCore.INSERT.equals(options.get(FORMATTER_INSERT_SPACE_AFTER_ASSIGNMENT_OPERATOR)))
replacement.append(' ');
edits.addChild(new ReplaceEdit(offset, length, replacement.toString()));
if (gcu.getContents()[args.getEnd()] == ')') edits.addChild(new DeleteEdit(args.getEnd(), 1));
gcu.applyTextEdit(edits, null);
}
}
}
}
} catch (Exception e) {
GroovyPlugin.getDefault().logError("Failure in convert to property", e);
}
}
};
setAction(toPropertyAction.getActionDefinitionId(), toPropertyAction);
}
/** Modifies, replaces, or disables actions managed by the {@link GenerateActionGroup}. */
protected void updateSourceActions() {
GenerateActionGroup group = getGenerateActionGroup();
// see also GenerateActionGroup.setGlobalActionHandlers
//editor context menu > 'Source' submenu
// GROUP_COMMENT
// "ToggleComment" -- works
// "AddBlockComment" -- works
// "RemoveBlockComment" -- works
// fAddJavaDocStub: AddJavaDocStubAction -- works
// GROUP_EDIT
// "Indent" -- replace
// "Format" -- replace
// "QuickFormat" -- TODO
// GROUP_IMPORT
// fAddImport: AddImportOnSelectionAction -- replace
// fOrganizeImports: OrganizeImportsAction -- replace
// fSortMembers: MultiSortMembersAction -- works
// fCleanUp: AllCleanUpsAction -- disable
// GROUP_GENERATE
// fOverrideMethods: OverrideMethodsAction -- works
// fAddGetterSetter: AddGetterSetterAction -- works
// fAddDelegateMethods: AddDelegateMethodsAction -- works
// fHashCodeEquals GenerateHashCodeEqualsAction -- works
// fToString: GenerateToStringAction -- works
// fGenerateConstructorUsingFields: GenerateNewConstructorUsingFieldsAction -- works
// fAddUnimplementedConstructors: AddUnimplementedConstructorsAction -- works
// GROUP_CODE
// GROUP_EXTERNALIZE
// fExternalizeStrings: ExternalizeStringsAction -- TODO
//view context menu > 'Source' submenu
// GROUP_COMMENT
// fAddJavaDocStub: AddJavaDocStubAction -- works
// GROUP_EDIT
// fFormatAll: FormatAllAction -- replace
// GROUP_IMPORT
// fAddImport: AddImportOnSelectionAction -- replace
// fOrganizeImports: OrganizeImportsAction -- replace
// fSortMembers: MultiSortMembersAction -- works
// fCleanUp: AllCleanUpsAction -- disable
// GROUP_GENERATE
// fOverrideMethods: OverrideMethodsAction -- works
// fAddGetterSetter: AddGetterSetterAction -- works
// fAddDelegateMethods: AddDelegateMethodsAction -- works
// fHashCodeEquals GenerateHashCodeEqualsAction -- works
// fToString: GenerateToStringAction -- works
// fGenerateConstructorUsingFields: GenerateNewConstructorUsingFieldsAction -- works
// fAddUnimplementedConstructors: AddUnimplementedConstructorsAction -- works
// GROUP_CODE
// GROUP_EXTERNALIZE
// fExternalizeStrings: ExternalizeStringsAction -- TODO
// fFindNLSProblems: FindBrokenNLSKeysAction -- TODO
// replace Indent
IAction indentAction = new FormatGroovyAction(getEditorSite(), FormatKind.INDENT_ONLY);
indentAction.setActionDefinitionId(IJavaEditorActionDefinitionIds.INDENT);
setAction("Indent", indentAction);
PlatformUI.getWorkbench().getHelpSystem().setHelp(indentAction, IJavaHelpContextIds.INDENT_ACTION);
// replace Format
IAction formatAction = new FormatGroovyAction(getEditorSite(), FormatKind.FORMAT);
formatAction.setActionDefinitionId(IJavaEditorActionDefinitionIds.FORMAT);
setAction("Format", formatAction);
PlatformUI.getWorkbench().getHelpSystem().setHelp(formatAction, IJavaHelpContextIds.FORMAT_ACTION);
// replace Format All
IAction formatAllAction = new FormatAllGroovyAction(getEditorSite(), FormatKind.FORMAT);
// setActionDefinitionId?
// setAction?
ReflectionUtils.setPrivateField(GenerateActionGroup.class, "fFormatAll", group, formatAllAction);
// replace Add Import
IAction addImportOnSelectionAction = new AddImportOnSelectionAction(this);
addImportOnSelectionAction.setActionDefinitionId(IJavaEditorActionDefinitionIds.ADD_IMPORT);
setAction("AddImport", addImportOnSelectionAction);
ReflectionUtils.setPrivateField(GenerateActionGroup.class, "fAddImport", group, addImportOnSelectionAction);
// replace Organize Imports
IAction organizeGroovyImportsAction = new OrganizeGroovyImportsAction(this);
organizeGroovyImportsAction.setActionDefinitionId(IJavaEditorActionDefinitionIds.ORGANIZE_IMPORTS);
setAction("OrganizeImports", organizeGroovyImportsAction);
ReflectionUtils.setPrivateField(GenerateActionGroup.class, "fOrganizeImports", group, organizeGroovyImportsAction);
// disable Clean Ups...
AllCleanUpsAction acua = (AllCleanUpsAction) ReflectionUtils.getPrivateField(GenerateActionGroup.class, "fCleanUp", group);
acua.setEnabled(false);
}
/** Modifies, replaces, or disables actions managed by the {@link RefactorActionGroup}. */
protected void updateRefactorActions() {
// remove most refactorings since they are not yet really supported
removeRefactoringAction("fSelfEncapsulateField");
removeRefactoringAction("fMoveAction");
//removeRefactoringAction("fRenameAction");
removeRefactoringAction("fModifyParametersAction");
// fPullUpAction
// fPushDownAction
removeRefactoringAction("fIntroduceParameterAction");
removeRefactoringAction("fIntroduceParameterObjectAction");
removeRefactoringAction("fIntroduceFactoryAction");
removeRefactoringAction("fExtractMethodAction");
removeRefactoringAction("fExtractInterfaceAction");
removeRefactoringAction("fExtractClassAction");
removeRefactoringAction("fExtractSupertypeAction");
removeRefactoringAction("fExtractTempAction");
removeRefactoringAction("fExtractConstantAction");
removeRefactoringAction("fChangeTypeAction");
removeRefactoringAction("fConvertNestedToTopAction");
removeRefactoringAction("fInferTypeArgumentsAction");
removeRefactoringAction("fInlineAction");
// fConvertLocalToFieldAction
removeRefactoringAction("fConvertAnonymousToNestedAction");
removeRefactoringAction("fIntroduceIndirectionAction");
// fInlineAction
removeRefactoringAction("fUseSupertypeAction");
// use our Rename action instead
GroovyRenameAction renameAction = new GroovyRenameAction(this);
renameAction.setActionDefinitionId(IGroovyEditorActionDefinitionIds.GROOVY_RENAME_ACTION);
setAction("RenameElement", renameAction);
replaceRefactoringAction("fRenameAction", renameAction);
// use our Extract constant action instead
GroovyExtractConstantAction extractConstantAction = new GroovyExtractConstantAction(this);
extractConstantAction.setActionDefinitionId(IJavaEditorActionDefinitionIds.EXTRACT_CONSTANT);
setAction("ExtractConstant", extractConstantAction);
replaceRefactoringAction("fExtractConstantAction", extractConstantAction);
// use our Extract method action instead
GroovyExtractMethodAction extractMethodAction = new GroovyExtractMethodAction(this);
extractMethodAction.setActionDefinitionId(IJavaEditorActionDefinitionIds.EXTRACT_METHOD);
setAction("ExtractMethod", extractMethodAction);
replaceRefactoringAction("fExtractMethodAction", extractMethodAction);
// use our Extract local instead
GroovyExtractLocalAction extractLocalAction = new GroovyExtractLocalAction(this);
extractLocalAction.setActionDefinitionId(IJavaEditorActionDefinitionIds.EXTRACT_LOCAL_VARIABLE);
setAction("ExtractLocalVariable", extractLocalAction);
replaceRefactoringAction("fExtractTempAction", extractLocalAction);
// use our Convert local instead
GroovyConvertLocalToFieldAction convertLocalAction = new GroovyConvertLocalToFieldAction(this);
convertLocalAction.setActionDefinitionId(IJavaEditorActionDefinitionIds.PROMOTE_LOCAL_VARIABLE);
setAction("ConvertLocalToField", convertLocalAction);
replaceRefactoringAction("fConvertLocalToFieldAction", convertLocalAction);
}
protected final void removeRefactoringAction(String actionFieldName) {
replaceRefactoringAction(actionFieldName, null);
}
protected final void replaceRefactoringAction(String actionFieldName, SelectionDispatchAction newAction) {
RefactorActionGroup group = getRefactorActionGroup();
SelectionDispatchAction action = (SelectionDispatchAction) ReflectionUtils.getPrivateField(RefactorActionGroup.class, actionFieldName, group);
if (action != null) {
getSite().getSelectionProvider().removeSelectionChangedListener(action);
}
ReflectionUtils.setPrivateField(RefactorActionGroup.class, actionFieldName, group, newAction);
}
@Override
public void editorContextMenuAboutToShow(IMenuManager contextMenu) {
super.editorContextMenuAboutToShow(contextMenu);
// inject to property action into the source submenu
IMenuManager sourceMenu = contextMenu.findMenuUsingPath(GenerateActionGroup.MENU_ID);
if (sourceMenu != null) {
sourceMenu.appendToGroup(GenerateActionGroup.GROUP_CODE, toPropertyAction);
}
}
//--------------------------------------------------------------------------
/**
* Updates the occurrences annotations based on the current selection.
*
* @see GroovyOccurrencesFinder
*/
@Override
protected void updateOccurrenceAnnotations(ITextSelection selection, org.eclipse.jdt.core.dom.CompilationUnit astRoot) {
try {
if (fOccurrencesFinderJob_get() != null)
fOccurrencesFinderJob_get().cancel();
if (!fMarkOccurrenceAnnotations_get())
return;
if (astRoot == null || selection == null)
return;
IDocument document = getSourceViewer().getDocument();
if (document == null)
return;
boolean hasChanged = false;
if (document instanceof IDocumentExtension4) {
int offset = selection.getOffset();
long currentModificationStamp = ((IDocumentExtension4) document).getModificationStamp();
IRegion markOccurrenceTargetRegion = fMarkOccurrenceTargetRegion_get();
hasChanged = currentModificationStamp != fMarkOccurrenceModificationStamp_get();
if (markOccurrenceTargetRegion != null && !hasChanged) {
if (markOccurrenceTargetRegion.getOffset() <= offset &&
offset <= markOccurrenceTargetRegion.getOffset() + markOccurrenceTargetRegion.getLength())
return;
}
fMarkOccurrenceTargetRegion_set(findMarkOccurrencesRegion(document, offset));
fMarkOccurrenceModificationStamp_set(currentModificationStamp);
}
Object locations = GroovyOccurrencesFinder.findOccurrences(
astRoot, selection.getOffset(), selection.getLength());
if (locations == null || getLength(locations) == 0) {
if (!fStickyOccurrenceAnnotations_get())
removeOccurrenceAnnotations_call();
else if (hasChanged) // check consistency of current annotations
removeOccurrenceAnnotations_call();
return;
}
fOccurrencesFinderJob_new(document, locations, selection);
} catch (Exception e) {
GroovyPlugin.getDefault().logError("Failure in GroovyEditor.updateOccurrenceAnnotations", e);
}
}
protected IRegion findMarkOccurrencesRegion(IDocument document, int offset) {
IRegion word = JavaWordFinder.findWord(document, offset);
try {
if (word != null && word.getLength() > 1 && document.getChar(word.getOffset()) == '$') {
// this is likely a GString expresion without {}, eg: "$var"
word = new Region(word.getOffset() + 1, word.getLength() - 1);
}
} catch (BadLocationException e) {
// if this ever gets thrown, then a more interesting exception will be thrown later
}
return word;
}
protected Job fOccurrencesFinderJob_get() throws Exception {
return (Job) ReflectionUtils.throwableGetPrivateField(JavaEditor.class, "fOccurrencesFinderJob", this);
}
protected boolean fMarkOccurrenceAnnotations_get() throws Exception {
return (Boolean) ReflectionUtils.throwableGetPrivateField(JavaEditor.class, "fMarkOccurrenceAnnotations", this);
}
protected IRegion fMarkOccurrenceTargetRegion_get() throws Exception {
return (IRegion) ReflectionUtils.throwableGetPrivateField(JavaEditor.class, "fMarkOccurrenceTargetRegion", this);
}
protected void fMarkOccurrenceTargetRegion_set(IRegion r) throws Exception {
ReflectionUtils.setPrivateField(JavaEditor.class, "fMarkOccurrenceTargetRegion", this, r);
}
protected long fMarkOccurrenceModificationStamp_get() throws Exception {
return (Long) ReflectionUtils.throwableGetPrivateField(JavaEditor.class, "fMarkOccurrenceModificationStamp", this);
}
protected void fMarkOccurrenceModificationStamp_set(long s) throws Exception {
ReflectionUtils.setPrivateField(JavaEditor.class, "fMarkOccurrenceModificationStamp", this, s);
}
protected boolean fStickyOccurrenceAnnotations_get() throws Exception {
return (Boolean) ReflectionUtils.throwableGetPrivateField(JavaEditor.class, "fStickyOccurrenceAnnotations", this);
}
protected void removeOccurrenceAnnotations_call() throws Exception {
ReflectionUtils.throwableExecutePrivateMethod(JavaEditor.class, "removeOccurrenceAnnotations", new Class[0], this, new Object[0]);
}
@SuppressWarnings({"rawtypes", "unchecked"})
protected void fOccurrencesFinderJob_new(IDocument document, Object locations, ISelection selection) throws Exception {
//OccurrencesFinderJob ofj = new OccurrencesFinderJob(document, locations, selection);
java.lang.reflect.Constructor ctor = ReflectionUtils.getConstructor(
Class.forName("org.eclipse.jdt.internal.ui.javaeditor.JavaEditor$OccurrencesFinderJob"),
new Class[] {JavaEditor.class, IDocument.class, locations.getClass(), ISelection.class});
Job ofj = ReflectionUtils.invokeConstructor(ctor, new Object[] {this, document, locations, selection});
//fOccurrencesFinderJob = ofj;
ReflectionUtils.setPrivateField(JavaEditor.class, "fOccurrencesFinderJob", this, ofj);
//ofj.run(new NullProgressMonitor());
ReflectionUtils.throwableExecutePrivateMethod(ofj.getClass(), "run", new Class[] {IProgressMonitor.class}, ofj, new Object[] {new NullProgressMonitor()});
}
}