/******************************************************************************* * 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); } } } }