/******************************************************************************* * Copyright (c) 2000, 2006 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 * Chris.Dennis@invidi.com - http://bugs.eclipse.org/bugs/show_bug.cgi?id=29027 *******************************************************************************/ package org.eclipse.ui.texteditor; import java.util.ResourceBundle; import org.eclipse.swt.custom.StyledText; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.source.ISourceViewer; /** * This action implements smart return. * Instead of breaking the line where we are, we do the following: * <p><b>Smart Enter</b> * <ul> * <li> if the caret is on a line containing any non-whitespace, a line is inserted below the * current one and the caret moved to it,</li> * <li> if the caret is on a whitespace-only line, a line is inserted below the current line, * but the caret stays in its position.</li> * </ul> * </p> * <p><b>Smart Enter Inverse</b> * <ul> * <li> if the caret is on a line containing any non-whitespace, we insert a line above the * current one and move the caret to it (i.e. it stays at the same offset in the widget),</li> * <li> if the caret is on a whitespace-only line, a line is inserted above the current line, * but the caret stays in its logical position (i.e., it gets shifted one line down in the * document, but keeps its position relative to the content following the caret).</li> * </ul> * </p> * @since 3.0 */ public class InsertLineAction extends TextEditorAction { /** * <code>true</code> if this action inserts a line above the current (Smart Enter Inverse), * <code>false</code> otherwise */ protected boolean fAbove; /** * Creates a new smart enter action. * @param bundle the resource bundle * @param prefix the prefix to use to get properties from <code>bundle</code> * @param textEditor the editor that the action acts upon * @param above whether new lines are inserted above or below the caret's line. */ public InsertLineAction(ResourceBundle bundle, String prefix, ITextEditor textEditor, boolean above) { super(bundle, prefix, textEditor); fAbove= above; } /* * @see org.eclipse.ui.texteditor.TextEditorAction#update() */ public void update() { super.update(); if (isEnabled()) setEnabled(canModifyEditor()); } /* * @see org.eclipse.jface.action.IAction#run() */ public void run() { /* * Implementation note: instead of computing any indentations needed * (which we can't at this generic level), we simply insert a new * line delimiter either at the end of the current line (normal) or * the end of the previous line (reverse). By operating directly on * the text widget, any auto-indent strategies can pick up on the * delimiter and perform any content-dependent modifications. */ ITextEditor ed= getTextEditor(); if (!(ed instanceof AbstractTextEditor)) return; if (!validateEditorInputState()) return; AbstractTextEditor editor= (AbstractTextEditor) ed; ISourceViewer sv= editor.getSourceViewer(); if (sv == null) return; IDocument document= sv.getDocument(); if (document == null) return; StyledText st= sv.getTextWidget(); if (st == null || st.isDisposed()) return; try { // get current line int widgetOffset= st.getCaretOffset(); int offset= AbstractTextEditor.widgetOffset2ModelOffset(sv, widgetOffset); int currentLineNumber= document.getLineOfOffset(offset); IRegion currentLine= document.getLineInformation(currentLineNumber); int insertionOffset= -1; if (fAbove) { if (currentLineNumber != 0) { IRegion previousLine= document.getLineInformation(currentLineNumber - 1); insertionOffset= previousLine.getOffset() + previousLine.getLength(); } } else { insertionOffset= currentLine.getOffset() + currentLine.getLength(); } boolean updateCaret= true; int widgetInsertionOffset= AbstractTextEditor.modelOffset2WidgetOffset(sv, insertionOffset); if (widgetInsertionOffset == -1 && fAbove) { // assume that the previous line was not accessible // (e.g. folded, or we are on line 0) // -> we insert the newline at the beginning of the current line, after any leading WS insertionOffset= currentLine.getOffset() + getIndentationLength(document, currentLine); widgetInsertionOffset= AbstractTextEditor.modelOffset2WidgetOffset(sv, insertionOffset); updateCaret= false; } if (widgetInsertionOffset == -1) return; // mark caret Position caret= new Position(insertionOffset, 0); document.addPosition(caret); st.setSelectionRange(widgetInsertionOffset, 0); // operate directly on the widget st.replaceTextRange(widgetInsertionOffset, 0, st.getLineDelimiter()); // restore caret unless an auto-indenter has already moved the caret // then leave it alone document.removePosition(caret); if (updateCaret && st.getSelection().x == widgetInsertionOffset) { int widgetCaret= AbstractTextEditor.modelOffset2WidgetOffset(sv, caret.getOffset()); if (widgetCaret != -1) st.setSelectionRange(widgetCaret, 0); st.showSelection(); } } catch (BadLocationException e) { // ignore } } /** * Computes the indentation length of a line. * * @param document the document * @param line the line * @return the number of whitespace characters at the beginning of * <code>line</code> * @throws BadLocationException on document access error */ private int getIndentationLength(IDocument document, IRegion line) throws BadLocationException { int pos= line.getOffset(); int max= pos + line.getLength(); while (pos < max) { if (!Character.isWhitespace(document.getChar(pos))) break; pos++; } return pos - line.getOffset(); } }