1 /*******************************************************************************
2 * Copyright (c) 2000, 2011 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
9 * IBM Corporation - initial API and implementation
10 * Tom Eicher (Avaloq Evolution AG) - block selection mode
11 *******************************************************************************/
12 package org.eclipse.jdt.internal.ui.actions;
14 import java.util.ArrayList;
15 import java.util.Iterator;
16 import java.util.ResourceBundle;
18 import org.eclipse.swt.custom.BusyIndicator;
19 import org.eclipse.swt.widgets.Display;
21 import org.eclipse.core.runtime.Assert;
22 import org.eclipse.core.runtime.IStatus;
23 import org.eclipse.core.runtime.Status;
25 import org.eclipse.text.edits.MultiTextEdit;
26 import org.eclipse.text.edits.ReplaceEdit;
27 import org.eclipse.text.edits.TextEdit;
29 import org.eclipse.jface.viewers.ISelection;
30 import org.eclipse.jface.viewers.ISelectionProvider;
32 import org.eclipse.jface.text.BadLocationException;
33 import org.eclipse.jface.text.IDocument;
34 import org.eclipse.jface.text.IRegion;
35 import org.eclipse.jface.text.IRewriteTarget;
36 import org.eclipse.jface.text.ITextSelection;
37 import org.eclipse.jface.text.ITypedRegion;
38 import org.eclipse.jface.text.Position;
39 import org.eclipse.jface.text.TextSelection;
40 import org.eclipse.jface.text.TextUtilities;
41 import org.eclipse.jface.text.source.ISourceViewer;
43 import org.eclipse.ui.IEditorInput;
45 import org.eclipse.ui.texteditor.IDocumentProvider;
46 import org.eclipse.ui.texteditor.ITextEditor;
47 import org.eclipse.ui.texteditor.ITextEditorExtension3;
48 import org.eclipse.ui.texteditor.TextEditorAction;
50 import org.eclipse.jdt.core.ICompilationUnit;
51 import org.eclipse.jdt.core.IJavaProject;
52 import org.eclipse.jdt.core.JavaCore;
53 import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
55 import org.eclipse.jdt.ui.text.IJavaPartitions;
57 import org.eclipse.jdt.internal.ui.JavaPlugin;
58 import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor;
59 import org.eclipse.jdt.internal.ui.text.JavaHeuristicScanner;
60 import org.eclipse.jdt.internal.ui.text.JavaIndenter;
64 * Indents a line or range of lines in a Java document to its correct position. No complete
65 * AST must be present, the indentation is computed using heuristics. The algorithm used is fast for
66 * single lines, but does not store any information and therefore not so efficient for large line
69 * @see org.eclipse.jdt.internal.ui.text.JavaHeuristicScanner
70 * @see org.eclipse.jdt.internal.ui.text.JavaIndenter
73 public class IndentAction extends TextEditorAction {
78 static final class ReplaceData {
83 public final String indent;
86 * The start of the replacement
88 public final int offset;
91 * The end of the replacement
96 * Replace string in document from offset to end with indent
97 * @param offset the start of the replacement
98 * @param end the end of the replacement
99 * @param indent the replacement
101 public ReplaceData(int offset, int end, String indent) {
107 public boolean generated_7289008201151769652(IDocument document, int caret, IJavaProject project, IndentAction indentaction) throws BadLocationException {
108 String indent= indent;
112 int length= end - offset;
113 String currentIndent= document.get(offset, length);
115 // if we are right before the text start / line end, and already after the insertion point
116 // then just insert a tab.
117 if (indentaction.fIsTabAction && caret == end && IndentAction.whiteSpaceLength(currentIndent, project) >= IndentAction.whiteSpaceLength(indent, project)) {
118 String tab= IndentAction.getTabEquivalent(project);
119 document.replace(caret, 0, tab);
120 indentaction.fCaretOffset= caret + tab.length();
124 // set the caret offset so it can be used when setting the selection
125 if (caret >= offset && caret <= end)
126 indentaction.fCaretOffset= offset + indent.length();
128 indentaction.fCaretOffset= -1;
130 // only change the document if it is a real change
131 if (!indent.equals(currentIndent)) {
132 document.replace(offset, length, indent);
140 /** The caret offset after an indent operation. */
141 private int fCaretOffset;
144 * Whether this is the action invoked by TAB. When <code>true</code>, indentation behaves
145 * differently to accommodate normal TAB operation.
147 private final boolean fIsTabAction;
150 * Creates a new instance.
152 * @param bundle the resource bundle
153 * @param prefix the prefix to use for keys in <code>bundle</code>
154 * @param editor the text editor
155 * @param isTabAction whether the action should insert tabs if over the indentation
157 public IndentAction(ResourceBundle bundle, String prefix, ITextEditor editor, boolean isTabAction) {
158 super(bundle, prefix, editor);
159 fIsTabAction= isTabAction;
163 * @see org.eclipse.jface.action.Action#run()
167 // update has been called by the framework
168 if (!isEnabled() || !validateEditorInputState())
171 ITextSelection selection= getSelection();
172 final IDocument document= getDocument();
174 if (document != null) {
176 final int offset= selection.getOffset();
177 final int length= selection.getLength();
178 final Position end= new Position(offset + length);
179 final int firstLine, nLines;
183 document.addPosition(end);
184 firstLine= selection.getStartLine();
185 nLines= selection.getEndLine() - firstLine + 1;
186 } catch (BadLocationException e) {
187 // will only happen on concurrent modification
188 JavaPlugin.log(new Status(IStatus.ERROR, JavaPlugin.getPluginId(), IStatus.OK, "", e)); //$NON-NLS-1$
192 Runnable runnable= new Runnable() {
194 IRewriteTarget target= (IRewriteTarget)getTextEditor().getAdapter(IRewriteTarget.class);
196 target.beginCompoundChange();
199 JavaHeuristicScanner scanner= new JavaHeuristicScanner(document);
200 JavaIndenter indenter= new JavaIndenter(document, scanner, getJavaProject());
201 final boolean multiLine= nLines > 1;
202 boolean hasChanged= false;
203 for (int i= 0; i < nLines; i++) {
204 hasChanged |= indentLine(document, firstLine + i, offset, indenter, scanner, multiLine);
207 // update caret position: move to new position when indenting just one line
208 // keep selection when indenting multiple
209 int newOffset, newLength;
210 if (!fIsTabAction && multiLine) {
212 newLength= end.getOffset() - offset;
214 newOffset= fCaretOffset;
218 // always reset the selection if anything was replaced
219 // but not when we had a single line non-tab invocation
220 if (newOffset != -1 && (hasChanged || newOffset != offset || newLength != length))
221 selectAndReveal(newOffset, newLength);
223 document.removePosition(end);
224 } catch (BadLocationException e) {
225 // will only happen on concurrent modification
226 JavaPlugin.log(new Status(IStatus.ERROR, JavaPlugin.getPluginId(), IStatus.OK, "ConcurrentModification in IndentAction", e)); //$NON-NLS-1$
230 target.endCompoundChange();
236 Display display= getTextEditor().getEditorSite().getWorkbenchWindow().getShell().getDisplay();
237 BusyIndicator.showWhile(display, runnable);
245 * Selects the given range on the editor.
247 * @param newOffset the selection offset
248 * @param newLength the selection range
250 private void selectAndReveal(int newOffset, int newLength) {
251 Assert.isTrue(newOffset >= 0);
252 Assert.isTrue(newLength >= 0);
253 ITextEditor editor= getTextEditor();
254 if (editor instanceof JavaEditor) {
255 ISourceViewer viewer= ((JavaEditor)editor).getViewer();
257 viewer.setSelectedRange(newOffset, newLength);
259 // this is too intrusive, but will never get called anyway
260 getTextEditor().selectAndReveal(newOffset, newLength);
265 * Indent the given <code>document</code> based on the <code>project</code> settings and
266 * return a text edit describing the changes applied to the document. Returns <b>null</b>
267 * if no changes have been applied.
269 * WARNING: This method does change the content of the given document.
272 * This method is for internal use only, it should not be called.
275 * @param document the document to indent must have a java partitioning installed
276 * @param project the project to retrieve the indentation settings from, <b>null</b> for workspace settings
277 * @return a text edit describing the changes or <b>null</b> if no changes required
278 * @throws BadLocationException if the document got modified concurrently
282 public static TextEdit indent(IDocument document, IJavaProject project) throws BadLocationException {
284 int length= document.getLength();
286 JavaHeuristicScanner scanner= new JavaHeuristicScanner(document);
287 JavaIndenter indenter= new JavaIndenter(document, scanner, project);
289 ArrayList<ReplaceEdit> edits= new ArrayList<ReplaceEdit>();
291 int firstLine= document.getLineOfOffset(offset);
292 // check for marginal (zero-length) lines
293 int minusOne= length == 0 ? 0 : 1;
294 int numberOfLines= document.getLineOfOffset(offset + length - minusOne) - firstLine + 1;
297 for (int i= 0; i < numberOfLines; i++) {
298 ReplaceData data= computeReplaceData(document, firstLine + i, indenter, scanner, numberOfLines > 1, false, project);
300 int replaceLength= data.end - data.offset;
301 String currentIndent= document.get(data.offset, replaceLength);
303 // only change the document if it is a real change
304 if (!data.indent.equals(currentIndent)) {
305 edits.add(new ReplaceEdit(data.offset + shift, replaceLength, data.indent));
306 //We need to change the document, the indenter depends on it.
307 document.replace(data.offset, replaceLength, data.indent);
308 shift-= data.indent.length() - replaceLength;
312 if (edits.size() == 0)
315 if (edits.size() == 1)
318 MultiTextEdit result= new MultiTextEdit();
319 for (Iterator<ReplaceEdit> iterator= edits.iterator(); iterator.hasNext();) {
320 TextEdit edit= iterator.next();
321 result.addChild(edit);
328 * Indents a single line using the java heuristic scanner. Javadoc and multiline comments are
329 * indented as specified by the <code>JavaDocAutoIndentStrategy</code>.
331 * @param document the document
332 * @param line the line to be indented
333 * @param indenter the java indenter
334 * @param scanner the heuristic scanner
335 * @param multiLine <code>true</code> if more than one line is being indented
336 * @param isTabAction <code>true</code> if this action has been invoked by TAB
337 * @param project the project to retrieve the indentation settings from, <b>null</b> for workspace settings
338 * @return <code>true</code> if <code>document</code> was modified, <code>false</code> otherwise
339 * @throws BadLocationException if the document got changed concurrently
341 private static ReplaceData computeReplaceData(IDocument document, int line, JavaIndenter indenter, JavaHeuristicScanner scanner, boolean multiLine, boolean isTabAction, IJavaProject project) throws BadLocationException {
342 IRegion currentLine= document.getLineInformation(line);
343 int offset= currentLine.getOffset();
344 int wsStart= offset; // where we start searching for non-WS; after the "//" in single line comments
347 if (offset < document.getLength()) {
348 ITypedRegion partition= TextUtilities.getPartition(document, IJavaPartitions.JAVA_PARTITIONING, offset, true);
349 String type= partition.getType();
350 ITypedRegion startingPartition= TextUtilities.getPartition(document, IJavaPartitions.JAVA_PARTITIONING, offset, false);
351 String startingType= startingPartition.getType();
352 boolean isCommentStart= startingPartition.getOffset() == offset;
353 if (isDontIndentMultiLineCommentOnFirstColumn(project) && isCommentStart && IJavaPartitions.JAVA_MULTI_LINE_COMMENT.equals(startingType)) {
354 indent= ""; //$NON-NLS-1$
355 } else if (IJavaPartitions.JAVA_DOC.equals(type) || IJavaPartitions.JAVA_MULTI_LINE_COMMENT.equals(type)) {
356 indent= computeJavadocIndent(document, line, scanner, startingPartition);
357 } else if (!isTabAction && isCommentStart && IJavaPartitions.JAVA_SINGLE_LINE_COMMENT.equals(startingType)) {
358 // line comment starting at position 0
360 //Do what the formatter does
361 if (isDontIndentSingleLineCommentOnFirstColumn(project))
362 indent= ""; //$NON-NLS-1$
364 //indent inside -> add/remove indent such that user can start typing at correct position
365 int slashes= countLeadingSlashPairs(document, offset) * 2;
366 wsStart= offset + slashes;
368 StringBuffer computed= indenter.computeIndentation(offset);
369 if (computed == null)
370 computed= new StringBuffer(0);
372 removeIndentations(slashes, getTabSize(project), computed);
373 indent= document.get(offset, wsStart - offset) + computed;
378 // standard java indentation
379 if (indent == null) {
380 StringBuffer computed= indenter.computeIndentation(offset);
381 if (computed != null)
382 indent= computed.toString();
384 indent= ""; //$NON-NLS-1$
388 // get current white space
389 int lineLength= currentLine.getLength();
390 int end= scanner.findNonWhitespaceForwardInAnyPartition(wsStart, offset + lineLength);
391 if (end == JavaHeuristicScanner.NOT_FOUND) {
393 end= offset + lineLength;
394 if (multiLine && !indentEmptyLines(project))
395 indent= ""; //$NON-NLS-1$
398 return new ReplaceData(offset, end, indent);
402 * Removes <code>count</code> indentations from start
403 * of <code>buffer</code>. The size of a space character
404 * is 1 and the size of a tab character is <code>tabSize</code>.
406 * @param count the number of indentations to remove
407 * @param tabSize the size of a tab character
408 * @param buffer the buffer to modify
411 private static void removeIndentations(int count, int tabSize, StringBuffer buffer) {
412 while (count > 0 && buffer.length() > 0) {
413 char c= buffer.charAt(0);
423 buffer.deleteCharAt(0);
428 * Returns number of continuous slashes pairs ('//') starting at <code>offset</code>
429 * in <code>document</code>
431 * @param document the document to inspect
432 * @param offset the offset where to start looking for slash pairs
433 * @return the number of slash pairs.
434 * @throws BadLocationException
437 private static int countLeadingSlashPairs(IDocument document, int offset) throws BadLocationException {
438 IRegion lineInfo= document.getLineInformationOfOffset(offset);
439 int max= lineInfo.getOffset() + lineInfo.getLength() - 1;
442 while (offset < max && document.get(offset, 2).equals("//")) { //$NON-NLS-1$
451 * Indents a single line using the java heuristic scanner. Javadoc and multiline comments are
452 * indented as specified by the <code>JavaDocAutoIndentStrategy</code>.
454 * @param document the document
455 * @param line the line to be indented
456 * @param caret the caret position
457 * @param indenter the java indenter
458 * @param scanner the heuristic scanner
459 * @param multiLine <code>true</code> if more than one line is being indented
460 * @return <code>true</code> if <code>document</code> was modified, <code>false</code> otherwise
461 * @throws BadLocationException if the document got changed concurrently
463 private boolean indentLine(IDocument document, int line, int caret, JavaIndenter indenter, JavaHeuristicScanner scanner, boolean multiLine) throws BadLocationException {
464 IJavaProject project= getJavaProject();
465 ReplaceData data= computeReplaceData(document, line, indenter, scanner, multiLine, fIsTabAction, project);
467 return data.generated_7289008201151769652(document, caret, project, this);
471 * Computes and returns the indentation for a javadoc line. The line
472 * must be inside a javadoc comment.
474 * @param document the document
475 * @param line the line in document
476 * @param scanner the scanner
477 * @param partition the javadoc partition
478 * @return the indent, or <code>null</code> if not computable
479 * @throws BadLocationException
482 private static String computeJavadocIndent(IDocument document, int line, JavaHeuristicScanner scanner, ITypedRegion partition) throws BadLocationException {
483 if (line == 0) // impossible - the first line is never inside a javadoc comment
486 // don't make any assumptions if the line does not start with \s*\* - it might be
487 // commented out code, for which we don't want to change the indent
488 final IRegion lineInfo= document.getLineInformation(line);
489 final int lineStart= lineInfo.getOffset();
490 final int lineLength= lineInfo.getLength();
491 final int lineEnd= lineStart + lineLength;
492 int nonWS= scanner.findNonWhitespaceForwardInAnyPartition(lineStart, lineEnd);
493 if (nonWS == JavaHeuristicScanner.NOT_FOUND || document.getChar(nonWS) != '*') {
494 if (nonWS == JavaHeuristicScanner.NOT_FOUND)
495 return document.get(lineStart, lineLength);
496 return document.get(lineStart, nonWS - lineStart);
499 // take the indent from the previous line and reuse
500 IRegion previousLine= document.getLineInformation(line - 1);
501 int previousLineStart= previousLine.getOffset();
502 int previousLineLength= previousLine.getLength();
503 int previousLineEnd= previousLineStart + previousLineLength;
505 StringBuffer buf= new StringBuffer();
506 int previousLineNonWS= scanner.findNonWhitespaceForwardInAnyPartition(previousLineStart, previousLineEnd);
507 if (previousLineNonWS == JavaHeuristicScanner.NOT_FOUND || document.getChar(previousLineNonWS) != '*') {
508 // align with the comment start if the previous line is not an asterisked line
509 previousLine= document.getLineInformationOfOffset(partition.getOffset());
510 previousLineStart= previousLine.getOffset();
511 previousLineLength= previousLine.getLength();
512 previousLineEnd= previousLineStart + previousLineLength;
513 previousLineNonWS= scanner.findNonWhitespaceForwardInAnyPartition(previousLineStart, previousLineEnd);
514 if (previousLineNonWS == JavaHeuristicScanner.NOT_FOUND)
515 previousLineNonWS= previousLineEnd;
517 // add the initial space
518 // TODO this may be controlled by a formatter preference in the future
522 String indentation= document.get(previousLineStart, previousLineNonWS - previousLineStart);
523 buf.insert(0, indentation);
524 return buf.toString();
528 * Returns the size in characters of a string. All characters count one, tabs count the editor's
529 * preference for the tab display
531 * @param indent the string to be measured.
532 * @param project the project to retrieve the indentation settings from, <b>null</b> for workspace settings
533 * @return the size in characters of a string
535 private static int whiteSpaceLength(String indent, IJavaProject project) {
540 int l= indent.length();
541 int tabSize= getTabSize(project);
543 for (int i= 0; i < l; i++)
544 size += indent.charAt(i) == '\t' ? tabSize : 1;
550 * Returns a tab equivalent, either as a tab character or as spaces, depending on the editor and
551 * formatter preferences.
553 * @param project the project to retrieve the indentation settings from, <b>null</b> for workspace settings
554 * @return a string representing one tab in the editor, never <code>null</code>
556 private static String getTabEquivalent(IJavaProject project) {
558 if (JavaCore.SPACE.equals(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, project))) {
559 int size= getTabSize(project);
560 StringBuffer buf= new StringBuffer();
561 for (int i= 0; i< size; i++)
565 tab= "\t"; //$NON-NLS-1$
571 * Returns the tab size used by the java editor, which is deduced from the
572 * formatter preferences.
574 * @param project the project to retrieve the indentation settings from, <b>null</b> for workspace settings
575 * @return the tab size as defined in the current formatter preferences
577 private static int getTabSize(IJavaProject project) {
578 return getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE, 4, project);
582 * Returns <code>true</code> if empty lines should be indented, <code>false</code> otherwise.
584 * @param project the project to retrieve the indentation settings from, <b>null</b> for workspace settings
585 * @return <code>true</code> if empty lines should be indented, <code>false</code> otherwise
588 private static boolean indentEmptyLines(IJavaProject project) {
589 return DefaultCodeFormatterConstants.TRUE.equals(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_INDENT_EMPTY_LINES, project));
593 * Returns <code>true</code> if multi line comments which start at first column
594 * should not be indented, <code>false</code> otherwise.
596 * @param project the project to retrieve the indentation settings from, <b>null</b> for workspace settings
597 * @return <code>true</code> if such multi line comments should be indented, <code>false</code> otherwise
600 private static boolean isDontIndentMultiLineCommentOnFirstColumn(IJavaProject project) {
601 return DefaultCodeFormatterConstants.TRUE.equals(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_NEVER_INDENT_BLOCK_COMMENTS_ON_FIRST_COLUMN, project));
605 * Returns <code>true</code> if single line comments which start at first column
606 * should not be indented, <code>false</code> otherwise.
608 * @param project the project to retrieve the indentation settings from, <b>null</b> for workspace settings
609 * @return <code>true</code> if such single line comments should be indented, <code>false</code> otherwise
612 private static boolean isDontIndentSingleLineCommentOnFirstColumn(IJavaProject project) {
613 return DefaultCodeFormatterConstants.TRUE.equals(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_NEVER_INDENT_LINE_COMMENTS_ON_FIRST_COLUMN, project));
617 * Returns the possibly project-specific core preference defined under <code>key</code>.
619 * @param key the key of the preference
620 * @param project the project to retrieve the indentation settings from, <b>null</b> for workspace settings
621 * @return the value of the preference
624 private static String getCoreFormatterOption(String key, IJavaProject project) {
626 return JavaCore.getOption(key);
627 return project.getOption(key, true);
631 * Returns the possibly project-specific core preference defined under <code>key</code>, or
632 * <code>def</code> if the value is not a integer.
634 * @param key the key of the preference
635 * @param def the default value
636 * @param project the project to retrieve the indentation settings from, <b>null</b> for workspace settings
637 * @return the value of the preference
640 private static int getCoreFormatterOption(String key, int def, IJavaProject project) {
642 return Integer.parseInt(getCoreFormatterOption(key, project));
643 } catch (NumberFormatException e) {
649 * Returns the <code>IJavaProject</code> of the current editor input, or
650 * <code>null</code> if it cannot be found.
652 * @return the <code>IJavaProject</code> of the current editor input, or
653 * <code>null</code> if it cannot be found
656 private IJavaProject getJavaProject() {
657 ITextEditor editor= getTextEditor();
661 ICompilationUnit cu= JavaPlugin.getDefault().getWorkingCopyManager().getWorkingCopy(editor.getEditorInput());
664 return cu.getJavaProject();
668 * Returns the editor's selection provider.
670 * @return the editor's selection provider or <code>null</code>
672 private ISelectionProvider getSelectionProvider() {
673 ITextEditor editor= getTextEditor();
674 if (editor != null) {
675 return editor.getSelectionProvider();
681 * @see org.eclipse.ui.texteditor.IUpdate#update()
684 public void update() {
689 setEnabled(canModifyEditor() && isSmartMode() && isValidSelection());
691 setEnabled(canModifyEditor() && !getSelection().isEmpty());
695 * Returns if the current selection is valid, i.e. whether it is empty and the caret in the
696 * whitespace at the start of a line, or covers multiple lines.
698 * @return <code>true</code> if the selection is valid for an indent operation
700 private boolean isValidSelection() {
701 ITextSelection selection= getSelection();
702 if (selection.isEmpty())
705 int offset= selection.getOffset();
706 int length= selection.getLength();
708 IDocument document= getDocument();
709 if (document == null)
713 IRegion firstLine= document.getLineInformationOfOffset(offset);
714 int lineOffset= firstLine.getOffset();
716 // either the selection has to be empty and the caret in the WS at the line start
717 // or the selection has to extend over multiple lines
719 return document.get(lineOffset, offset - lineOffset).trim().length() == 0;
721 // return lineOffset + firstLine.getLength() < offset + length;
722 return false; // only enable for empty selections for now
724 } catch (BadLocationException e) {
731 * Returns the smart preference state.
733 * @return <code>true</code> if smart mode is on, <code>false</code> otherwise
735 private boolean isSmartMode() {
736 ITextEditor editor= getTextEditor();
738 if (editor instanceof ITextEditorExtension3)
739 return ((ITextEditorExtension3) editor).getInsertMode() == ITextEditorExtension3.SMART_INSERT;
745 * Returns the document currently displayed in the editor, or <code>null</code> if none can be
748 * @return the current document or <code>null</code>
750 private IDocument getDocument() {
752 ITextEditor editor= getTextEditor();
753 if (editor != null) {
755 IDocumentProvider provider= editor.getDocumentProvider();
756 IEditorInput input= editor.getEditorInput();
757 if (provider != null && input != null)
758 return provider.getDocument(input);
765 * Returns the selection on the editor or an invalid selection if none can be obtained. Returns
766 * never <code>null</code>.
768 * @return the current selection, never <code>null</code>
770 private ITextSelection getSelection() {
771 ISelectionProvider provider= getSelectionProvider();
772 if (provider != null) {
774 ISelection selection= provider.getSelection();
775 if (selection instanceof ITextSelection)
776 return (ITextSelection) selection;
780 return TextSelection.emptySelection();