/*
* Copyright (c) 2012, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.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.eclipse.org/legal/epl-v10.html
*
* 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 com.google.dart.tools.ui.internal.text.editor;
import com.google.dart.tools.core.DartCore;
import com.google.dart.tools.core.DartCoreDebug;
import com.google.dart.tools.core.formatter.DefaultCodeFormatterConstants;
import com.google.dart.tools.internal.corext.refactoring.util.ExecutionUtils;
import com.google.dart.tools.internal.corext.refactoring.util.ReflectionUtils;
import com.google.dart.tools.internal.corext.refactoring.util.RunnableEx;
import com.google.dart.tools.ui.DartToolsPlugin;
import com.google.dart.tools.ui.DartUI;
import com.google.dart.tools.ui.DartX;
import com.google.dart.tools.ui.PreferenceConstants;
import com.google.dart.tools.ui.actions.DartEditorActionDefinitionIds;
import com.google.dart.tools.ui.actions.GenerateActionGroup;
import com.google.dart.tools.ui.actions.RefactorActionGroup_NEW;
import com.google.dart.tools.ui.actions.RefactorActionGroup_OLD;
import com.google.dart.tools.ui.internal.actions.NewSelectionConverter;
import com.google.dart.tools.ui.internal.text.DartHelpContextIds;
import com.google.dart.tools.ui.internal.text.DartStatusConstants;
import com.google.dart.tools.ui.internal.text.comment.CommentFormattingContext;
import com.google.dart.tools.ui.internal.text.dart.DartReconcilingStrategy;
import com.google.dart.tools.ui.internal.text.dart.IDartReconcilingListener;
import com.google.dart.tools.ui.internal.text.dart.IDartReconcilingListener_OLD;
import com.google.dart.tools.ui.internal.text.functions.ContentAssistPreference;
import com.google.dart.tools.ui.internal.text.functions.DartHeuristicScanner;
import com.google.dart.tools.ui.internal.text.functions.SmartBackspaceManager;
import com.google.dart.tools.ui.internal.text.functions.Symbols;
import com.google.dart.tools.ui.text.DartPartitions;
import com.google.dart.tools.ui.text.editor.tmp.JavaScriptCore;
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.AbstractInformationControlManager;
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.ITextSelection;
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.text.source.projection.ProjectionViewer;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.custom.VerifyKeyListener;
import org.eclipse.swt.events.DisposeListener;
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.ide.FileStoreEditorInput;
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 java.util.HashMap;
import java.util.Map;
import java.util.Stack;
/**
* Dart code editor.
*/
public class CompilationUnitEditor extends DartEditor implements IDartReconcilingListener_OLD {
public class AdaptedSourceViewer extends DartSourceViewer {
public AdaptedSourceViewer(Composite parent, IVerticalRuler verticalRuler,
IOverviewRuler overviewRuler, boolean showAnnotationsOverview, int styles,
IPreferenceStore store) {
super(parent, verticalRuler, overviewRuler, showAnnotationsOverview, styles, store);
}
@Override
public IFormattingContext createFormattingContext() {
IFormattingContext context = new CommentFormattingContext();
Map<String, String> preferences = new HashMap<String, String>(DartCore.getOptions());
context.setProperty(FormattingContextProperties.CONTEXT_PREFERENCES, preferences);
return context;
}
@Override
public void doOperation(int operation) {
if (getTextWidget() == null) { //|| !isEditorInputModifiable()) {
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;
case CUT:
boolean success = doCut_fix18161();
if (success) {
return;
}
// use default implementation from ProjectionViewer
break;
}
super.doOperation(operation);
}
public IContentAssistant getContentAssistant() {
return fContentAssistant;
}
public CompilationUnitEditor getEditor() {
return CompilationUnitEditor.this;
}
@Override
public boolean requestWidgetToken(IWidgetTokenKeeper requester) {
if (PlatformUI.getWorkbench().getHelpSystem().isContextHelpDisplayed()) {
return false;
}
return super.requestWidgetToken(requester);
}
@Override
public boolean requestWidgetToken(IWidgetTokenKeeper requester, int priority) {
if (PlatformUI.getWorkbench().getHelpSystem().isContextHelpDisplayed()) {
return false;
}
return super.requestWidgetToken(requester, priority);
}
@Override
protected void ensureAnnotationHoverManagerInstalled() {
super.ensureAnnotationHoverManagerInstalled();
// Hack to force ANCHOR_TOP instead of default ANCHOR_RIGHT.
// https://code.google.com/p/dart/issues/detail?id=17109
try {
AbstractInformationControlManager manager = ReflectionUtils.getFieldObject(
this,
"fVerticalRulerHoveringController");
manager.setAnchor(AbstractInformationControlManager.ANCHOR_TOP);
} catch (Throwable e) {
}
}
@Override
protected int getEmptySelectionChangedEventDelay() {
return 10; // reduced from 500 to speed up mark occurrences
}
/**
* This method fixes https://code.google.com/p/dart/issues/detail?id=18161
* <p>
* We need to copy implementation form {@link ProjectionViewer} because of
* <p>
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=75222
*/
private boolean doCut_fix18161() {
ITextSelection selection = (ITextSelection) getSelection();
if (selection.getLength() == 0) {
copyMarkedRegion(true);
return true;
} else {
StyledText textWidget = getTextWidget();
try {
ReflectionUtils.invokeMethod(
this,
"copyToClipboard(org.eclipse.jface.text.ITextSelection,boolean,org.eclipse.swt.custom.StyledText)",
selection,
true,
textWidget);
Point range = textWidget.getSelectionRange();
fireSelectionChanged(range.x, range.y);
return true;
} catch (Throwable e) {
return false;
}
}
}
}
interface ITextConverter {
void customizeDocumentCommand(IDocument document, DocumentCommand command);
}
private class BracketInserter implements VerifyKeyListener, ILinkedModeListener {
private boolean fCloseBrackets = true;
private boolean fCloseStrings = true;
private boolean fCloseAngularBrackets = true;
private final String CATEGORY = toString();
private final IPositionUpdater fUpdater = new ExclusivePositionUpdater(CATEGORY);
private final Stack<BracketLevel> fBracketLevelStack = new Stack<BracketLevel>();
@Override
public void left(LinkedModeModel environment, int flags) {
final BracketLevel 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() {
@Override
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) {
DartToolsPlugin.log(e);
}
}
if (fBracketLevelStack.size() == 0) {
document.removePositionUpdater(fUpdater);
try {
document.removePositionCategory(CATEGORY);
} catch (BadPositionCategoryException e) {
DartToolsPlugin.log(e);
}
}
}
});
}
}
@Override
public void resume(LinkedModeModel environment, int flags) {
}
public void setCloseAngularBracketsEnabled(boolean enabled) {
fCloseAngularBrackets = enabled;
}
public void setCloseBracketsEnabled(boolean enabled) {
fCloseBrackets = enabled;
}
public void setCloseStringsEnabled(boolean enabled) {
fCloseStrings = enabled;
}
@Override
public void suspend(LinkedModeModel environment) {
}
@Override
public void verifyKey(VerifyEvent event) {
if (!isEditable()) {
return;
}
// early pruning to slow down normal typing as little as possible
if (!event.doit || getInsertMode() != SMART_INSERT || isBlockSelectionModeEnabled()
&& isMultilineSelection()) {
return;
}
// boolean checkWrappingThenReturn = false;
switch (event.character) {
case '(':
case '<':
case '[':
case '\'':
case '\"':
break;
// case '{':
// checkWrappingThenReturn = true;
// 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;
// if (length > 0) {
// IRewriteTarget target = ((ITextViewerExtension) sourceViewer).getRewriteTarget();
// if (couldWrapWithGroup(event.character, document, offset, length, target)) {
// event.doit = false;
// return;
// }
// if (checkWrappingThenReturn) {
// return;
// }
// }
LinkedModeModel existingModel = LinkedModeModel.getModel(document, offset);
if (existingModel != null && existingModel.anyPositionContains(offset)) {
if (LinkedModeModel.class.isAssignableFrom(existingModel.getClass().getSuperclass())) {
// Adding a bracket matcher while completion proposal editing is active causes problems
return;
}
}
try {
IRegion startLine = document.getLineInformationOfOffset(offset);
IRegion endLine = document.getLineInformationOfOffset(offset + length);
DartHeuristicScanner scanner = new DartHeuristicScanner(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() - 1);
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
|| nextToken == Symbols.TokenQUESTIONMARK || nextToken == Symbols.TokenIDENT
&& isTypeArgumentStart(next) || prevToken != Symbols.TokenLBRACE
&& prevToken != Symbols.TokenRBRACE && prevToken != Symbols.TokenSEMICOLON
&& 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 '"':
if (!fCloseStrings) {
return;
}
if (prevToken == Symbols.TokenIDENT && "r".equals(previous)) {
break; // handle raw strings
}
if (prevToken == Symbols.TokenRBRACE) {
return; // "${expression}^
}
if (nextToken == Symbols.TokenIDENT || prevToken == Symbols.TokenIDENT || next != null
&& next.length() > 1
|| (previous != null && previous.length() > 1 && !previous.equals("import"))) {
return;
}
break;
case '{':
if (prevToken == Symbols.TokenIDENT && previous.equals("$")) {
// does not support defining functions within interpolation
break;
} else {
return;
}
default:
return;
}
ITypedRegion partition = TextUtilities.getPartition(
document,
DartPartitions.DART_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();
// 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) {
DartToolsPlugin.log(e);
} catch (BadPositionCategoryException e) {
DartToolsPlugin.log(e);
}
}
// private boolean couldWrapWithGroup(char startCh, IDocument document, int offset, int length,
// IRewriteTarget target) {
// int end = offset + length;
// if (offset < 0 || (document.getLength() <= end)) {
// return false;
// }
// char endCh;
// switch (startCh) {
// case '(':
// endCh = ')';
// break;
// case '<':
// endCh = '>';
// break;
// case '[':
// endCh = ']';
// break;
// case '{':
// endCh = '}';
// break;
//// case '\'':
//// case '\"':
//// endCh = startCh;
//// break;
// default:
// return false;
// }
// MultiTextEdit textEdit = new MultiTextEdit();
// textEdit.addChild(new InsertEdit(offset, String.valueOf(startCh)));
// textEdit.addChild(new InsertEdit(offset + length, String.valueOf(endCh)));
// try {
// target.beginCompoundChange();
// textEdit.apply(document);
// } catch (BadLocationException ex) {
// return false;
// } finally {
// target.endCompoundChange();
// }
// return true;
// }
private boolean isAngularIntroducer(String identifier) {
return identifier.length() > 0
&& (Character.isUpperCase(identifier.charAt(0)) || identifier.startsWith("final")); //$NON-NLS-1$
}
private boolean isMultilineSelection() {
ISelection selection = getSelectionProvider().getSelection();
if (selection instanceof ITextSelection) {
ITextSelection ts = (ITextSelection) selection;
return ts.getStartLine() != ts.getEndLine();
}
return false;
}
private boolean isTypeArgumentStart(String identifier) {
return identifier.length() > 0 && Character.isUpperCase(identifier.charAt(0));
}
}
private static class BracketLevel {
@SuppressWarnings("unused")
int fOffset;
@SuppressWarnings("unused")
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;
}
@Override
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
}
}
}
private class ExitPolicy implements IExitPolicy {
final char fExitCharacter;
final char fEscapeCharacter;
final Stack<BracketLevel> fStack;
final int fSize;
public ExitPolicy(char exitCharacter, char escapeCharacter, Stack<BracketLevel> stack) {
fExitCharacter = exitCharacter;
fEscapeCharacter = escapeCharacter;
fStack = stack;
fSize = fStack.size();
}
@Override
public ExitFlags doExit(LinkedModeModel model, VerifyEvent event, int offset, int length) {
if (fSize == fStack.size() && !isMasked(offset)) {
if (event.character == fExitCharacter) {
BracketLevel 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;
}
}
/**
* Remembers additional data for a given offset to be able restore it later.
*/
private class RememberedOffset {
/**
* Store visual properties of the given offset.
*
* @param offset Offset in the document
*/
public void setOffset(int offset) {
}
}
/**
* 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;
}
//TODO (pquitslund): implement restore for new elements
return;
// try {
//
// int startOffset, endOffset;
// int revealStartOffset, revealEndOffset;
// if (showsHighlightRangeOnly()) {
// DartElement newStartElement = (DartElement) fStartOffset.getElement();
// startOffset = fStartOffset.getRememberedOffset(newStartElement);
// revealStartOffset = fStartOffset.getRevealOffset(newStartElement, startOffset);
// if (revealStartOffset == -1) {
// startOffset = -1;
// }
//
// DartElement newEndElement = (DartElement) 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;
// }
//
// DartElement element;
// if (endOffset == -1) {
// // fallback to element selection
// element = (DartElement) fEndOffset.getElement();
// if (element == null) {
// element = (DartElement) 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;
// }
}
private static final boolean CODE_ASSIST_DEBUG = "true".equalsIgnoreCase(Platform.getDebugOption("com.google.dart.tools.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;
/** 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;
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();
}
}
/**
* Listener to annotation model changes that updates the error tick in the tab image
*/
private DartEditorErrorTickUpdater 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 ActionGroup fRefactorActionGroup;
//
private CompositeActionGroup fContextMenuGroup;
//
// private CorrectionCommandInstaller fCorrectionCommands;
/**
* Reconciling listeners.
*/
private ListenerList fReconcilingListeners_OLD = new ListenerList(ListenerList.IDENTITY);
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();
/** The reconciling strategy */
private DartReconcilingStrategy dartReconcilingStrategy;
/**
* Creates a new compilation unit editor.
*/
public CompilationUnitEditor() {
super();
setDocumentProvider(DartToolsPlugin.getDefault().getCompilationUnitDocumentProvider());
setEditorContextMenuId("#DartEditorContext"); //$NON-NLS-1$
setRulerContextMenuId("#DartRulerContext"); //$NON-NLS-1$
setOutlinerContextMenuId("#JavaScriptOutlinerContext"); //$NON-NLS-1$
scheduleReconcileAfterBuild();
fJavaEditorErrorTickUpdater = new DartEditorErrorTickUpdater(this);
DartX.todo("actions");
// fCorrectionCommands = null;
}
/*
* @see com.google.dart.tools.ui.functions.java.IJavaReconcilingListener# aboutToBeReconciled()
*/
@Override
public void aboutToBeReconciled() {
// Notify listeners
Object[] listeners = fReconcilingListeners_OLD.getListeners();
for (int i = 0, length = listeners.length; i < length; ++i) {
((IDartReconcilingListener_OLD) listeners[i]).aboutToBeReconciled();
}
}
/**
* Adds the given listener. Has no effect if an identical listener was not already registered.
*
* @param listener The reconcile listener to be added
*/
public void addReconcileListener(IDartReconcilingListener listener) {
synchronized (fReconcilingListeners) {
fReconcilingListeners.add(listener);
}
}
@Override
public void addViewerDisposeListener(DisposeListener listener) {
getViewer().getTextWidget().addDisposeListener(listener);
}
@Override
public void applyResolvedUnit(com.google.dart.engine.ast.CompilationUnit unit) {
super.applyResolvedUnit(unit);
if (unit != null) {
// notify listeners
{
Object[] listeners = fReconcilingListeners.getListeners();
for (int i = 0, length = listeners.length; i < length; ++i) {
((IDartReconcilingListener) listeners[i]).reconciled(unit);
}
}
}
}
/*
* @see AbstractTextEditor#createPartControl(Composite)
*/
@Override
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 = closeBrackets;
fBracketInserter.setCloseBracketsEnabled(closeBrackets);
fBracketInserter.setCloseStringsEnabled(closeStrings);
fBracketInserter.setCloseAngularBracketsEnabled(closeAngularBrackets);
ISourceViewer sourceViewer = getSourceViewer();
if (sourceViewer instanceof ITextViewerExtension) {
((ITextViewerExtension) sourceViewer).prependVerifyKeyListener(fBracketInserter);
}
if (isMarkingOccurrences()) {
installOccurrencesFinder(false);
}
}
@Override
public void dispose() {
ISourceViewer sourceViewer = getSourceViewer();
if (sourceViewer instanceof ITextViewerExtension) {
((ITextViewerExtension) sourceViewer).removeVerifyKeyListener(fBracketInserter);
}
if (fJavaEditorErrorTickUpdater != null) {
fJavaEditorErrorTickUpdater.dispose();
fJavaEditorErrorTickUpdater = null;
}
DartX.todo("actions");
// if (fCorrectionCommands != null) {
// fCorrectionCommands.deregisterCommands();
// fCorrectionCommands = null;
// }
fReconcilingListeners = new ListenerList(ListenerList.IDENTITY);
super.dispose();
}
/*
* @see AbstractTextEditor#doSave(IProgressMonitor)
*/
@Override
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,
DartEditorMessages.CompilationUnitEditor_error_saving_title1,
DartEditorMessages.CompilationUnitEditor_error_saving_message1);
}
} else {
setStatusLineErrorMessage(null);
updateState(getEditorInput());
validateState(getEditorInput());
performSave(false, progressMonitor);
}
}
/*
* @see AbstractTextEditor#editorContextMenuAboutToShow(IMenuManager)
*/
@Override
public void editorContextMenuAboutToShow(IMenuManager menu) {
super.editorContextMenuAboutToShow(menu);
// addAction(menu, "ToggleComment");
// add Organize Imports action to menu
// menu.add(new Separator());
// addAction(menu, "OrganizeImports");
}
/*
* @see com.google.dart.tools.ui.editor.JavaEditor#getAdapter(java.lang.Class)
*/
@SuppressWarnings("rawtypes")
@Override
public Object getAdapter(Class required) {
if (SmartBackspaceManager.class.equals(required)) {
if (getSourceViewer() instanceof DartSourceViewer) {
return ((DartSourceViewer) getSourceViewer()).getBackspaceManager();
}
}
return super.getAdapter(required);
}
@Override
public DartReconcilingStrategy getDartReconcilingStrategy() {
return dartReconcilingStrategy;
}
/**
* 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;
}
public ActionGroup getRefactorActionGroup() {
return fRefactorActionGroup;
}
@Override
public Object getViewPartInput() {
return null;
}
@Override
public boolean isSaveAsAllowed() {
return true;
}
@Override
public void reconciled(boolean forced, IProgressMonitor progressMonitor) {
AutoSaveHelper.reconciled(getEditorInput(), getSourceViewer(), getTextSelectionRange());
// see: https://bugs.eclipse.org/bugs/show_bug.cgi?id=58245
DartToolsPlugin dartPlugin = DartToolsPlugin.getDefault();
if (dartPlugin == null) {
return;
}
// Always notify AST provider
dartPlugin.getASTProvider().reconciled(progressMonitor);
// Notify listeners
Object[] listeners = fReconcilingListeners_OLD.getListeners();
for (int i = 0, length = listeners.length; i < length; ++i) {
((IDartReconcilingListener_OLD) listeners[i]).reconciled(forced, progressMonitor);
}
// Update Outline page selection
if (!forced && !progressMonitor.isCanceled()) {
Shell shell = getSite().getShell();
if (shell != null && !shell.isDisposed()) {
ExecutionUtils.runLogAsync(new RunnableEx() {
@Override
public void run() {
selectionChanged();
}
});
}
}
}
/**
* Removes the given listener. Has no effect if an identical listener was not already registered.
*
* @param listener the reconcile listener to be removed
*/
public void removeReconcileListener(IDartReconcilingListener listener) {
synchronized (fReconcilingListeners) {
fReconcilingListeners.remove(listener);
}
}
@Override
public void setDartReconcilingStrategy(DartReconcilingStrategy dartReconcilingStrategy) {
this.dartReconcilingStrategy = dartReconcilingStrategy;
}
/*
* @see AbstractTextEditor#canHandleMove(IEditorInput, IEditorInput)
*/
@Override
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 AbstractTextEditor#createActions()
*/
@Override
protected void createActions() {
super.createActions();
DartX.todo("actions");
IAction action = new ContentAssistAction(
DartEditorMessages.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,
DartHelpContextIds.CONTENT_ASSIST_ACTION);
action = new TextOperationAction(
DartEditorMessages.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,
DartHelpContextIds.PARAMETER_HINTS_ACTION);
action = new TextOperationAction(
DartEditorMessages.getBundleForConstructedKeys(),
"Comment.", this, ITextOperationTarget.PREFIX); //$NON-NLS-1$
action.setActionDefinitionId(DartEditorActionDefinitionIds.COMMENT);
setAction("Comment", action); //$NON-NLS-1$
markAsStateDependentAction("Comment", true); //$NON-NLS-1$
PlatformUI.getWorkbench().getHelpSystem().setHelp(action, DartHelpContextIds.COMMENT_ACTION);
action = new TextOperationAction(
DartEditorMessages.getBundleForConstructedKeys(),
"Uncomment.", this, ITextOperationTarget.STRIP_PREFIX); //$NON-NLS-1$
action.setActionDefinitionId(DartEditorActionDefinitionIds.UNCOMMENT);
setAction("Uncomment", action); //$NON-NLS-1$
markAsStateDependentAction("Uncomment", true); //$NON-NLS-1$
PlatformUI.getWorkbench().getHelpSystem().setHelp(action, DartHelpContextIds.UNCOMMENT_ACTION);
action = new ToggleCommentAction(
DartEditorMessages.getBundleForConstructedKeys(),
"ToggleComment.", this); //$NON-NLS-1$
action.setActionDefinitionId(DartEditorActionDefinitionIds.TOGGLE_COMMENT);
setAction("ToggleComment", action); //$NON-NLS-1$
markAsStateDependentAction("ToggleComment", true); //$NON-NLS-1$
PlatformUI.getWorkbench().getHelpSystem().setHelp(
action,
DartHelpContextIds.TOGGLE_COMMENT_ACTION);
configureToggleCommentAction();
action = new TextOperationAction(
DartEditorMessages.getBundleForConstructedKeys(),
"Format.", this, ISourceViewer.FORMAT); //$NON-NLS-1$
action.setActionDefinitionId(DartEditorActionDefinitionIds.FORMAT);
setAction("Format", action); //$NON-NLS-1$
markAsStateDependentAction("Format", true); //$NON-NLS-1$
markAsSelectionDependentAction("Format", true); //$NON-NLS-1$
PlatformUI.getWorkbench().getHelpSystem().setHelp(action, DartHelpContextIds.FORMAT_ACTION);
// action = new OrganizeImportsAction(this);
// action.setActionDefinitionId(DartEditorActionDefinitionIds.ORGANIZE_IMPORTS);
// setAction("OrganizeImports", action);
// action = new AddBlockCommentAction(
// DartEditorMessages.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,
// DartHelpContextIds.ADD_BLOCK_COMMENT_ACTION);
// action = new RemoveBlockCommentAction(
// DartEditorMessages.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,
// DartHelpContextIds.REMOVE_BLOCK_COMMENT_ACTION);
// override the text editor actions with indenting move line actions
DartMoveLinesAction[] moveLinesActions = DartMoveLinesAction.createMoveCopyActionSet(
DartEditorMessages.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);
if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) {
fRefactorActionGroup = new RefactorActionGroup_NEW(this);
} else {
fRefactorActionGroup = new RefactorActionGroup_OLD(this);
// ActionGroup surroundWith = new SurroundWithActionGroup(this,
// ITextEditorActionConstants.GROUP_EDIT);
}
// fActionGroups.addGroup(surroundWith);
fActionGroups.addGroup(fRefactorActionGroup);
fActionGroups.addGroup(fGenerateActionGroup);
// We have to keep the context menu group separate to have better control
// over positioning
fContextMenuGroup = new CompositeActionGroup(new ActionGroup[] {
// fGenerateActionGroup,
fRefactorActionGroup,
// surroundWith,
// new LocalHistoryActionGroup(this,
// ITextEditorActionConstants.GROUP_EDIT)
});
// allow shortcuts for quick fix/assist
// fCorrectionCommands = new CorrectionCommandInstaller();
// fCorrectionCommands.registerCommands(this);
}
/*
* @see com.google.dart.tools.ui.editor.DartEditor#createDartSourceViewer(org.eclipse
* .swt.widgets.Composite, org.eclipse.jface.text.source.IVerticalRuler,
* org.eclipse.jface.text.source.IOverviewRuler, boolean, int)
*/
@Override
protected ISourceViewer createDartSourceViewer(Composite parent, IVerticalRuler verticalRuler,
IOverviewRuler overviewRuler, boolean isOverviewRulerVisible, int styles,
IPreferenceStore store) {
return new AdaptedSourceViewer(
parent,
verticalRuler,
overviewRuler,
isOverviewRulerVisible,
styles,
store);
}
/*
* @see com.google.dart.tools.ui.editor.JavaEditor#createNavigationActions()
*/
@Override
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);
}
/*
* @see AbstractTextEditor#doSetInput(IEditorInput)
*/
@Override
protected void doSetInput(IEditorInput input) throws CoreException {
super.doSetInput(input);
configureToggleCommentAction();
if (fJavaEditorErrorTickUpdater != null) {
checkEditableState();
if (input instanceof FileStoreEditorInput) {
fJavaEditorErrorTickUpdater.updateEditorImage(input);
}
}
}
@Override
protected void firePropertyChange(int property) {
if (property == PROP_DIRTY && !isDirty()) {
if (dartReconcilingStrategy != null) {
dartReconcilingStrategy.saved();
}
}
super.firePropertyChange(property);
}
@Override
protected Object 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
*/
@Override
protected Object getElementAt(int offset, boolean reconcile) {
return NewSelectionConverter.getElementAtOffset(this, offset);
}
/*
* @see AbstractTextEditor#handlePreferenceStoreChanged(PropertyChangeEvent)
*/
@Override
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.ui.texteditor.AbstractTextEditor#installTabsToSpacesConverter()
*/
@Override
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();
}
}
/**
* 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);
}
/*
* @see org.eclipse.ui.texteditor.AbstractTextEditor#openSaveErrorDialog(java.lang .String,
* java.lang.String, org.eclipse.core.runtime.CoreException)
*/
@Override
protected void openSaveErrorDialog(String title, String message, CoreException exception) {
IStatus status = exception.getStatus();
if (DartUI.ID_PLUGIN.equals(status.getPlugin())
&& status.getCode() == DartStatusConstants.EDITOR_POST_SAVE_NOTIFICATION) {
int mask = IStatus.OK | IStatus.INFO | IStatus.WARNING | IStatus.ERROR;
ErrorDialog dialog = new ErrorDialog(getSite().getShell(), title, message, status, mask) {
@Override
protected Control createDialogArea(Composite parent) {
parent = (Composite) super.createDialogArea(parent);
Link link = new Link(parent, SWT.NONE);
link.setText(DartEditorMessages.CompilationUnitEditor_error_saving_saveParticipant);
link.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
PreferencesUtil.createPreferenceDialogOn(
getShell(),
"com.google.dart.tools.ui.internal.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);
}
}
/*
* @see org.eclipse.ui.texteditor.AbstractTextEditor#performSave(boolean,
* org.eclipse.core.runtime.IProgressMonitor)
*/
@Override
protected void performSave(boolean overwrite, IProgressMonitor progressMonitor) {
IDocumentProvider p = getDocumentProvider();
try {
if (dartReconcilingStrategy != null) {
dartReconcilingStrategy.reconcile();
}
super.performSave(overwrite, progressMonitor);
} finally {
if (p instanceof ICompilationUnitDocumentProvider) {
checkEditableState();
}
}
}
/*
* @see AbstractTextEditor#rememberSelection()
*/
@Override
protected void rememberSelection() {
fRememberedSelection.remember();
}
/*
* @see AbstractTextEditor#restoreSelection()
*/
@Override
protected void restoreSelection() {
fRememberedSelection.restore();
}
@Override
protected void setContextMenuContext(IMenuManager menu, ActionContext context) {
super.setContextMenuContext(menu, context);
fContextMenuGroup.setContext(context);
fContextMenuGroup.fillContextMenu(menu);
fContextMenuGroup.setContext(null);
}
@Override
protected void updateStateDependentActions() {
super.updateStateDependentActions();
DartX.todo("actions");
fGenerateActionGroup.editorStateChanged();
}
@Override
protected void updateStatusField(String category) {
super.updateStatusField(category);
if (ITextEditorActionConstants.STATUS_CATEGORY_INPUT_POSITION.equals(category)) {
IStatusField field = getStatusField(IDartEditorActionConstants.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);
}
}
}
/**
* 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_OLD(IDartReconcilingListener_OLD listener) {
synchronized (fReconcilingListeners_OLD) {
fReconcilingListeners_OLD.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_OLD(IDartReconcilingListener_OLD listener) {
synchronized (fReconcilingListeners_OLD) {
fReconcilingListeners_OLD.remove(listener);
}
}
/**
* 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);
}
}
/**
* We need to force reconcile each time when builder reanalyzes underlying file. For example: use
* imports from saved unit; update problems-as-you-type after external change, such as reanalyze
* all.
*/
private void scheduleReconcileAfterBuild() {
//TODO (pquitslund): investigate whether we need hooks for reconcile on build
}
}