/*******************************************************************************
* Copyright (c) 2000, 2013 IBM Corporation 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.wst.jsdt.internal.ui.javaeditor;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.BadPositionCategoryException;
import org.eclipse.jface.text.DefaultLineTracker;
import org.eclipse.jface.text.DocumentCommand;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentExtension;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.IPositionUpdater;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextOperationTarget;
import org.eclipse.jface.text.ITextViewerExtension;
import org.eclipse.jface.text.ITextViewerExtension7;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.IWidgetTokenKeeper;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.TabsToSpacesConverter;
import org.eclipse.jface.text.TextUtilities;
import org.eclipse.jface.text.contentassist.ContentAssistant;
import org.eclipse.jface.text.contentassist.IContentAssistant;
import org.eclipse.jface.text.formatter.FormattingContextProperties;
import org.eclipse.jface.text.formatter.IFormattingContext;
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.IOverviewRuler;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.IVerticalRuler;
import org.eclipse.jface.text.source.SourceViewerConfiguration;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.custom.VerifyKeyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ActionContext;
import org.eclipse.ui.actions.ActionGroup;
import org.eclipse.ui.dialogs.PreferencesUtil;
import org.eclipse.ui.texteditor.ContentAssistAction;
import org.eclipse.ui.texteditor.IAbstractTextEditorHelpContextIds;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.IStatusField;
import org.eclipse.ui.texteditor.ITextEditorActionConstants;
import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
import org.eclipse.ui.texteditor.ResourceAction;
import org.eclipse.ui.texteditor.TextOperationAction;
import org.eclipse.ui.texteditor.link.EditorLinkedModeUI;
import org.eclipse.wst.jsdt.core.IJavaScriptElement;
import org.eclipse.wst.jsdt.core.IJavaScriptProject;
import org.eclipse.wst.jsdt.core.IJavaScriptUnit;
import org.eclipse.wst.jsdt.core.IMember;
import org.eclipse.wst.jsdt.core.ISourceRange;
import org.eclipse.wst.jsdt.core.ISourceReference;
import org.eclipse.wst.jsdt.core.JavaScriptCore;
import org.eclipse.wst.jsdt.core.JavaScriptModelException;
import org.eclipse.wst.jsdt.core.dom.JavaScriptUnit;
import org.eclipse.wst.jsdt.core.formatter.DefaultCodeFormatterConstants;
import org.eclipse.wst.jsdt.internal.corext.util.JavaModelUtil;
import org.eclipse.wst.jsdt.internal.ui.IJavaHelpContextIds;
import org.eclipse.wst.jsdt.internal.ui.IJavaStatusConstants;
import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin;
import org.eclipse.wst.jsdt.internal.ui.actions.AddBlockCommentAction;
import org.eclipse.wst.jsdt.internal.ui.actions.CompositeActionGroup;
import org.eclipse.wst.jsdt.internal.ui.actions.IndentAction;
import org.eclipse.wst.jsdt.internal.ui.actions.RemoveBlockCommentAction;
import org.eclipse.wst.jsdt.internal.ui.actions.SurroundWithActionGroup;
import org.eclipse.wst.jsdt.internal.ui.compare.LocalHistoryActionGroup;
import org.eclipse.wst.jsdt.internal.ui.text.ContentAssistPreference;
import org.eclipse.wst.jsdt.internal.ui.text.JavaHeuristicScanner;
import org.eclipse.wst.jsdt.internal.ui.text.SmartBackspaceManager;
import org.eclipse.wst.jsdt.internal.ui.text.Symbols;
import org.eclipse.wst.jsdt.internal.ui.text.comment.CommentFormattingContext;
import org.eclipse.wst.jsdt.internal.ui.text.correction.CorrectionCommandInstaller;
import org.eclipse.wst.jsdt.internal.ui.text.java.IJavaReconcilingListener;
import org.eclipse.wst.jsdt.ui.IWorkingCopyManager;
import org.eclipse.wst.jsdt.ui.JavaScriptUI;
import org.eclipse.wst.jsdt.ui.PreferenceConstants;
import org.eclipse.wst.jsdt.ui.actions.GenerateActionGroup;
import org.eclipse.wst.jsdt.ui.actions.IJavaEditorActionDefinitionIds;
import org.eclipse.wst.jsdt.ui.actions.RefactorActionGroup;
import org.eclipse.wst.jsdt.ui.text.IJavaScriptPartitions;
/**
* Java specific text editor.
*/
public class CompilationUnitEditor extends JavaEditor implements IJavaReconcilingListener {
private static final boolean CODE_ASSIST_DEBUG= "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.wst.jsdt.ui/debug/ResultCollector")); //$NON-NLS-1$//$NON-NLS-2$
/**
* Text operation code for requesting common prefix completion.
*/
public static final int CONTENTASSIST_COMPLETE_PREFIX= 60;
interface ITextConverter {
void customizeDocumentCommand(IDocument document, DocumentCommand command);
}
class AdaptedSourceViewer extends JavaSourceViewer {
public AdaptedSourceViewer(Composite parent, IVerticalRuler verticalRuler, IOverviewRuler overviewRuler, boolean showAnnotationsOverview, int styles, IPreferenceStore store) {
super(parent, verticalRuler, overviewRuler, showAnnotationsOverview, styles, store);
}
public IContentAssistant getContentAssistant() {
return fContentAssistant;
}
/*
* @see ITextOperationTarget#doOperation(int)
*/
public void doOperation(int operation) {
if (getTextWidget() == null)
return;
switch (operation) {
case CONTENTASSIST_PROPOSALS:
long time= CODE_ASSIST_DEBUG ? System.currentTimeMillis() : 0;
String msg= fContentAssistant.showPossibleCompletions();
if (CODE_ASSIST_DEBUG) {
long delta= System.currentTimeMillis() - time;
System.err.println("Code Assist (total): " + delta); //$NON-NLS-1$
}
setStatusLineErrorMessage(msg);
return;
case QUICK_ASSIST:
/*
* XXX: We can get rid of this once the SourceViewer has a way to update the status line
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=133787
*/
msg= fQuickAssistAssistant.showPossibleQuickAssists();
setStatusLineErrorMessage(msg);
return;
}
super.doOperation(operation);
}
/*
* @see IWidgetTokenOwner#requestWidgetToken(IWidgetTokenKeeper)
*/
public boolean requestWidgetToken(IWidgetTokenKeeper requester) {
if (PlatformUI.getWorkbench().getHelpSystem().isContextHelpDisplayed())
return false;
return super.requestWidgetToken(requester);
}
/*
* @see IWidgetTokenOwnerExtension#requestWidgetToken(IWidgetTokenKeeper, int)
*
*/
public boolean requestWidgetToken(IWidgetTokenKeeper requester, int priority) {
if (PlatformUI.getWorkbench().getHelpSystem().isContextHelpDisplayed())
return false;
return super.requestWidgetToken(requester, priority);
}
/*
* @see org.eclipse.jface.text.source.SourceViewer#createFormattingContext()
*
*/
public IFormattingContext createFormattingContext() {
IFormattingContext context= new CommentFormattingContext();
Map preferences;
IJavaScriptElement inputJavaElement= getInputJavaElement();
IJavaScriptProject javaProject= inputJavaElement != null ? inputJavaElement.getJavaScriptProject() : null;
if (javaProject == null)
preferences= new HashMap(JavaScriptCore.getOptions());
else
preferences= new HashMap(javaProject.getOptions(true));
context.setProperty(FormattingContextProperties.CONTEXT_PREFERENCES, preferences);
return context;
}
}
private class ExitPolicy implements IExitPolicy {
final char fExitCharacter;
final char fEscapeCharacter;
final Stack fStack;
final int fSize;
public ExitPolicy(char exitCharacter, char escapeCharacter, Stack stack) {
fExitCharacter= exitCharacter;
fEscapeCharacter= escapeCharacter;
fStack= stack;
fSize= fStack.size();
}
/*
* @see org.eclipse.wst.jsdt.internal.ui.text.link.LinkedPositionUI.ExitPolicy#doExit(org.eclipse.wst.jsdt.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) {
BracketLevel level= (BracketLevel) 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;
}
}
private static class BracketLevel {
int fOffset;
int fLength;
LinkedModeUI fUI;
Position fFirstPosition;
Position fSecondPosition;
}
/**
* Position updater that takes any changes at the borders of a position to not belong to the position.
*
*
*/
private static class ExclusivePositionUpdater 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 ExclusivePositionUpdater(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
}
}
/**
* Returns the position category.
*
* @return the position category
*/
public String getCategory() {
return fCategory;
}
}
private class BracketInserter implements VerifyKeyListener, ILinkedModeListener {
private boolean fCloseBrackets= true;
private boolean fCloseStrings= true;
private boolean fCloseAngularBrackets= true;
private final String CATEGORY= toString();
private IPositionUpdater fUpdater= new ExclusivePositionUpdater(CATEGORY);
private Stack fBracketLevelStack= new Stack();
public void setCloseBracketsEnabled(boolean enabled) {
fCloseBrackets= enabled;
}
public void setCloseStringsEnabled(boolean enabled) {
fCloseStrings= 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") //$NON-NLS-1$
|| identifier.startsWith("public") //$NON-NLS-1$
|| identifier.startsWith("public") //$NON-NLS-1$
|| identifier.startsWith("protected") //$NON-NLS-1$
|| identifier.startsWith("private")); //$NON-NLS-1$
}
/*
* @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)
return;
switch (event.character) {
case '(':
case '<':
case '[':
case '\'':
case '\"':
case '{':
break;
default:
return;
}
final ISourceViewer sourceViewer= getSourceViewer();
IDocument document= sourceViewer.getDocument();
final Point selection= sourceViewer.getSelectedRange();
final 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 '{':
case '[':
if (!fCloseBrackets
|| nextToken == Symbols.TokenIDENT
|| next != null && next.length() > 1)
return;
break;
case '\'':
case '"':
if (!fCloseStrings
|| nextToken == Symbols.TokenIDENT
|| prevToken == Symbols.TokenIDENT
|| next != null && next.length() > 1
|| previous != null && previous.length() > 1)
return;
break;
default:
return;
}
ITypedRegion partition= TextUtilities.getPartition(document, IJavaScriptPartitions.JAVA_PARTITIONING, offset, true);
if (!IDocument.DEFAULT_CONTENT_TYPE.equals(partition.getType()))
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);
document.replace(offset, length, buffer.toString());
BracketLevel level= new BracketLevel();
fBracketLevelStack.push(level);
LinkedPositionGroup group= new LinkedPositionGroup();
group.addPosition(new LinkedPosition(document, offset + 1, 0, LinkedPositionGroup.NO_STOP));
LinkedModeModel model= new LinkedModeModel();
model.addLinkingListener(this);
model.addGroup(group);
model.forceInstall();
level.fOffset= offset;
level.fLength= 2;
// 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);
document.addPosition(CATEGORY, level.fFirstPosition);
document.addPosition(CATEGORY, level.fSecondPosition);
level.fUI= new EditorLinkedModeUI(model, sourceViewer);
level.fUI.setSimpleMode(true);
level.fUI.setExitPolicy(new ExitPolicy(closingCharacter, getEscapeCharacter(closingCharacter), fBracketLevelStack));
level.fUI.setExitPosition(sourceViewer, offset + 2, 0, Integer.MAX_VALUE);
level.fUI.setCyclingMode(LinkedModeUI.CYCLE_NEVER);
level.fUI.enter();
IRegion newSelection= level.fUI.getSelectedRegion();
sourceViewer.setSelectedRange(newSelection.getOffset(), newSelection.getLength());
event.doit= false;
} catch (BadLocationException e) {
JavaScriptPlugin.log(e);
} catch (BadPositionCategoryException e) {
JavaScriptPlugin.log(e);
}
}
/*
* @see org.eclipse.jface.text.link.ILinkedModeListener#left(org.eclipse.jface.text.link.LinkedModeModel, int)
*/
public void left(LinkedModeModel environment, int flags) {
final BracketLevel level= (BracketLevel) 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,
""); //$NON-NLS-1$
} catch (BadLocationException e) {
JavaScriptPlugin.log(e);
}
}
if (fBracketLevelStack.size() == 0) {
document.removePositionUpdater(fUpdater);
try {
document.removePositionCategory(CATEGORY);
} catch (BadPositionCategoryException e) {
JavaScriptPlugin.log(e);
}
}
}
});
}
}
/*
* @see org.eclipse.jface.text.link.ILinkedModeListener#suspend(org.eclipse.jface.text.link.LinkedModeModel)
*/
public void suspend(LinkedModeModel environment) {
}
/*
* @see org.eclipse.jface.text.link.ILinkedModeListener#resume(org.eclipse.jface.text.link.LinkedModeModel, int)
*/
public void resume(LinkedModeModel environment, int flags) {
}
}
/**
* Remembers data related to the current selection to be able to
* restore it later.
*
*
*/
private class RememberedSelection {
/** The remembered selection start. */
private RememberedOffset fStartOffset= new RememberedOffset();
/** The remembered selection end. */
private RememberedOffset fEndOffset= new RememberedOffset();
/**
* Remember current selection.
*/
public void remember() {
/* https://bugs.eclipse.org/bugs/show_bug.cgi?id=52257
* This method may be called inside an asynchronous call posted
* to the UI thread, so protect against intermediate disposal
* of the editor.
*/
ISourceViewer viewer= getSourceViewer();
if (viewer != null) {
Point selection= viewer.getSelectedRange();
int startOffset= selection.x;
int endOffset= startOffset + selection.y;
fStartOffset.setOffset(startOffset);
fEndOffset.setOffset(endOffset);
}
}
/**
* Restore remembered selection.
*/
public void restore() {
/* https://bugs.eclipse.org/bugs/show_bug.cgi?id=52257
* This method may be called inside an asynchronous call posted
* to the UI thread, so protect against intermediate disposal
* of the editor.
*/
if (getSourceViewer() == null)
return;
try {
int startOffset, endOffset;
int revealStartOffset, revealEndOffset;
if (showsHighlightRangeOnly()) {
IJavaScriptElement newStartElement= fStartOffset.getElement();
startOffset= fStartOffset.getRememberedOffset(newStartElement);
revealStartOffset= fStartOffset.getRevealOffset(newStartElement, startOffset);
if (revealStartOffset == -1)
startOffset= -1;
IJavaScriptElement newEndElement= fEndOffset.getElement();
endOffset= fEndOffset.getRememberedOffset(newEndElement);
revealEndOffset= fEndOffset.getRevealOffset(newEndElement, endOffset);
if (revealEndOffset == -1)
endOffset= -1;
} else {
startOffset= fStartOffset.getOffset();
revealStartOffset= startOffset;
endOffset= fEndOffset.getOffset();
revealEndOffset= endOffset;
}
if (startOffset == -1) {
startOffset= endOffset; // fallback to caret offset
revealStartOffset= revealEndOffset;
}
if (endOffset == -1) {
endOffset= startOffset; // fallback to other offset
revealEndOffset= revealStartOffset;
}
IJavaScriptElement element;
if (endOffset == -1) {
// fallback to element selection
element= fEndOffset.getElement();
if (element == null)
element= fStartOffset.getElement();
if (element != null)
setSelection(element);
return;
}
if (isValidSelection(revealStartOffset, revealEndOffset - revealStartOffset) && isValidSelection(startOffset, endOffset - startOffset))
selectAndReveal(startOffset, endOffset - startOffset, revealStartOffset, revealEndOffset - revealStartOffset);
} finally {
fStartOffset.clear();
fEndOffset.clear();
}
}
private boolean isValidSelection(int offset, int length) {
IDocumentProvider provider= getDocumentProvider();
if (provider != null) {
IDocument document= provider.getDocument(getEditorInput());
if (document != null) {
int end= offset + length;
int documentLength= document.getLength();
return 0 <= offset && offset <= documentLength && 0 <= end && end <= documentLength && length >= 0;
}
}
return false;
}
}
/**
* Remembers additional data for a given
* offset to be able restore it later.
*
*
*/
private class RememberedOffset {
/** Remembered line for the given offset */
private int fLine;
/** Remembered column for the given offset*/
private int fColumn;
/** Remembered Java element for the given offset*/
private IJavaScriptElement fElement;
/** Remembered Java element line for the given offset*/
private int fElementLine;
/**
* Store visual properties of the given offset.
*
* @param offset Offset in the document
*/
public void setOffset(int offset) {
try {
IDocument document= getSourceViewer().getDocument();
fLine= document.getLineOfOffset(offset);
fColumn= offset - document.getLineOffset(fLine);
fElement= getElementAt(offset, true);
fElementLine= getElementLine(document, fElement);
} catch (BadLocationException e) {
// should not happen
JavaScriptPlugin.log(e);
clear();
} catch (JavaScriptModelException e) {
// should not happen
JavaScriptPlugin.log(e.getStatus());
clear();
}
}
/**
* Computes the element line of a java element (the start of the element, or the line with
* the element's name range).
*
* @param document the displayed document for line information
* @param element the java element, may be <code>null</code>
* @return the element's start line, or -1
* @throws BadLocationException
* @throws JavaScriptModelException
*
*/
private int getElementLine(IDocument document, IJavaScriptElement element) throws BadLocationException, JavaScriptModelException {
if (element instanceof IMember) {
ISourceRange range= ((IMember) element).getNameRange();
if (range != null)
return document.getLineOfOffset(range.getOffset());
}
int elementOffset= getOffset(element);
if (elementOffset != -1)
return document.getLineOfOffset(elementOffset);
return -1;
}
/**
* Return offset recomputed from stored visual properties.
*
* @return Offset in the document
*/
public int getOffset() {
IJavaScriptElement newElement= getElement();
int offset= getRememberedOffset(newElement);
if (offset == -1 || newElement != null && !containsOffset(newElement, offset) && (offset == 0 || !containsOffset(newElement, offset - 1)))
return -1;
return offset;
}
/**
* Return offset recomputed from stored visual properties.
*
* @param newElement Enclosing element
* @return Offset in the document
*/
public int getRememberedOffset(IJavaScriptElement newElement) {
try {
IDocument document= getSourceViewer().getDocument();
int newElementLine= getElementLine(document, newElement);
int newLine= fLine;
if (newElementLine != -1 && fElementLine != -1)
newLine += newElementLine - fElementLine;
if (newLine < 0 || newLine >= document.getNumberOfLines())
return -1;
int maxColumn= document.getLineLength(newLine);
String lineDelimiter= document.getLineDelimiter(newLine);
if (lineDelimiter != null)
maxColumn= maxColumn - lineDelimiter.length();
int offset;
if (fColumn > maxColumn)
offset= document.getLineOffset(newLine) + maxColumn;
else
offset= document.getLineOffset(newLine) + fColumn;
return offset;
} catch (BadLocationException e) {
// should not happen
JavaScriptPlugin.log(e);
return -1;
} catch (JavaScriptModelException e) {
// should not happen
JavaScriptPlugin.log(e.getStatus());
return -1;
}
}
/**
* Returns the offset used to reveal the given element based on the given selection offset.
* @param element the element
* @param offset the selection offset
* @return the offset to reveal the given element based on the given selection offset
*/
public int getRevealOffset(IJavaScriptElement element, int offset) {
if (element == null || offset == -1)
return -1;
if (containsOffset(element, offset)) {
if (offset > 0) {
IJavaScriptElement alternateElement= getElementAt(offset, false);
if (element.getHandleIdentifier().equals(alternateElement.getParent().getHandleIdentifier()))
return offset - 1; // Solves test case 2 from https://bugs.eclipse.org/bugs/show_bug.cgi?id=47727#c3
}
return offset;
} else if (offset > 0 && containsOffset(element, offset - 1))
return offset - 1; // Solves test case 1 from https://bugs.eclipse.org/bugs/show_bug.cgi?id=47727#c3
return -1;
}
/**
* Return Java element recomputed from stored visual properties.
*
* @return Java element
*/
public IJavaScriptElement getElement() {
if (fElement == null)
return null;
return findElement(fElement);
}
/**
* Clears the stored position
*/
public void clear() {
fLine= -1;
fColumn= -1;
fElement= null;
fElementLine= -1;
}
/**
* Does the given Java element contain the given offset?
* @param element Java element
* @param offset Offset
* @return <code>true</code> iff the Java element contains the offset
*/
private boolean containsOffset(IJavaScriptElement element, int offset) {
int elementOffset= getOffset(element);
int elementLength= getLength(element);
return (elementOffset > -1 && elementLength > -1) ? (offset >= elementOffset && offset < elementOffset + elementLength) : false;
}
/**
* Returns the offset of the given Java element.
*
* @param element Java element
* @return Offset of the given Java element
*/
private int getOffset(IJavaScriptElement element) {
if (element instanceof ISourceReference) {
ISourceReference sr= (ISourceReference) element;
try {
ISourceRange srcRange= sr.getSourceRange();
if (srcRange != null)
return srcRange.getOffset();
} catch (JavaScriptModelException e) {
}
}
return -1;
}
/**
* Returns the length of the given Java element.
*
* @param element Java element
* @return Length of the given Java element
*/
private int getLength(IJavaScriptElement element) {
if (element instanceof ISourceReference) {
ISourceReference sr= (ISourceReference) element;
try {
ISourceRange srcRange= sr.getSourceRange();
if (srcRange != null)
return srcRange.getLength();
} catch (JavaScriptModelException e) {
}
}
return -1;
}
/**
* Returns the updated java element for the old java element.
*
* @param element Old Java element
* @return Updated Java element
*/
private IJavaScriptElement findElement(IJavaScriptElement element) {
if (element == null)
return null;
IWorkingCopyManager manager= JavaScriptPlugin.getDefault().getWorkingCopyManager();
IJavaScriptUnit unit= manager.getWorkingCopy(getEditorInput());
if (unit != null) {
try {
JavaModelUtil.reconcile(unit);
IJavaScriptElement[] findings= unit.findElements(element);
if (findings != null && findings.length > 0)
return findings[0];
} catch (JavaScriptModelException x) {
JavaScriptPlugin.log(x.getStatus());
// nothing found, be tolerant and go on
}
}
return null;
}
}
/** Preference key for code formatter tab size */
private final static String CODE_FORMATTER_TAB_SIZE= DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE;
/** Preference key for inserting spaces rather than tabs */
private final static String SPACES_FOR_TABS= DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR;
/** Preference key for automatically closing strings */
private final static String CLOSE_STRINGS= PreferenceConstants.EDITOR_CLOSE_STRINGS;
/** Preference key for automatically closing brackets and parenthesis */
private final static String CLOSE_BRACKETS= PreferenceConstants.EDITOR_CLOSE_BRACKETS;
/** The editor's save policy */
protected ISavePolicy fSavePolicy;
/** Listener to annotation model changes that updates the error tick in the tab image */
private JavaEditorErrorTickUpdater fJavaEditorErrorTickUpdater;
/**
* The remembered selection.
*
*/
private RememberedSelection fRememberedSelection= new RememberedSelection();
/** The bracket inserter. */
private BracketInserter fBracketInserter= new BracketInserter();
/** The standard action groups added to the menu */
private GenerateActionGroup fGenerateActionGroup;
private CompositeActionGroup fContextMenuGroup;
private CorrectionCommandInstaller fCorrectionCommands;
/**
* Reconciling listeners.
*
*/
private ListenerList fReconcilingListeners= new ListenerList(ListenerList.IDENTITY);
/**
* Mutex for the reconciler. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=63898
* for a description of the problem.
* <p>
* XXX remove once the underlying problem (https://bugs.eclipse.org/bugs/show_bug.cgi?id=66176) is solved.
* </p>
*/
private final Object fReconcilerLock= new Object();
/**
* Creates a new compilation unit editor.
*/
public CompilationUnitEditor() {
super();
setDocumentProvider(JavaScriptPlugin.getDefault().getCompilationUnitDocumentProvider());
setEditorContextMenuId("#JavaScriptEditorContext"); //$NON-NLS-1$
setRulerContextMenuId("#JavaScriptRulerContext"); //$NON-NLS-1$
setOutlinerContextMenuId("#JavaScriptOutlinerContext"); //$NON-NLS-1$
// don't set help contextId, we install our own help context
fSavePolicy= null;
fJavaEditorErrorTickUpdater= new JavaEditorErrorTickUpdater(this);
fCorrectionCommands= null;
}
/*
* @see AbstractTextEditor#createActions()
*/
protected void createActions() {
super.createActions();
IAction action= new ContentAssistAction(JavaEditorMessages.getBundleForConstructedKeys(), "ContentAssistProposal.", this); //$NON-NLS-1$
action.setActionDefinitionId(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS);
setAction("ContentAssistProposal", action); //$NON-NLS-1$
markAsStateDependentAction("ContentAssistProposal", true); //$NON-NLS-1$
PlatformUI.getWorkbench().getHelpSystem().setHelp(action, IJavaHelpContextIds.CONTENT_ASSIST_ACTION);
action= new TextOperationAction(JavaEditorMessages.getBundleForConstructedKeys(), "ContentAssistContextInformation.", this, ISourceViewer.CONTENTASSIST_CONTEXT_INFORMATION); //$NON-NLS-1$
action.setActionDefinitionId(ITextEditorActionDefinitionIds.CONTENT_ASSIST_CONTEXT_INFORMATION);
setAction("ContentAssistContextInformation", action); //$NON-NLS-1$
markAsStateDependentAction("ContentAssistContextInformation", true); //$NON-NLS-1$
PlatformUI.getWorkbench().getHelpSystem().setHelp(action, IJavaHelpContextIds.PARAMETER_HINTS_ACTION);
action= new TextOperationAction(JavaEditorMessages.getBundleForConstructedKeys(), "Comment.", this, ITextOperationTarget.PREFIX); //$NON-NLS-1$
action.setActionDefinitionId(IJavaEditorActionDefinitionIds.COMMENT);
setAction("Comment", action); //$NON-NLS-1$
markAsStateDependentAction("Comment", true); //$NON-NLS-1$
PlatformUI.getWorkbench().getHelpSystem().setHelp(action, IJavaHelpContextIds.COMMENT_ACTION);
action= new TextOperationAction(JavaEditorMessages.getBundleForConstructedKeys(), "Uncomment.", this, ITextOperationTarget.STRIP_PREFIX); //$NON-NLS-1$
action.setActionDefinitionId(IJavaEditorActionDefinitionIds.UNCOMMENT);
setAction("Uncomment", action); //$NON-NLS-1$
markAsStateDependentAction("Uncomment", true); //$NON-NLS-1$
PlatformUI.getWorkbench().getHelpSystem().setHelp(action, IJavaHelpContextIds.UNCOMMENT_ACTION);
action= new ToggleCommentAction(JavaEditorMessages.getBundleForConstructedKeys(), "ToggleComment.", this); //$NON-NLS-1$
action.setActionDefinitionId(IJavaEditorActionDefinitionIds.TOGGLE_COMMENT);
setAction("ToggleComment", action); //$NON-NLS-1$
markAsStateDependentAction("ToggleComment", true); //$NON-NLS-1$
PlatformUI.getWorkbench().getHelpSystem().setHelp(action, IJavaHelpContextIds.TOGGLE_COMMENT_ACTION);
configureToggleCommentAction();
action= new TextOperationAction(JavaEditorMessages.getBundleForConstructedKeys(), "Format.", this, ISourceViewer.FORMAT); //$NON-NLS-1$
action.setActionDefinitionId(IJavaEditorActionDefinitionIds.FORMAT);
setAction("Format", action); //$NON-NLS-1$
markAsStateDependentAction("Format", true); //$NON-NLS-1$
markAsSelectionDependentAction("Format", true); //$NON-NLS-1$
PlatformUI.getWorkbench().getHelpSystem().setHelp(action, IJavaHelpContextIds.FORMAT_ACTION);
action= new AddBlockCommentAction(JavaEditorMessages.getBundleForConstructedKeys(), "AddBlockComment.", this); //$NON-NLS-1$
action.setActionDefinitionId(IJavaEditorActionDefinitionIds.ADD_BLOCK_COMMENT);
setAction("AddBlockComment", action); //$NON-NLS-1$
markAsStateDependentAction("AddBlockComment", true); //$NON-NLS-1$
markAsSelectionDependentAction("AddBlockComment", true); //$NON-NLS-1$
PlatformUI.getWorkbench().getHelpSystem().setHelp(action, IJavaHelpContextIds.ADD_BLOCK_COMMENT_ACTION);
action= new RemoveBlockCommentAction(JavaEditorMessages.getBundleForConstructedKeys(), "RemoveBlockComment.", this); //$NON-NLS-1$
action.setActionDefinitionId(IJavaEditorActionDefinitionIds.REMOVE_BLOCK_COMMENT);
setAction("RemoveBlockComment", action); //$NON-NLS-1$
markAsStateDependentAction("RemoveBlockComment", true); //$NON-NLS-1$
markAsSelectionDependentAction("RemoveBlockComment", true); //$NON-NLS-1$
PlatformUI.getWorkbench().getHelpSystem().setHelp(action, IJavaHelpContextIds.REMOVE_BLOCK_COMMENT_ACTION);
action= new IndentAction(JavaEditorMessages.getBundleForConstructedKeys(), "Indent.", this, false); //$NON-NLS-1$
action.setActionDefinitionId(IJavaEditorActionDefinitionIds.INDENT);
setAction("Indent", action); //$NON-NLS-1$
markAsStateDependentAction("Indent", true); //$NON-NLS-1$
markAsSelectionDependentAction("Indent", true); //$NON-NLS-1$
PlatformUI.getWorkbench().getHelpSystem().setHelp(action, IJavaHelpContextIds.INDENT_ACTION);
action= new IndentAction(JavaEditorMessages.getBundleForConstructedKeys(), "Indent.", this, true); //$NON-NLS-1$
setAction("IndentOnTab", action); //$NON-NLS-1$
markAsStateDependentAction("IndentOnTab", true); //$NON-NLS-1$
markAsSelectionDependentAction("IndentOnTab", true); //$NON-NLS-1$
// override the text editor actions with indenting move line actions
JavaMoveLinesAction[] moveLinesActions= JavaMoveLinesAction.createMoveCopyActionSet(JavaEditorMessages.getBundleForConstructedKeys(), this);
ResourceAction rAction= moveLinesActions[0];
rAction.setHelpContextId(IAbstractTextEditorHelpContextIds.MOVE_LINES_ACTION);
rAction.setActionDefinitionId(ITextEditorActionDefinitionIds.MOVE_LINES_UP);
setAction(ITextEditorActionConstants.MOVE_LINE_UP, rAction);
rAction= moveLinesActions[1];
rAction.setHelpContextId(IAbstractTextEditorHelpContextIds.MOVE_LINES_ACTION);
rAction.setActionDefinitionId(ITextEditorActionDefinitionIds.MOVE_LINES_DOWN);
setAction(ITextEditorActionConstants.MOVE_LINE_DOWN, rAction);
rAction= moveLinesActions[2];
rAction.setHelpContextId(IAbstractTextEditorHelpContextIds.COPY_LINES_ACTION);
rAction.setActionDefinitionId(ITextEditorActionDefinitionIds.COPY_LINES_UP);
setAction(ITextEditorActionConstants.COPY_LINE_UP, rAction);
rAction= moveLinesActions[3];
rAction.setHelpContextId(IAbstractTextEditorHelpContextIds.COPY_LINES_ACTION);
rAction.setActionDefinitionId(ITextEditorActionDefinitionIds.COPY_LINES_DOWN);
setAction(ITextEditorActionConstants.COPY_LINE_DOWN, rAction);
if (getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_SMART_TAB)) {
// don't replace Shift Right - have to make sure their enablement is mutually exclusive
// removeActionActivationCode(ITextEditorActionConstants.SHIFT_RIGHT);
setActionActivationCode("IndentOnTab", '\t', -1, SWT.NONE); //$NON-NLS-1$
}
fGenerateActionGroup= new GenerateActionGroup(this, ITextEditorActionConstants.GROUP_EDIT);
ActionGroup rg= new RefactorActionGroup(this, ITextEditorActionConstants.GROUP_EDIT, false);
ActionGroup surroundWith= new SurroundWithActionGroup(this, ITextEditorActionConstants.GROUP_EDIT);
fActionGroups.addGroup(surroundWith);
fActionGroups.addGroup(rg);
fActionGroups.addGroup(fGenerateActionGroup);
// We have to keep the context menu group separate to have better control over positioning
fContextMenuGroup= new CompositeActionGroup(new ActionGroup[] {
fGenerateActionGroup,
rg,
surroundWith,
new LocalHistoryActionGroup(this, ITextEditorActionConstants.GROUP_EDIT)});
fCorrectionCommands= new CorrectionCommandInstaller(); // allow shortcuts for quick fix/assist
fCorrectionCommands.registerCommands(this);
}
/*
* @see JavaEditor#getElementAt(int)
*/
protected IJavaScriptElement getElementAt(int offset) {
return getElementAt(offset, true);
}
/**
* Returns the most narrow element including the given offset. If <code>reconcile</code>
* is <code>true</code> the editor's input element is reconciled in advance. If it is
* <code>false</code> this method only returns a result if the editor's input element
* does not need to be reconciled.
*
* @param offset the offset included by the retrieved element
* @param reconcile <code>true</code> if working copy should be reconciled
* @return the most narrow element which includes the given offset
*/
protected IJavaScriptElement getElementAt(int offset, boolean reconcile) {
IJavaScriptUnit unit= (IJavaScriptUnit)getInputJavaElement();
if (unit != null) {
try {
if (reconcile) {
JavaModelUtil.reconcile(unit);
return unit.getElementAt(offset);
} else if (unit.isConsistent())
return unit.getElementAt(offset);
} catch (JavaScriptModelException x) {
if (!x.isDoesNotExist())
JavaScriptPlugin.log(x.getStatus());
// nothing found, be tolerant and go on
}
}
return null;
}
/*
* @see JavaEditor#getCorrespondingElement(IJavaScriptElement)
*/
protected IJavaScriptElement getCorrespondingElement(IJavaScriptElement element) {
// XXX: With new working copy story: original == working copy.
// Note that the previous code could result in a reconcile as side effect. Should check if that
// is still required.
return element;
}
/*
* @see AbstractTextEditor#editorContextMenuAboutToShow(IMenuManager)
*/
public void editorContextMenuAboutToShow(IMenuManager menu) {
super.editorContextMenuAboutToShow(menu);
ActionContext context= new ActionContext(getSelectionProvider().getSelection());
fContextMenuGroup.setContext(context);
fContextMenuGroup.fillContextMenu(menu);
fContextMenuGroup.setContext(null);
}
/*
* @see org.eclipse.ui.texteditor.AbstractTextEditor#performSave(boolean, org.eclipse.core.runtime.IProgressMonitor)
*/
protected void performSave(boolean overwrite, IProgressMonitor progressMonitor) {
IDocumentProvider p= getDocumentProvider();
if (p instanceof ICompilationUnitDocumentProvider) {
ICompilationUnitDocumentProvider cp= (ICompilationUnitDocumentProvider) p;
cp.setSavePolicy(fSavePolicy);
}
try {
super.performSave(overwrite, progressMonitor);
} finally {
if (p instanceof ICompilationUnitDocumentProvider) {
ICompilationUnitDocumentProvider cp= (ICompilationUnitDocumentProvider) p;
cp.setSavePolicy(null);
}
}
}
/*
* @see AbstractTextEditor#doSave(IProgressMonitor)
*/
public void doSave(IProgressMonitor progressMonitor) {
IDocumentProvider p= getDocumentProvider();
if (p == null) {
// editor has been closed
return;
}
if (p.isDeleted(getEditorInput())) {
if (isSaveAsAllowed()) {
/*
* 1GEUSSR: ITPUI:ALL - User should never loose changes made in the editors.
* Changed Behavior to make sure that if called inside a regular save (because
* of deletion of input element) there is a way to report back to the caller.
*/
performSaveAs(progressMonitor);
} else {
/*
* 1GF5YOX: ITPJUI:ALL - Save of delete file claims it's still there
* Missing resources.
*/
Shell shell= getSite().getShell();
MessageDialog.openError(shell, JavaEditorMessages.CompilationUnitEditor_error_saving_title1, JavaEditorMessages.CompilationUnitEditor_error_saving_message1);
}
} else {
setStatusLineErrorMessage(null);
updateState(getEditorInput());
validateState(getEditorInput());
IWorkingCopyManager manager= JavaScriptPlugin.getDefault().getWorkingCopyManager();
IJavaScriptUnit unit= manager.getWorkingCopy(getEditorInput());
if (unit != null) {
synchronized (unit) {
performSave(false, progressMonitor);
}
} else
performSave(false, progressMonitor);
}
}
/*
* @see org.eclipse.ui.texteditor.AbstractTextEditor#openSaveErrorDialog(java.lang.String, java.lang.String, org.eclipse.core.runtime.CoreException)
*
*/
protected void openSaveErrorDialog(String title, String message, CoreException exception) {
IStatus status= exception.getStatus();
if (JavaScriptUI.ID_PLUGIN.equals(status.getPlugin()) && status.getCode() == IJavaStatusConstants.EDITOR_POST_SAVE_NOTIFICATION) {
int mask= IStatus.OK | IStatus.INFO | IStatus.WARNING | IStatus.ERROR;
ErrorDialog dialog = new ErrorDialog(getSite().getShell(), title, message, status, mask) {
protected Control createDialogArea(Composite parent) {
parent= (Composite)super.createDialogArea(parent);
Link link= new Link(parent, SWT.NONE);
link.setText(JavaEditorMessages.CompilationUnitEditor_error_saving_saveParticipant);
link.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
PreferencesUtil.createPreferenceDialogOn(getShell(), "org.eclipse.wst.jsdt.ui.preferences.SaveParticipantPreferencePage", null, null).open(); //$NON-NLS-1$
}
});
GridData gridData= new GridData(SWT.FILL, SWT.BEGINNING, true, false);
link.setLayoutData(gridData);
return parent;
}
};
dialog.open();
} else
super.openSaveErrorDialog(title, message, exception);
}
public boolean isSaveAsAllowed() {
return true;
}
/*
* @see AbstractTextEditor#doSetInput(IEditorInput)
*/
protected void doSetInput(IEditorInput input) throws CoreException {
super.doSetInput(input);
configureToggleCommentAction();
if (fJavaEditorErrorTickUpdater != null)
fJavaEditorErrorTickUpdater.updateEditorImage(getInputJavaElement());
}
/*
* @see org.eclipse.wst.jsdt.internal.ui.javaeditor.JavaEditor#installOverrideIndicator(boolean)
*
*/
protected void installOverrideIndicator(boolean provideAST) {
super.installOverrideIndicator(provideAST);
if (fOverrideIndicatorManager == null)
return;
addReconcileListener(fOverrideIndicatorManager);
}
/*
* @see org.eclipse.wst.jsdt.internal.ui.javaeditor.JavaEditor#uninstallOverrideIndicator()
*
*/
protected void uninstallOverrideIndicator() {
if (fOverrideIndicatorManager != null)
removeReconcileListener(fOverrideIndicatorManager);
super.uninstallOverrideIndicator();
}
/**
* Configures the toggle comment action
*
*
*/
private void configureToggleCommentAction() {
IAction action= getAction("ToggleComment"); //$NON-NLS-1$
if (action instanceof ToggleCommentAction) {
ISourceViewer sourceViewer= getSourceViewer();
SourceViewerConfiguration configuration= getSourceViewerConfiguration();
((ToggleCommentAction)action).configure(sourceViewer, configuration);
}
}
/*
* @see org.eclipse.ui.texteditor.AbstractTextEditor#installTabsToSpacesConverter()
*
*/
protected void installTabsToSpacesConverter() {
ISourceViewer sourceViewer= getSourceViewer();
SourceViewerConfiguration config= getSourceViewerConfiguration();
if (config != null && sourceViewer instanceof ITextViewerExtension7) {
int tabWidth= config.getTabWidth(sourceViewer);
TabsToSpacesConverter tabToSpacesConverter= new TabsToSpacesConverter();
tabToSpacesConverter.setNumberOfSpacesPerTab(tabWidth);
IDocumentProvider provider= getDocumentProvider();
if (provider instanceof ICompilationUnitDocumentProvider) {
ICompilationUnitDocumentProvider cup= (ICompilationUnitDocumentProvider) provider;
tabToSpacesConverter.setLineTracker(cup.createLineTracker(getEditorInput()));
} else
tabToSpacesConverter.setLineTracker(new DefaultLineTracker());
((ITextViewerExtension7)sourceViewer).setTabsToSpacesConverter(tabToSpacesConverter);
updateIndentPrefixes();
}
}
/*
* @see org.eclipse.ui.texteditor.AbstractDecoratedTextEditor#isTabsToSpacesConversionEnabled()
*
*/
protected boolean isTabsToSpacesConversionEnabled() {
IJavaScriptElement element= getInputJavaElement();
IJavaScriptProject project= element == null ? null : element.getJavaScriptProject();
String option;
if (project == null)
option= JavaScriptCore.getOption(SPACES_FOR_TABS);
else
option= project.getOption(SPACES_FOR_TABS, true);
return JavaScriptCore.SPACE.equals(option);
}
public void dispose() {
ISourceViewer sourceViewer= getSourceViewer();
if (sourceViewer instanceof ITextViewerExtension)
((ITextViewerExtension) sourceViewer).removeVerifyKeyListener(fBracketInserter);
if (fJavaEditorErrorTickUpdater != null) {
fJavaEditorErrorTickUpdater.dispose();
fJavaEditorErrorTickUpdater= null;
}
if (fCorrectionCommands != null) {
fCorrectionCommands.deregisterCommands();
fCorrectionCommands= null;
}
super.dispose();
}
/*
* @see AbstractTextEditor#createPartControl(Composite)
*/
public void createPartControl(Composite parent) {
super.createPartControl(parent);
IPreferenceStore preferenceStore= getPreferenceStore();
boolean closeBrackets= preferenceStore.getBoolean(CLOSE_BRACKETS);
boolean closeStrings= preferenceStore.getBoolean(CLOSE_STRINGS);
boolean closeAngularBrackets= JavaScriptCore.VERSION_1_5.compareTo(preferenceStore.getString(JavaScriptCore.COMPILER_SOURCE)) <= 0;
fBracketInserter.setCloseBracketsEnabled(closeBrackets);
fBracketInserter.setCloseStringsEnabled(closeStrings);
fBracketInserter.setCloseAngularBracketsEnabled(closeAngularBrackets);
ISourceViewer sourceViewer= getSourceViewer();
if (sourceViewer instanceof ITextViewerExtension)
((ITextViewerExtension) sourceViewer).prependVerifyKeyListener(fBracketInserter);
if (isMarkingOccurrences())
installOccurrencesFinder(false);
}
private static char getEscapeCharacter(char character) {
switch (character) {
case '"':
case '\'':
return '\\';
default:
return 0;
}
}
private static char getPeerCharacter(char character) {
switch (character) {
case '(':
return ')';
case ')':
return '(';
case '<':
return '>';
case '>':
return '<';
case '[':
return ']';
case ']':
return '[';
case '{':
return '}';
case '}':
return '{';
case '"':
return character;
case '\'':
return character;
default:
throw new IllegalArgumentException();
}
}
/*
* @see AbstractTextEditor#handlePreferenceStoreChanged(PropertyChangeEvent)
*/
protected void handlePreferenceStoreChanged(PropertyChangeEvent event) {
try {
AdaptedSourceViewer asv= (AdaptedSourceViewer) getSourceViewer();
if (asv != null) {
String p= event.getProperty();
if (CLOSE_BRACKETS.equals(p)) {
fBracketInserter.setCloseBracketsEnabled(getPreferenceStore().getBoolean(p));
return;
}
if (CLOSE_STRINGS.equals(p)) {
fBracketInserter.setCloseStringsEnabled(getPreferenceStore().getBoolean(p));
return;
}
if (JavaScriptCore.COMPILER_SOURCE.equals(p)) {
boolean closeAngularBrackets= JavaScriptCore.VERSION_1_5.compareTo(getPreferenceStore().getString(p)) <= 0;
fBracketInserter.setCloseAngularBracketsEnabled(closeAngularBrackets);
}
if (SPACES_FOR_TABS.equals(p)) {
if (isTabsToSpacesConversionEnabled())
installTabsToSpacesConverter();
else
uninstallTabsToSpacesConverter();
return;
}
if (PreferenceConstants.EDITOR_SMART_TAB.equals(p)) {
if (getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_SMART_TAB)) {
setActionActivationCode("IndentOnTab", '\t', -1, SWT.NONE); //$NON-NLS-1$
} else {
removeActionActivationCode("IndentOnTab"); //$NON-NLS-1$
}
}
IContentAssistant c= asv.getContentAssistant();
if (c instanceof ContentAssistant)
ContentAssistPreference.changeConfiguration((ContentAssistant) c, getPreferenceStore(), event);
if (CODE_FORMATTER_TAB_SIZE.equals(p) && isTabsToSpacesConversionEnabled()) {
uninstallTabsToSpacesConverter();
installTabsToSpacesConverter();
}
}
} finally {
super.handlePreferenceStoreChanged(event);
}
}
/*
* @see org.eclipse.wst.jsdt.internal.ui.javaeditor.JavaEditor#createJavaSourceViewer(org.eclipse.swt.widgets.Composite, org.eclipse.jface.text.source.IVerticalRuler, org.eclipse.jface.text.source.IOverviewRuler, boolean, int)
*/
protected ISourceViewer createJavaSourceViewer(Composite parent, IVerticalRuler verticalRuler, IOverviewRuler overviewRuler, boolean isOverviewRulerVisible, int styles, IPreferenceStore store) {
return new AdaptedSourceViewer(parent, verticalRuler, overviewRuler, isOverviewRulerVisible, styles, store);
}
/*
* @see org.eclipse.wst.jsdt.internal.ui.text.java.IJavaReconcilingListener#aboutToBeReconciled()
*
*/
public void aboutToBeReconciled() {
// Notify AST provider
JavaScriptPlugin.getDefault().getASTProvider().aboutToBeReconciled(getInputJavaElement());
// Notify listeners
Object[] listeners = fReconcilingListeners.getListeners();
for (int i = 0, length= listeners.length; i < length; ++i)
((IJavaReconcilingListener)listeners[i]).aboutToBeReconciled();
}
/*
* @see org.eclipse.wst.jsdt.internal.ui.text.java.IJavaReconcilingListener#reconciled(JavaScriptUnit, boolean, IProgressMonitor)
*
*/
public void reconciled(JavaScriptUnit ast, boolean forced, IProgressMonitor progressMonitor) {
// see: https://bugs.eclipse.org/bugs/show_bug.cgi?id=58245
JavaScriptPlugin javaPlugin= JavaScriptPlugin.getDefault();
if (javaPlugin == null)
return;
// Always notify AST provider
javaPlugin.getASTProvider().reconciled(ast, getInputJavaElement(), progressMonitor);
// Notify listeners
Object[] listeners = fReconcilingListeners.getListeners();
for (int i = 0, length= listeners.length; i < length; ++i)
((IJavaReconcilingListener)listeners[i]).reconciled(ast, forced, progressMonitor);
// Update Java Outline page selection
if (!forced && !progressMonitor.isCanceled()) {
Shell shell= getSite().getShell();
if (shell != null && !shell.isDisposed()) {
shell.getDisplay().asyncExec(new Runnable() {
public void run() {
selectionChanged();
}
});
}
}
}
/**
* Tells whether this is the active editor in the active page.
*
* @return <code>true</code> if this is the active editor in the active page
* @see IWorkbenchPage#getActiveEditor
*/
protected final boolean isActiveEditor() {
IWorkbenchWindow window= getSite().getWorkbenchWindow();
IWorkbenchPage page= window.getActivePage();
if (page == null)
return false;
IEditorPart activeEditor= page.getActiveEditor();
return activeEditor != null && activeEditor.equals(this);
}
/**
* Adds the given listener.
* Has no effect if an identical listener was not already registered.
*
* @param listener The reconcile listener to be added
*
*/
final void addReconcileListener(IJavaReconcilingListener listener) {
synchronized (fReconcilingListeners) {
fReconcilingListeners.add(listener);
}
}
/**
* Removes the given listener.
* Has no effect if an identical listener was not already registered.
*
* @param listener the reconcile listener to be removed
*
*/
final void removeReconcileListener(IJavaReconcilingListener listener) {
synchronized (fReconcilingListeners) {
fReconcilingListeners.remove(listener);
}
}
protected void updateStateDependentActions() {
super.updateStateDependentActions();
fGenerateActionGroup.editorStateChanged();
}
/*
* @see AbstractTextEditor#rememberSelection()
*/
protected void rememberSelection() {
fRememberedSelection.remember();
}
/*
* @see AbstractTextEditor#restoreSelection()
*/
protected void restoreSelection() {
fRememberedSelection.restore();
}
/*
* @see AbstractTextEditor#canHandleMove(IEditorInput, IEditorInput)
*/
protected boolean canHandleMove(IEditorInput originalElement, IEditorInput movedElement) {
String oldExtension= ""; //$NON-NLS-1$
if (originalElement instanceof IFileEditorInput) {
IFile file= ((IFileEditorInput) originalElement).getFile();
if (file != null) {
String ext= file.getFileExtension();
if (ext != null)
oldExtension= ext;
}
}
String newExtension= ""; //$NON-NLS-1$
if (movedElement instanceof IFileEditorInput) {
IFile file= ((IFileEditorInput) movedElement).getFile();
if (file != null)
newExtension= file.getFileExtension();
}
return oldExtension.equals(newExtension);
}
/*
* @see org.eclipse.wst.jsdt.internal.ui.javaeditor.JavaEditor#getAdapter(java.lang.Class)
*/
public Object getAdapter(Class required) {
if (SmartBackspaceManager.class.equals(required)) {
if (getSourceViewer() instanceof JavaSourceViewer) {
return ((JavaSourceViewer) getSourceViewer()).getBackspaceManager();
}
}
return super.getAdapter(required);
}
/**
* Returns the mutex for the reconciler. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=63898
* for a description of the problem.
* <p>
* XXX remove once the underlying problem (https://bugs.eclipse.org/bugs/show_bug.cgi?id=66176) is solved.
* </p>
* @return the lock reconcilers may use to synchronize on
*/
public Object getReconcilerLock() {
return fReconcilerLock;
}
/*
* @see org.eclipse.wst.jsdt.internal.ui.javaeditor.JavaEditor#createNavigationActions()
*/
protected void createNavigationActions() {
super.createNavigationActions();
final StyledText textWidget= getSourceViewer().getTextWidget();
IAction action= new DeletePreviousSubWordAction();
action.setActionDefinitionId(ITextEditorActionDefinitionIds.DELETE_PREVIOUS_WORD);
setAction(ITextEditorActionDefinitionIds.DELETE_PREVIOUS_WORD, action);
textWidget.setKeyBinding(SWT.CTRL | SWT.BS, SWT.NULL);
markAsStateDependentAction(ITextEditorActionDefinitionIds.DELETE_PREVIOUS_WORD, true);
action= new DeleteNextSubWordAction();
action.setActionDefinitionId(ITextEditorActionDefinitionIds.DELETE_NEXT_WORD);
setAction(ITextEditorActionDefinitionIds.DELETE_NEXT_WORD, action);
textWidget.setKeyBinding(SWT.CTRL | SWT.DEL, SWT.NULL);
markAsStateDependentAction(ITextEditorActionDefinitionIds.DELETE_NEXT_WORD, true);
}
protected void updateStatusField(String category) {
super.updateStatusField(category);
if (ITextEditorActionConstants.STATUS_CATEGORY_INPUT_POSITION.equals(category)) {
IStatusField field = getStatusField(IJavaEditorActionConstants.STATUS_CATEGORY_OFFSET);
if (field != null) {
ISourceViewer sourceViewer = getSourceViewer();
Point selection = sourceViewer.getTextWidget().getSelection();
int offset1 = widgetOffset2ModelOffset(sourceViewer, selection.x);
int offset2 = widgetOffset2ModelOffset(sourceViewer, selection.y);
String text = null;
if (offset1 != offset2)
text = "[" + offset1 + "-" + offset2 + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
else
text = "[ " + offset1 + " ]"; //$NON-NLS-1$ //$NON-NLS-2$
field.setText(text);
}
}
}
}