Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 67de4574c00571f972a4adb984cf55df555c894e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
/*******************************************************************************
 * 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;
	}

	@Override
	public void update() {
		super.update();
		if (isEnabled())
			setEnabled(canModifyEditor());
	}

	@Override
	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();
	}
}

Back to the top