001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.ui.actions;
011:
012: import java.util.ArrayList;
013: import java.util.Iterator;
014: import java.util.ResourceBundle;
015:
016: import org.eclipse.text.edits.MultiTextEdit;
017: import org.eclipse.text.edits.ReplaceEdit;
018: import org.eclipse.text.edits.TextEdit;
019:
020: import org.eclipse.core.runtime.Assert;
021: import org.eclipse.core.runtime.IStatus;
022: import org.eclipse.core.runtime.Status;
023:
024: import org.eclipse.swt.custom.BusyIndicator;
025: import org.eclipse.swt.widgets.Display;
026:
027: import org.eclipse.jface.viewers.ISelection;
028: import org.eclipse.jface.viewers.ISelectionProvider;
029:
030: import org.eclipse.jface.text.BadLocationException;
031: import org.eclipse.jface.text.IDocument;
032: import org.eclipse.jface.text.IRegion;
033: import org.eclipse.jface.text.IRewriteTarget;
034: import org.eclipse.jface.text.ITextSelection;
035: import org.eclipse.jface.text.ITypedRegion;
036: import org.eclipse.jface.text.Position;
037: import org.eclipse.jface.text.TextSelection;
038: import org.eclipse.jface.text.TextUtilities;
039: import org.eclipse.jface.text.source.ISourceViewer;
040:
041: import org.eclipse.ui.IEditorInput;
042: import org.eclipse.ui.texteditor.IDocumentProvider;
043: import org.eclipse.ui.texteditor.ITextEditor;
044: import org.eclipse.ui.texteditor.ITextEditorExtension3;
045: import org.eclipse.ui.texteditor.TextEditorAction;
046:
047: import org.eclipse.jdt.core.ICompilationUnit;
048: import org.eclipse.jdt.core.IJavaProject;
049: import org.eclipse.jdt.core.JavaCore;
050: import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
051:
052: import org.eclipse.jdt.ui.text.IJavaPartitions;
053:
054: import org.eclipse.jdt.internal.ui.JavaPlugin;
055: import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor;
056: import org.eclipse.jdt.internal.ui.text.JavaHeuristicScanner;
057: import org.eclipse.jdt.internal.ui.text.JavaIndenter;
058:
059: /**
060: * Indents a line or range of lines in a Java document to its correct position. No complete
061: * AST must be present, the indentation is computed using heuristics. The algorithm used is fast for
062: * single lines, but does not store any information and therefore not so efficient for large line
063: * ranges.
064: *
065: * @see org.eclipse.jdt.internal.ui.text.JavaHeuristicScanner
066: * @see org.eclipse.jdt.internal.ui.text.JavaIndenter
067: * @since 3.0
068: */
069: public class IndentAction extends TextEditorAction {
070:
071: /**
072: * @since 3.4
073: */
074: private static final class ReplaceData {
075:
076: /**
077: * The replacement
078: */
079: public final String indent;
080:
081: /**
082: * The start of the replacement
083: */
084: public final int offset;
085:
086: /**
087: * The end of the replacement
088: */
089: public final int end;
090:
091: /**
092: * Replace string in document from offset to end with indent
093: * @param offset the start of the replacement
094: * @param end the end of the replacement
095: * @param indent the replacement
096: */
097: public ReplaceData(int offset, int end, String indent) {
098: this .indent = indent;
099: this .end = end;
100: this .offset = offset;
101: }
102:
103: }
104:
105: /** The caret offset after an indent operation. */
106: private int fCaretOffset;
107:
108: /**
109: * Whether this is the action invoked by TAB. When <code>true</code>, indentation behaves
110: * differently to accommodate normal TAB operation.
111: */
112: private final boolean fIsTabAction;
113:
114: /**
115: * Creates a new instance.
116: *
117: * @param bundle the resource bundle
118: * @param prefix the prefix to use for keys in <code>bundle</code>
119: * @param editor the text editor
120: * @param isTabAction whether the action should insert tabs if over the indentation
121: */
122: public IndentAction(ResourceBundle bundle, String prefix,
123: ITextEditor editor, boolean isTabAction) {
124: super (bundle, prefix, editor);
125: fIsTabAction = isTabAction;
126: }
127:
128: /*
129: * @see org.eclipse.jface.action.Action#run()
130: */
131: public void run() {
132: // update has been called by the framework
133: if (!isEnabled() || !validateEditorInputState())
134: return;
135:
136: ITextSelection selection = getSelection();
137: final IDocument document = getDocument();
138:
139: if (document != null) {
140:
141: final int offset = selection.getOffset();
142: final int length = selection.getLength();
143: final Position end = new Position(offset + length);
144: final int firstLine, nLines;
145: fCaretOffset = -1;
146:
147: try {
148: document.addPosition(end);
149: firstLine = document.getLineOfOffset(offset);
150: // check for marginal (zero-length) lines
151: int minusOne = length == 0 ? 0 : 1;
152: nLines = document.getLineOfOffset(offset + length
153: - minusOne)
154: - firstLine + 1;
155: } catch (BadLocationException e) {
156: // will only happen on concurrent modification
157: JavaPlugin.log(new Status(IStatus.ERROR, JavaPlugin
158: .getPluginId(), IStatus.OK, "", e)); //$NON-NLS-1$
159: return;
160: }
161:
162: Runnable runnable = new Runnable() {
163: public void run() {
164: IRewriteTarget target = (IRewriteTarget) getTextEditor()
165: .getAdapter(IRewriteTarget.class);
166: if (target != null)
167: target.beginCompoundChange();
168:
169: try {
170: JavaHeuristicScanner scanner = new JavaHeuristicScanner(
171: document);
172: JavaIndenter indenter = new JavaIndenter(
173: document, scanner, getJavaProject());
174: final boolean multiLine = nLines > 1;
175: boolean hasChanged = false;
176: for (int i = 0; i < nLines; i++) {
177: hasChanged |= indentLine(document,
178: firstLine + i, offset, indenter,
179: scanner, multiLine);
180: }
181:
182: // update caret position: move to new position when indenting just one line
183: // keep selection when indenting multiple
184: int newOffset, newLength;
185: if (!fIsTabAction && multiLine) {
186: newOffset = offset;
187: newLength = end.getOffset() - offset;
188: } else {
189: newOffset = fCaretOffset;
190: newLength = 0;
191: }
192:
193: // always reset the selection if anything was replaced
194: // but not when we had a single line non-tab invocation
195: if (newOffset != -1
196: && (hasChanged || newOffset != offset || newLength != length))
197: selectAndReveal(newOffset, newLength);
198:
199: document.removePosition(end);
200: } catch (BadLocationException e) {
201: // will only happen on concurrent modification
202: JavaPlugin
203: .log(new Status(
204: IStatus.ERROR,
205: JavaPlugin.getPluginId(),
206: IStatus.OK,
207: "ConcurrentModification in IndentAction", e)); //$NON-NLS-1$
208:
209: } finally {
210: if (target != null)
211: target.endCompoundChange();
212: }
213: }
214: };
215:
216: if (nLines > 50) {
217: Display display = getTextEditor().getEditorSite()
218: .getWorkbenchWindow().getShell().getDisplay();
219: BusyIndicator.showWhile(display, runnable);
220: } else
221: runnable.run();
222:
223: }
224: }
225:
226: /**
227: * Selects the given range on the editor.
228: *
229: * @param newOffset the selection offset
230: * @param newLength the selection range
231: */
232: private void selectAndReveal(int newOffset, int newLength) {
233: Assert.isTrue(newOffset >= 0);
234: Assert.isTrue(newLength >= 0);
235: ITextEditor editor = getTextEditor();
236: if (editor instanceof JavaEditor) {
237: ISourceViewer viewer = ((JavaEditor) editor).getViewer();
238: if (viewer != null)
239: viewer.setSelectedRange(newOffset, newLength);
240: } else
241: // this is too intrusive, but will never get called anyway
242: getTextEditor().selectAndReveal(newOffset, newLength);
243:
244: }
245:
246: /**
247: * Indent the given <code>document</code> based on the <code>project</code> settings and
248: * return a text edit describing the changes applied to the document. Returns <b>null</b>
249: * if no changes have been applied.
250: * <p>
251: * WARNING: This method does change the content of the given document.
252: * </p>
253: * <p>
254: * This method is for internal use only, it should not be called.
255: * </p>
256: *
257: * @param document the document to indent
258: * @param project the project to retrieve the indentation settings from, <b>null</b> for workspace settings
259: * @return a text edit describing the changes or <b>null</b> if no changes required
260: * @throws BadLocationException if the document got modified concurrently
261: *
262: * @since 3.4
263: */
264: public static TextEdit indent(IDocument document,
265: IJavaProject project) throws BadLocationException {
266: JavaPlugin.getDefault().getJavaTextTools()
267: .setupJavaDocumentPartitioner(document,
268: IJavaPartitions.JAVA_PARTITIONING);
269:
270: int offset = 0;
271: int length = document.getLength();
272:
273: JavaHeuristicScanner scanner = new JavaHeuristicScanner(
274: document);
275: JavaIndenter indenter = new JavaIndenter(document, scanner,
276: project);
277:
278: ArrayList edits = new ArrayList();
279:
280: int firstLine = document.getLineOfOffset(offset);
281: // check for marginal (zero-length) lines
282: int minusOne = length == 0 ? 0 : 1;
283: int numberOfLines = document.getLineOfOffset(offset + length
284: - minusOne)
285: - firstLine + 1;
286:
287: int shift = 0;
288: for (int i = 0; i < numberOfLines; i++) {
289: ReplaceData data = computeReplaceData(document, firstLine
290: + i, indenter, scanner, numberOfLines > 1, false,
291: project);
292:
293: int replaceLength = data.end - data.offset;
294: String currentIndent = document.get(data.offset,
295: replaceLength);
296:
297: // only change the document if it is a real change
298: if (!data.indent.equals(currentIndent)) {
299: edits.add(new ReplaceEdit(data.offset + shift,
300: replaceLength, data.indent));
301: //We need to change the document, the indenter depends on it.
302: document.replace(data.offset, replaceLength,
303: data.indent);
304: shift -= data.indent.length() - replaceLength;
305: }
306: }
307:
308: if (edits.size() == 0)
309: return null;
310:
311: if (edits.size() == 1)
312: return (TextEdit) edits.get(0);
313:
314: MultiTextEdit result = new MultiTextEdit();
315: for (Iterator iterator = edits.iterator(); iterator.hasNext();) {
316: TextEdit edit = (TextEdit) iterator.next();
317: result.addChild(edit);
318: }
319:
320: return result;
321: }
322:
323: /**
324: * Indents a single line using the java heuristic scanner. Javadoc and multiline comments are
325: * indented as specified by the <code>JavaDocAutoIndentStrategy</code>.
326: *
327: * @param document the document
328: * @param line the line to be indented
329: * @param indenter the java indenter
330: * @param scanner the heuristic scanner
331: * @param multiLine <code>true</code> if more than one line is being indented
332: * @param isTabAction <code>true</code> if this action has been invoked by TAB
333: * @param project the project to retrieve the indentation settings from, <b>null</b> for workspace settings
334: * @return <code>true</code> if <code>document</code> was modified, <code>false</code> otherwise
335: * @throws BadLocationException if the document got changed concurrently
336: */
337: private static ReplaceData computeReplaceData(IDocument document,
338: int line, JavaIndenter indenter,
339: JavaHeuristicScanner scanner, boolean multiLine,
340: boolean isTabAction, IJavaProject project)
341: 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
345:
346: String indent = null;
347: if (offset < document.getLength()) {
348: ITypedRegion partition = TextUtilities.getPartition(
349: document, IJavaPartitions.JAVA_PARTITIONING,
350: offset, true);
351: ITypedRegion startingPartition = TextUtilities
352: .getPartition(document,
353: IJavaPartitions.JAVA_PARTITIONING, offset,
354: false);
355: String type = partition.getType();
356: if (type.equals(IJavaPartitions.JAVA_DOC)
357: || type
358: .equals(IJavaPartitions.JAVA_MULTI_LINE_COMMENT)) {
359: indent = computeJavadocIndent(document, line, scanner,
360: startingPartition);
361: } else if (!isTabAction
362: && startingPartition.getOffset() == offset
363: && startingPartition.getType().equals(
364: IJavaPartitions.JAVA_SINGLE_LINE_COMMENT)) {
365:
366: // line comment starting at position 0 -> indent inside
367: int max = document.getLength() - offset;
368: int slashes = 2;
369: while (slashes < max - 1
370: && document.get(offset + slashes, 2).equals(
371: "//")) //$NON-NLS-1$
372: slashes += 2;
373:
374: wsStart = offset + slashes;
375:
376: StringBuffer computed = indenter
377: .computeIndentation(offset);
378: if (computed == null)
379: computed = new StringBuffer(0);
380: int tabSize = getTabSize(project);
381: while (slashes > 0 && computed.length() > 0) {
382: char c = computed.charAt(0);
383: if (c == '\t')
384: if (slashes > tabSize)
385: slashes -= tabSize;
386: else
387: break;
388: else if (c == ' ')
389: slashes--;
390: else
391: break;
392:
393: computed.deleteCharAt(0);
394: }
395:
396: indent = document.get(offset, wsStart - offset)
397: + computed;
398:
399: }
400: }
401:
402: // standard java indentation
403: if (indent == null) {
404: StringBuffer computed = indenter.computeIndentation(offset);
405: if (computed != null)
406: indent = computed.toString();
407: else
408: indent = ""; //$NON-NLS-1$
409: }
410:
411: // change document:
412: // get current white space
413: int lineLength = currentLine.getLength();
414: int end = scanner.findNonWhitespaceForwardInAnyPartition(
415: wsStart, offset + lineLength);
416: if (end == JavaHeuristicScanner.NOT_FOUND) {
417: // an empty line
418: end = offset + lineLength;
419: if (multiLine && !indentEmptyLines(project))
420: indent = ""; //$NON-NLS-1$
421: }
422:
423: return new ReplaceData(offset, end, indent);
424: }
425:
426: /**
427: * Indents a single line using the java heuristic scanner. Javadoc and multiline comments are
428: * indented as specified by the <code>JavaDocAutoIndentStrategy</code>.
429: *
430: * @param document the document
431: * @param line the line to be indented
432: * @param caret the caret position
433: * @param indenter the java indenter
434: * @param scanner the heuristic scanner
435: * @param multiLine <code>true</code> if more than one line is being indented
436: * @return <code>true</code> if <code>document</code> was modified, <code>false</code> otherwise
437: * @throws BadLocationException if the document got changed concurrently
438: */
439: private boolean indentLine(IDocument document, int line, int caret,
440: JavaIndenter indenter, JavaHeuristicScanner scanner,
441: boolean multiLine) throws BadLocationException {
442: IJavaProject project = getJavaProject();
443: ReplaceData data = computeReplaceData(document, line, indenter,
444: scanner, multiLine, fIsTabAction, project);
445:
446: String indent = data.indent;
447: int end = data.end;
448: int offset = data.offset;
449:
450: int length = end - offset;
451: String currentIndent = document.get(offset, length);
452:
453: // if we are right before the text start / line end, and already after the insertion point
454: // then just insert a tab.
455: if (fIsTabAction
456: && caret == end
457: && whiteSpaceLength(currentIndent, project) >= whiteSpaceLength(
458: indent, project)) {
459: String tab = getTabEquivalent(project);
460: document.replace(caret, 0, tab);
461: fCaretOffset = caret + tab.length();
462: return true;
463: }
464:
465: // set the caret offset so it can be used when setting the selection
466: if (caret >= offset && caret <= end)
467: fCaretOffset = offset + indent.length();
468: else
469: fCaretOffset = -1;
470:
471: // only change the document if it is a real change
472: if (!indent.equals(currentIndent)) {
473: document.replace(offset, length, indent);
474: return true;
475: } else
476: return false;
477: }
478:
479: /**
480: * Computes and returns the indentation for a javadoc line. The line
481: * must be inside a javadoc comment.
482: *
483: * @param document the document
484: * @param line the line in document
485: * @param scanner the scanner
486: * @param partition the javadoc partition
487: * @return the indent, or <code>null</code> if not computable
488: * @throws BadLocationException
489: * @since 3.1
490: */
491: private static String computeJavadocIndent(IDocument document,
492: int line, JavaHeuristicScanner scanner,
493: ITypedRegion partition) throws BadLocationException {
494: if (line == 0) // impossible - the first line is never inside a javadoc comment
495: return null;
496:
497: // don't make any assumptions if the line does not start with \s*\* - it might be
498: // commented out code, for which we don't want to change the indent
499: final IRegion lineInfo = document.getLineInformation(line);
500: final int lineStart = lineInfo.getOffset();
501: final int lineLength = lineInfo.getLength();
502: final int lineEnd = lineStart + lineLength;
503: int nonWS = scanner.findNonWhitespaceForwardInAnyPartition(
504: lineStart, lineEnd);
505: if (nonWS == JavaHeuristicScanner.NOT_FOUND
506: || document.getChar(nonWS) != '*') {
507: if (nonWS == JavaHeuristicScanner.NOT_FOUND)
508: return document.get(lineStart, lineLength);
509: return document.get(lineStart, nonWS - lineStart);
510: }
511:
512: // take the indent from the previous line and reuse
513: IRegion previousLine = document.getLineInformation(line - 1);
514: int previousLineStart = previousLine.getOffset();
515: int previousLineLength = previousLine.getLength();
516: int previousLineEnd = previousLineStart + previousLineLength;
517:
518: StringBuffer buf = new StringBuffer();
519: int previousLineNonWS = scanner
520: .findNonWhitespaceForwardInAnyPartition(
521: previousLineStart, previousLineEnd);
522: if (previousLineNonWS == JavaHeuristicScanner.NOT_FOUND
523: || document.getChar(previousLineNonWS) != '*') {
524: // align with the comment start if the previous line is not an asterisked line
525: previousLine = document
526: .getLineInformationOfOffset(partition.getOffset());
527: previousLineStart = previousLine.getOffset();
528: previousLineLength = previousLine.getLength();
529: previousLineEnd = previousLineStart + previousLineLength;
530: previousLineNonWS = scanner
531: .findNonWhitespaceForwardInAnyPartition(
532: previousLineStart, previousLineEnd);
533: if (previousLineNonWS == JavaHeuristicScanner.NOT_FOUND)
534: previousLineNonWS = previousLineEnd;
535:
536: // add the initial space
537: // TODO this may be controlled by a formatter preference in the future
538: buf.append(' ');
539: }
540:
541: String indentation = document.get(previousLineStart,
542: previousLineNonWS - previousLineStart);
543: buf.insert(0, indentation);
544: return buf.toString();
545: }
546:
547: /**
548: * Returns the size in characters of a string. All characters count one, tabs count the editor's
549: * preference for the tab display
550: *
551: * @param indent the string to be measured.
552: * @param project the project to retrieve the indentation settings from, <b>null</b> for workspace settings
553: * @return the size in characters of a string
554: */
555: private static int whiteSpaceLength(String indent,
556: IJavaProject project) {
557: if (indent == null)
558: return 0;
559: else {
560: int size = 0;
561: int l = indent.length();
562: int tabSize = getTabSize(project);
563:
564: for (int i = 0; i < l; i++)
565: size += indent.charAt(i) == '\t' ? tabSize : 1;
566: return size;
567: }
568: }
569:
570: /**
571: * Returns a tab equivalent, either as a tab character or as spaces, depending on the editor and
572: * formatter preferences.
573: *
574: * @param project the project to retrieve the indentation settings from, <b>null</b> for workspace settings
575: * @return a string representing one tab in the editor, never <code>null</code>
576: */
577: private static String getTabEquivalent(IJavaProject project) {
578: String tab;
579: if (JavaCore.SPACE.equals(getCoreFormatterOption(
580: DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR,
581: project))) {
582: int size = getTabSize(project);
583: StringBuffer buf = new StringBuffer();
584: for (int i = 0; i < size; i++)
585: buf.append(' ');
586: tab = buf.toString();
587: } else
588: tab = "\t"; //$NON-NLS-1$
589:
590: return tab;
591: }
592:
593: /**
594: * Returns the tab size used by the java editor, which is deduced from the
595: * formatter preferences.
596: *
597: * @param project the project to retrieve the indentation settings from, <b>null</b> for workspace settings
598: * @return the tab size as defined in the current formatter preferences
599: */
600: private static int getTabSize(IJavaProject project) {
601: return getCoreFormatterOption(
602: DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE, 4,
603: project);
604: }
605:
606: /**
607: * Returns <code>true</code> if empty lines should be indented, false otherwise.
608: *
609: * @param project the project to retrieve the indentation settings from, <b>null</b> for workspace settings
610: * @return <code>true</code> if empty lines should be indented, false otherwise
611: * @since 3.2
612: */
613: private static boolean indentEmptyLines(IJavaProject project) {
614: return DefaultCodeFormatterConstants.TRUE
615: .equals(getCoreFormatterOption(
616: DefaultCodeFormatterConstants.FORMATTER_INDENT_EMPTY_LINES,
617: project));
618: }
619:
620: /**
621: * Returns the possibly project-specific core preference defined under <code>key</code>.
622: *
623: * @param key the key of the preference
624: * @param project the project to retrieve the indentation settings from, <b>null</b> for workspace settings
625: * @return the value of the preference
626: * @since 3.1
627: */
628: private static String getCoreFormatterOption(String key,
629: IJavaProject project) {
630: if (project == null)
631: return JavaCore.getOption(key);
632: return project.getOption(key, true);
633: }
634:
635: /**
636: * Returns the possibly project-specific core preference defined under <code>key</code>, or
637: * <code>def</code> if the value is not a integer.
638: *
639: * @param key the key of the preference
640: * @param def the default value
641: * @param project the project to retrieve the indentation settings from, <b>null</b> for workspace settings
642: * @return the value of the preference
643: * @since 3.1
644: */
645: private static int getCoreFormatterOption(String key, int def,
646: IJavaProject project) {
647: try {
648: return Integer
649: .parseInt(getCoreFormatterOption(key, project));
650: } catch (NumberFormatException e) {
651: return def;
652: }
653: }
654:
655: /**
656: * Returns the <code>IJavaProject</code> of the current editor input, or
657: * <code>null</code> if it cannot be found.
658: *
659: * @return the <code>IJavaProject</code> of the current editor input, or
660: * <code>null</code> if it cannot be found
661: * @since 3.1
662: */
663: private IJavaProject getJavaProject() {
664: ITextEditor editor = getTextEditor();
665: if (editor == null)
666: return null;
667:
668: ICompilationUnit cu = JavaPlugin.getDefault()
669: .getWorkingCopyManager().getWorkingCopy(
670: editor.getEditorInput());
671: if (cu == null)
672: return null;
673: return cu.getJavaProject();
674: }
675:
676: /**
677: * Returns the editor's selection provider.
678: *
679: * @return the editor's selection provider or <code>null</code>
680: */
681: private ISelectionProvider getSelectionProvider() {
682: ITextEditor editor = getTextEditor();
683: if (editor != null) {
684: return editor.getSelectionProvider();
685: }
686: return null;
687: }
688:
689: /*
690: * @see org.eclipse.ui.texteditor.IUpdate#update()
691: */
692: public void update() {
693: super .update();
694:
695: if (isEnabled())
696: if (fIsTabAction)
697: setEnabled(canModifyEditor() && isSmartMode()
698: && isValidSelection());
699: else
700: setEnabled(canModifyEditor()
701: && !getSelection().isEmpty());
702: }
703:
704: /**
705: * Returns if the current selection is valid, i.e. whether it is empty and the caret in the
706: * whitespace at the start of a line, or covers multiple lines.
707: *
708: * @return <code>true</code> if the selection is valid for an indent operation
709: */
710: private boolean isValidSelection() {
711: ITextSelection selection = getSelection();
712: if (selection.isEmpty())
713: return false;
714:
715: int offset = selection.getOffset();
716: int length = selection.getLength();
717:
718: IDocument document = getDocument();
719: if (document == null)
720: return false;
721:
722: try {
723: IRegion firstLine = document
724: .getLineInformationOfOffset(offset);
725: int lineOffset = firstLine.getOffset();
726:
727: // either the selection has to be empty and the caret in the WS at the line start
728: // or the selection has to extend over multiple lines
729: if (length == 0)
730: return document.get(lineOffset, offset - lineOffset)
731: .trim().length() == 0;
732: else
733: // return lineOffset + firstLine.getLength() < offset + length;
734: return false; // only enable for empty selections for now
735:
736: } catch (BadLocationException e) {
737: }
738:
739: return false;
740: }
741:
742: /**
743: * Returns the smart preference state.
744: *
745: * @return <code>true</code> if smart mode is on, <code>false</code> otherwise
746: */
747: private boolean isSmartMode() {
748: ITextEditor editor = getTextEditor();
749:
750: if (editor instanceof ITextEditorExtension3)
751: return ((ITextEditorExtension3) editor).getInsertMode() == ITextEditorExtension3.SMART_INSERT;
752:
753: return false;
754: }
755:
756: /**
757: * Returns the document currently displayed in the editor, or <code>null</code> if none can be
758: * obtained.
759: *
760: * @return the current document or <code>null</code>
761: */
762: private IDocument getDocument() {
763:
764: ITextEditor editor = getTextEditor();
765: if (editor != null) {
766:
767: IDocumentProvider provider = editor.getDocumentProvider();
768: IEditorInput input = editor.getEditorInput();
769: if (provider != null && input != null)
770: return provider.getDocument(input);
771:
772: }
773: return null;
774: }
775:
776: /**
777: * Returns the selection on the editor or an invalid selection if none can be obtained. Returns
778: * never <code>null</code>.
779: *
780: * @return the current selection, never <code>null</code>
781: */
782: private ITextSelection getSelection() {
783: ISelectionProvider provider = getSelectionProvider();
784: if (provider != null) {
785:
786: ISelection selection = provider.getSelection();
787: if (selection instanceof ITextSelection)
788: return (ITextSelection) selection;
789: }
790:
791: // null object
792: return TextSelection.emptySelection();
793: }
794:
795: }
|