Skip to main content
summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorAnton Leherbauer2006-07-17 14:26:49 +0000
committerAnton Leherbauer2006-07-17 14:26:49 +0000
commit51e04549cc68b71cb0188c639689175ee3f136ff (patch)
treeb18cd317d5ae602354c312cad510ef1cd7f614a4 /core
parente9c33c53c8ac51e374f082b35cbd5ca6948b6bd9 (diff)
downloadorg.eclipse.cdt-51e04549cc68b71cb0188c639689175ee3f136ff.tar.gz
org.eclipse.cdt-51e04549cc68b71cb0188c639689175ee3f136ff.tar.xz
org.eclipse.cdt-51e04549cc68b71cb0188c639689175ee3f136ff.zip
Patch from Sergey Prigogin for bug 140489 - Support "smart caret positioning" similar to Java
Diffstat (limited to 'core')
-rw-r--r--core/org.eclipse.cdt.ui.tests/META-INF/MANIFEST.MF3
-rw-r--r--core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/AutomatedSuite.java6
-rw-r--r--core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/BreakIteratorTest.java88
-rw-r--r--core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/CBreakIteratorTest.java122
-rw-r--r--core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/CWordIteratorTest.java123
-rw-r--r--core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF3
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java414
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/CEditorPreferencePage.java8
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties1
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CBreakIterator.java449
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CWordIterator.java239
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/DocumentCharacterIterator.java222
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/SequenceCharacterIterator.java166
13 files changed, 1841 insertions, 3 deletions
diff --git a/core/org.eclipse.cdt.ui.tests/META-INF/MANIFEST.MF b/core/org.eclipse.cdt.ui.tests/META-INF/MANIFEST.MF
index 815d3692a0f..a61944f81ba 100644
--- a/core/org.eclipse.cdt.ui.tests/META-INF/MANIFEST.MF
+++ b/core/org.eclipse.cdt.ui.tests/META-INF/MANIFEST.MF
@@ -32,6 +32,7 @@ Require-Bundle: org.eclipse.jface.text,
org.eclipse.compare,
org.eclipse.ui.console,
org.eclipse.core.expressions,
- org.eclipse.cdt.make.core
+ org.eclipse.cdt.make.core,
+ com.ibm.icu
Eclipse-LazyStart: true
Bundle-Vendor: Eclipse.org
diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/AutomatedSuite.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/AutomatedSuite.java
index cbab27a38a7..27fa89014f3 100644
--- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/AutomatedSuite.java
+++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/AutomatedSuite.java
@@ -15,6 +15,8 @@ import junit.framework.Test;
import junit.framework.TestSuite;
import org.eclipse.cdt.ui.tests.text.CAutoIndentTest;
+import org.eclipse.cdt.ui.tests.text.CBreakIteratorTest;
+import org.eclipse.cdt.ui.tests.text.CWordIteratorTest;
import org.eclipse.cdt.ui.tests.text.NumberRuleTest;
import org.eclipse.cdt.ui.tests.text.contentassist.CompletionFailedTest_MemberReference_Arrow_Prefix2;
import org.eclipse.cdt.ui.tests.text.contentassist.CompletionTest_ArgumentType_NoPrefix;
@@ -136,6 +138,10 @@ public class AutomatedSuite extends TestSuite {
addTest( CSelectionTestsDOMIndexer.suite() );
addTest( CPPSelectionTestsCTagsIndexer.suite() );
addTest( CSelectionTestsCTagsIndexer.suite() );
+
+ // Break iterator tests.
+ addTest(CBreakIteratorTest.suite());
+ addTest(CWordIteratorTest.suite());
}
}
diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/BreakIteratorTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/BreakIteratorTest.java
new file mode 100644
index 00000000000..352a0884f6e
--- /dev/null
+++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/BreakIteratorTest.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * 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
+ * Sergey Prigogin, Google
+ *******************************************************************************/
+package org.eclipse.cdt.ui.tests.text;
+
+import com.ibm.icu.text.BreakIterator;
+
+import junit.framework.TestCase;
+
+
+public class BreakIteratorTest extends TestCase {
+
+ protected BreakIterator fBreakIterator;
+
+ public void assertNextPositions(CharSequence ci, int position) {
+ assertNextPositions(ci, new int[] {position});
+ }
+
+ public void assertNextPositions(CharSequence ci, int[] positions) {
+ fBreakIterator.setText(ci.toString());
+
+ // test next()
+ for (int i = 0; i < positions.length; i++) {
+ int pos= fBreakIterator.next();
+ assertEquals(positions[i], pos);
+ }
+
+ // test following()
+ int idx= 0;
+ for (int i = 0; i < positions.length; i++) {
+ int position= positions[i];
+ while (idx < position) {
+ if (!illegalPos(ci, idx))
+ assertEquals(position, fBreakIterator.following(idx));
+ idx++;
+ }
+ }
+
+ }
+
+ /**
+ * Check if we are in a multibyte delimiter
+ * @param idx
+ * @return
+ */
+ private boolean illegalPos(CharSequence seq, int idx) {
+ String DELIMS= "\n\r";
+ if (idx == 0 || idx == seq.length())
+ return false;
+ char one= seq.charAt(idx - 1);
+ char two= seq.charAt(idx);
+ return one != two && DELIMS.indexOf(one) != -1 && DELIMS.indexOf(two) != -1;
+ }
+
+ public void assertPreviousPositions(CharSequence ci, int position) {
+ assertPreviousPositions(ci, new int[] {position});
+ }
+
+ public void assertPreviousPositions(CharSequence ci, int[] positions) {
+ fBreakIterator.setText(ci.toString());
+ fBreakIterator.last();
+
+ for (int i = positions.length - 1; i >= 0; i--) {
+ int pos= fBreakIterator.previous();
+ assertEquals(positions[i], pos);
+ }
+
+ // test preceding()
+ int idx= ci.length();
+ for (int i = positions.length - 1; i >= 0; i--) {
+ int position= positions[i];
+ while (idx > position) {
+ if (!illegalPos(ci, idx))
+ assertEquals(position, fBreakIterator.preceding(idx));
+ idx--;
+ }
+ }
+ }
+
+}
diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/CBreakIteratorTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/CBreakIteratorTest.java
new file mode 100644
index 00000000000..3ccd60ecb88
--- /dev/null
+++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/CBreakIteratorTest.java
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Sergey Prigogin, Google
+ *******************************************************************************/
+package org.eclipse.cdt.ui.tests.text;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.cdt.internal.ui.text.CBreakIterator;
+
+
+public class CBreakIteratorTest extends BreakIteratorTest {
+
+ public static Test suite() {
+ return new TestSuite(CBreakIteratorTest.class);
+ }
+
+ /*
+ * @see junit.framework.TestCase#setUp()
+ */
+ protected void setUp() throws Exception {
+ fBreakIterator= new CBreakIterator();
+ }
+
+ public void testNext1() {
+ assertNextPositions("word word", new int[] { 4, 5, 9 });
+ }
+
+ public void testNext2() {
+ assertNextPositions("wordWord word", new int[] { 4, 8, 9, 13 });
+ }
+
+ public void testNextSpace() {
+ assertNextPositions(" word ", new int[] { 1, 5, 6 });
+ }
+
+ public void testNextParen() {
+ assertNextPositions("word(params)", new int[] { 4, 5, 11, 12 });
+ }
+
+ public void testNextLn() {
+ String s= new String("word \n" +
+ " word2");
+ assertNextPositions(s, new int[] { 4, 5, 6, 8, 13 });
+ }
+
+ public void testMultiNextLn() {
+ String s= new String("word \n" +
+ "\n" +
+ "\n" +
+ " word2");
+ assertNextPositions(s, new int[] { 4, 5, 6, 7, 8, 10, 15 });
+ }
+
+ public void testMultiNextLn2() {
+ String s= new String("word \r\n" +
+ "\r\n" +
+ "\r\n" +
+ " word2");
+ assertNextPositions(s, new int[] { 4, 5, 7, 9, 11, 13, 18 });
+ }
+
+ public void testNextCamelCaseWord() {
+ String s= new String(" _isURLConnection_pool ");
+ assertNextPositions(s, new int[] { 3, 4, 6, 9, 20, 24, 27 });
+ }
+
+ public void testPrevious1() {
+ String s= new String("word word");
+ assertPreviousPositions(s, new int[] { 0, 4, 5 });
+ }
+
+ public void testPrevious2() {
+ String s= new String("wordWord word");
+ assertPreviousPositions(s, new int[] { 0, 4, 8, 9 });
+ }
+
+ public void testPreviousSpace() {
+ String s= new String(" word ");
+ assertPreviousPositions(s, new int[] { 1, 5 });
+ }
+
+ public void testPreviousParen() {
+ String s= new String("word(params)");
+ assertPreviousPositions(s, new int[] { 0, 4, 5, 11 });
+ }
+
+ public void testPreviousLn() {
+ String s= new String("word \n" +
+ " word2");
+ assertPreviousPositions(s, new int[] { 0, 4, 5, 6, 8 });
+ }
+
+ public void testMultiPreviousLn() {
+ String s= new String("word \n" +
+ "\n" +
+ "\n" +
+ " word2");
+ assertPreviousPositions(s, new int[] { 0, 4, 5, 6, 7, 8, 10 });
+ }
+
+ public void testMultiPreviousLn2() {
+ String s= new String("word \r\n" +
+ "\r\n" +
+ "\r\n" +
+ " word2");
+ assertPreviousPositions(s, new int[] { 0, 4, 5, 7, 9, 11, 13 });
+ }
+
+ public void testPreviousCamelCaseWord() {
+ String s= new String(" _isURLConnection_pool ");
+ assertPreviousPositions(s, new int[] { 0, 3, 4, 6, 9, 20, 24 });
+ }
+}
diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/CWordIteratorTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/CWordIteratorTest.java
new file mode 100644
index 00000000000..ba5e1123a7b
--- /dev/null
+++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/CWordIteratorTest.java
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ * Sergey Prigogin, Google
+******************************************************************************/
+package org.eclipse.cdt.ui.tests.text;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.cdt.internal.ui.text.CWordIterator;
+
+
+public class CWordIteratorTest extends BreakIteratorTest {
+
+ public static Test suite() {
+ return new TestSuite(CBreakIteratorTest.class);
+ }
+
+ /*
+ * @see junit.framework.TestCase#setUp()
+ */
+ protected void setUp() throws Exception {
+ fBreakIterator= new CWordIterator();
+ }
+
+ public void testNext1() {
+ assertNextPositions("word word", new int[] { 5, 9 });
+ }
+
+ public void testNext2() {
+ assertNextPositions("wordWord word", new int[] { 4, 9, 13 });
+ }
+
+ public void testNextSpace() {
+ assertNextPositions(" word ", new int[] { 1, 6 });
+ }
+
+ public void testNextParen() {
+ assertNextPositions("word(params)", new int[] { 4, 5, 11, 12 });
+ }
+
+ public void testNextLn() {
+ String s= new String("word \n" +
+ " word2");
+ assertNextPositions(s, new int[] { 5, 6, 8, 13 });
+ }
+
+ public void testMultiNextLn() {
+ String s= new String("word \n" +
+ "\n" +
+ "\n" +
+ " word2");
+ assertNextPositions(s, new int[] { 5, 6, 7, 8, 10, 15 });
+ }
+
+ public void testMultiNextLn2() {
+ String s= new String("word \r\n" +
+ "\r\n" +
+ "\r\n" +
+ " word2");
+ assertNextPositions(s, new int[] { 5, 7, 9, 11, 13, 18 });
+ }
+
+ public void testNextCamelCaseWord() {
+ String s= new String(" _isURLConnection_pool ");
+ assertNextPositions(s, new int[] { 3, 4, 6, 9, 20, 27 });
+ }
+
+ public void testPrevious1() {
+ String s= new String("word word");
+ assertPreviousPositions(s, new int[] { 0, 5 });
+ }
+
+ public void testPrevious2() {
+ String s= new String("wordWord word");
+ assertPreviousPositions(s, new int[] { 0, 4, 9 });
+ }
+
+ public void testPreviousSpace() {
+ String s= new String(" word ");
+ assertPreviousPositions(s, new int[] { 1 });
+ }
+
+ public void testPreviousParen() {
+ String s= new String("word(params)");
+ assertPreviousPositions(s, new int[] { 0, 4, 5, 11 });
+ }
+
+ public void testPreviousLn() {
+ String s= new String("word \n" +
+ " word2");
+ assertPreviousPositions(s, new int[] { 0, 5, 6, 8 });
+ }
+
+ public void testMultiPreviousLn() {
+ String s= new String("word \n" +
+ "\n" +
+ "\n" +
+ " word2");
+ assertPreviousPositions(s, new int[] { 0, 5, 6, 7, 8, 10 });
+ }
+
+ public void testMultiPreviousLn2() {
+ String s= new String("word \r\n" +
+ "\r\n" +
+ "\r\n" +
+ " word2");
+ assertPreviousPositions(s, new int[] { 0, 5, 7, 9, 11, 13 });
+ }
+
+ public void testPreviousCamelCaseWord() {
+ String s= new String(" _isURLConnection_pool ");
+ assertPreviousPositions(s, new int[] { 0, 3, 4, 6, 9, 20 });
+ }
+
+}
diff --git a/core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF b/core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF
index 95b291fbeb3..c069f9abbb5 100644
--- a/core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF
+++ b/core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF
@@ -69,6 +69,7 @@ Require-Bundle: org.eclipse.ui.ide,
org.eclipse.help,
org.eclipse.ui.navigator,
org.eclipse.core.expressions,
- org.eclipse.ui.navigator.resources
+ org.eclipse.ui.navigator.resources,
+ com.ibm.icu
Eclipse-LazyStart: true
Bundle-RequiredExecutionEnvironment: J2SE-1.4
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java
index afc860e111f..03f3ac7b135 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java
@@ -10,10 +10,12 @@
* QNX Software System
* Anton Leherbauer (Wind River Systems)
* Markus Schorn (Wind River Systems)
+ * Sergey Prigogin, Google
*******************************************************************************/
package org.eclipse.cdt.internal.ui.editor;
+import java.text.CharacterIterator;
import java.util.Iterator;
import java.util.ResourceBundle;
@@ -21,6 +23,7 @@ import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IStatusLineManager;
import org.eclipse.jface.preference.IPreferenceStore;
@@ -72,6 +75,7 @@ import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.search.ui.actions.TextSearchGroup;
import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ST;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSource;
@@ -104,12 +108,16 @@ import org.eclipse.ui.texteditor.IEditorStatusLine;
import org.eclipse.ui.texteditor.ITextEditorActionConstants;
import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
import org.eclipse.ui.texteditor.ITextEditorDropTargetListener;
+import org.eclipse.ui.texteditor.IUpdate;
import org.eclipse.ui.texteditor.ResourceAction;
import org.eclipse.ui.texteditor.SourceViewerDecorationSupport;
import org.eclipse.ui.texteditor.TextEditorAction;
+import org.eclipse.ui.texteditor.TextNavigationAction;
import org.eclipse.ui.texteditor.TextOperationAction;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
+import com.ibm.icu.text.BreakIterator;
+
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.CCorePreferenceConstants;
import org.eclipse.cdt.core.model.CModelException;
@@ -138,6 +146,8 @@ import org.eclipse.cdt.internal.ui.search.actions.SelectionSearchGroup;
import org.eclipse.cdt.internal.ui.text.CPairMatcher;
import org.eclipse.cdt.internal.ui.text.CSourceViewerConfiguration;
import org.eclipse.cdt.internal.ui.text.CTextTools;
+import org.eclipse.cdt.internal.ui.text.CWordIterator;
+import org.eclipse.cdt.internal.ui.text.DocumentCharacterIterator;
import org.eclipse.cdt.internal.ui.text.HTMLTextPresenter;
import org.eclipse.cdt.internal.ui.text.ICPartitions;
import org.eclipse.cdt.internal.ui.text.c.hover.SourceViewerInformationControl;
@@ -512,6 +522,8 @@ public class CEditor extends TextEditor implements ISelectionChangedListener, IS
/** Listener to annotation model changes that updates the error tick in the tab image */
private CEditorErrorTickUpdater fCEditorErrorTickUpdater;
+ /** Preference key for sub-word navigation, aka smart caret positioning */
+ public final static String SUB_WORD_NAVIGATION= "subWordNavigation"; //$NON-NLS-1$
/** Preference key for matching brackets */
public final static String MATCHING_BRACKETS = "matchingBrackets"; //$NON-NLS-1$
/** Preference key for matching brackets color */
@@ -1396,6 +1408,47 @@ public class CEditor extends TextEditor implements ISelectionChangedListener, IS
return store.getBoolean(SPACES_FOR_TABS);
}
+ /*
+ * @see org.eclipse.ui.texteditor.AbstractTextEditor#createNavigationActions()
+ */
+ protected void createNavigationActions() {
+ super.createNavigationActions();
+
+ final StyledText textWidget = getSourceViewer().getTextWidget();
+
+ IAction action = new NavigatePreviousSubWordAction();
+ action.setActionDefinitionId(ITextEditorActionDefinitionIds.WORD_PREVIOUS);
+ setAction(ITextEditorActionDefinitionIds.WORD_PREVIOUS, action);
+ textWidget.setKeyBinding(SWT.CTRL | SWT.ARROW_LEFT, SWT.NULL);
+
+ action = new NavigateNextSubWordAction();
+ action.setActionDefinitionId(ITextEditorActionDefinitionIds.WORD_NEXT);
+ setAction(ITextEditorActionDefinitionIds.WORD_NEXT, action);
+ textWidget.setKeyBinding(SWT.CTRL | SWT.ARROW_RIGHT, SWT.NULL);
+
+ action = new SelectPreviousSubWordAction();
+ action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_WORD_PREVIOUS);
+ setAction(ITextEditorActionDefinitionIds.SELECT_WORD_PREVIOUS, action);
+ textWidget.setKeyBinding(SWT.CTRL | SWT.SHIFT | SWT.ARROW_LEFT, SWT.NULL);
+
+ action = new SelectNextSubWordAction();
+ action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_WORD_NEXT);
+ setAction(ITextEditorActionDefinitionIds.SELECT_WORD_NEXT, action);
+ textWidget.setKeyBinding(SWT.CTRL | SWT.SHIFT | SWT.ARROW_RIGHT, SWT.NULL);
+
+ 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);
+ }
+
interface ITextConverter {
void customizeDocumentCommand(IDocument document, DocumentCommand command);
}
@@ -1669,4 +1722,365 @@ public class CEditor extends TextEditor implements ISelectionChangedListener, IS
System.arraycopy(parentPrefPageIds, 0, prefPageIds, nIds, parentPrefPageIds.length);
return prefPageIds;
}
+
+ /**
+ * Text navigation action to navigate to the next sub-word.
+ *
+ * @since 4.0
+ */
+ protected abstract class NextSubWordAction extends TextNavigationAction {
+
+ protected CWordIterator fIterator = new CWordIterator();
+
+ /**
+ * Creates a new next sub-word action.
+ *
+ * @param code Action code for the default operation. Must be an action code from @see org.eclipse.swt.custom.ST.
+ */
+ protected NextSubWordAction(int code) {
+ super(getSourceViewer().getTextWidget(), code);
+ }
+
+ /*
+ * @see org.eclipse.jface.action.IAction#run()
+ */
+ public void run() {
+ // Check whether sub word navigation is enabled.
+ final IPreferenceStore store = getPreferenceStore();
+ if (!store.getBoolean(SUB_WORD_NAVIGATION)) {
+ super.run();
+ return;
+ }
+
+ final ISourceViewer viewer = getSourceViewer();
+ final IDocument document = viewer.getDocument();
+ fIterator.setText((CharacterIterator) new DocumentCharacterIterator(document));
+ int position = widgetOffset2ModelOffset(viewer, viewer.getTextWidget().getCaretOffset());
+ if (position == -1)
+ return;
+
+ int next = findNextPosition(position);
+ if (next != BreakIterator.DONE) {
+ setCaretPosition(next);
+ getTextWidget().showSelection();
+ fireSelectionChanged();
+ }
+
+ }
+
+ /**
+ * Finds the next position after the given position.
+ *
+ * @param position the current position
+ * @return the next position
+ */
+ protected int findNextPosition(int position) {
+ ISourceViewer viewer = getSourceViewer();
+ int widget = -1;
+ while (position != BreakIterator.DONE && widget == -1) { // TODO: optimize
+ position = fIterator.following(position);
+ if (position != BreakIterator.DONE)
+ widget = modelOffset2WidgetOffset(viewer, position);
+ }
+ return position;
+ }
+
+ /**
+ * Sets the caret position to the sub-word boundary given with <code>position</code>.
+ *
+ * @param position Position where the action should move the caret
+ */
+ protected abstract void setCaretPosition(int position);
+ }
+
+ /**
+ * Text navigation action to navigate to the next sub-word.
+ *
+ * @since 4.0
+ */
+ protected class NavigateNextSubWordAction extends NextSubWordAction {
+
+ /**
+ * Creates a new navigate next sub-word action.
+ */
+ public NavigateNextSubWordAction() {
+ super(ST.WORD_NEXT);
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.editor.CEditor.NextSubWordAction#setCaretPosition(int)
+ */
+ protected void setCaretPosition(final int position) {
+ getTextWidget().setCaretOffset(modelOffset2WidgetOffset(getSourceViewer(), position));
+ }
+ }
+
+ /**
+ * Text operation action to delete the next sub-word.
+ *
+ * @since 4.0
+ */
+ protected class DeleteNextSubWordAction extends NextSubWordAction implements IUpdate {
+
+ /**
+ * Creates a new delete next sub-word action.
+ */
+ public DeleteNextSubWordAction() {
+ super(ST.DELETE_WORD_NEXT);
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.editor.CEditor.NextSubWordAction#setCaretPosition(int)
+ */
+ protected void setCaretPosition(final int position) {
+ if (!validateEditorInputState())
+ return;
+
+ final ISourceViewer viewer = getSourceViewer();
+ final int caret, length;
+ Point selection = viewer.getSelectedRange();
+ if (selection.y != 0) {
+ caret = selection.x;
+ length = selection.y;
+ } else {
+ caret = widgetOffset2ModelOffset(viewer, viewer.getTextWidget().getCaretOffset());
+ length = position - caret;
+ }
+
+ try {
+ viewer.getDocument().replace(caret, length, ""); //$NON-NLS-1$
+ } catch (BadLocationException exception) {
+ // Should not happen
+ }
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.editor.CEditor.NextSubWordAction#findNextPosition(int)
+ */
+ protected int findNextPosition(int position) {
+ return fIterator.following(position);
+ }
+
+ /*
+ * @see org.eclipse.ui.texteditor.IUpdate#update()
+ */
+ public void update() {
+ setEnabled(isEditorInputModifiable());
+ }
+ }
+
+ /**
+ * Text operation action to select the next sub-word.
+ *
+ * @since 4.0
+ */
+ protected class SelectNextSubWordAction extends NextSubWordAction {
+
+ /**
+ * Creates a new select next sub-word action.
+ */
+ public SelectNextSubWordAction() {
+ super(ST.SELECT_WORD_NEXT);
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.editor.CEditor.NextSubWordAction#setCaretPosition(int)
+ */
+ protected void setCaretPosition(final int position) {
+ final ISourceViewer viewer = getSourceViewer();
+
+ final StyledText text = viewer.getTextWidget();
+ if (text != null && !text.isDisposed()) {
+
+ final Point selection = text.getSelection();
+ final int caret = text.getCaretOffset();
+ final int offset = modelOffset2WidgetOffset(viewer, position);
+
+ if (caret == selection.x)
+ text.setSelectionRange(selection.y, offset - selection.y);
+ else
+ text.setSelectionRange(selection.x, offset - selection.x);
+ }
+ }
+ }
+
+ /**
+ * Text navigation action to navigate to the previous sub-word.
+ *
+ * @since 4.0
+ */
+ protected abstract class PreviousSubWordAction extends TextNavigationAction {
+
+ protected CWordIterator fIterator = new CWordIterator();
+
+ /**
+ * Creates a new previous sub-word action.
+ *
+ * @param code Action code for the default operation. Must be an action code from @see org.eclipse.swt.custom.ST.
+ */
+ protected PreviousSubWordAction(final int code) {
+ super(getSourceViewer().getTextWidget(), code);
+ }
+
+ /*
+ * @see org.eclipse.jface.action.IAction#run()
+ */
+ public void run() {
+ // Check whether sub word navigation is enabled.
+ final IPreferenceStore store = getPreferenceStore();
+ if (!store.getBoolean(SUB_WORD_NAVIGATION)) {
+ super.run();
+ return;
+ }
+
+ final ISourceViewer viewer = getSourceViewer();
+ final IDocument document = viewer.getDocument();
+ fIterator.setText((CharacterIterator) new DocumentCharacterIterator(document));
+ int position = widgetOffset2ModelOffset(viewer, viewer.getTextWidget().getCaretOffset());
+ if (position == -1)
+ return;
+
+ int previous = findPreviousPosition(position);
+ if (previous != BreakIterator.DONE) {
+ setCaretPosition(previous);
+ getTextWidget().showSelection();
+ fireSelectionChanged();
+ }
+
+ }
+
+ /**
+ * Finds the previous position before the given position.
+ *
+ * @param position the current position
+ * @return the previous position
+ */
+ protected int findPreviousPosition(int position) {
+ ISourceViewer viewer = getSourceViewer();
+ int widget = -1;
+ while (position != BreakIterator.DONE && widget == -1) { // TODO: optimize
+ position = fIterator.preceding(position);
+ if (position != BreakIterator.DONE)
+ widget = modelOffset2WidgetOffset(viewer, position);
+ }
+ return position;
+ }
+
+ /**
+ * Sets the caret position to the sub-word boundary given with <code>position</code>.
+ *
+ * @param position Position where the action should move the caret
+ */
+ protected abstract void setCaretPosition(int position);
+ }
+
+ /**
+ * Text navigation action to navigate to the previous sub-word.
+ *
+ * @since 4.0
+ */
+ protected class NavigatePreviousSubWordAction extends PreviousSubWordAction {
+
+ /**
+ * Creates a new navigate previous sub-word action.
+ */
+ public NavigatePreviousSubWordAction() {
+ super(ST.WORD_PREVIOUS);
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.editor.CEditor.PreviousSubWordAction#setCaretPosition(int)
+ */
+ protected void setCaretPosition(final int position) {
+ getTextWidget().setCaretOffset(modelOffset2WidgetOffset(getSourceViewer(), position));
+ }
+ }
+
+ /**
+ * Text operation action to delete the previous sub-word.
+ *
+ * @since 4.0
+ */
+ protected class DeletePreviousSubWordAction extends PreviousSubWordAction implements IUpdate {
+
+ /**
+ * Creates a new delete previous sub-word action.
+ */
+ public DeletePreviousSubWordAction() {
+ super(ST.DELETE_WORD_PREVIOUS);
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.editor.CEditor.PreviousSubWordAction#setCaretPosition(int)
+ */
+ protected void setCaretPosition(int position) {
+ if (!validateEditorInputState())
+ return;
+
+ final int length;
+ final ISourceViewer viewer = getSourceViewer();
+ Point selection = viewer.getSelectedRange();
+ if (selection.y != 0) {
+ position = selection.x;
+ length = selection.y;
+ } else {
+ length = widgetOffset2ModelOffset(viewer, viewer.getTextWidget().getCaretOffset()) - position;
+ }
+
+ try {
+ viewer.getDocument().replace(position, length, ""); //$NON-NLS-1$
+ } catch (BadLocationException exception) {
+ // Should not happen
+ }
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.editor.CEditor.PreviousSubWordAction#findPreviousPosition(int)
+ */
+ protected int findPreviousPosition(int position) {
+ return fIterator.preceding(position);
+ }
+
+ /*
+ * @see org.eclipse.ui.texteditor.IUpdate#update()
+ */
+ public void update() {
+ setEnabled(isEditorInputModifiable());
+ }
+ }
+
+ /**
+ * Text operation action to select the previous sub-word.
+ *
+ * @since 4.0
+ */
+ protected class SelectPreviousSubWordAction extends PreviousSubWordAction {
+
+ /**
+ * Creates a new select previous sub-word action.
+ */
+ public SelectPreviousSubWordAction() {
+ super(ST.SELECT_WORD_PREVIOUS);
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.editor.CEditor.PreviousSubWordAction#setCaretPosition(int)
+ */
+ protected void setCaretPosition(final int position) {
+ final ISourceViewer viewer = getSourceViewer();
+
+ final StyledText text = viewer.getTextWidget();
+ if (text != null && !text.isDisposed()) {
+
+ final Point selection = text.getSelection();
+ final int caret = text.getCaretOffset();
+ final int offset = modelOffset2WidgetOffset(viewer, position);
+
+ if (caret == selection.x)
+ text.setSelectionRange(selection.y, offset - selection.y);
+ else
+ text.setSelectionRange(selection.x, offset - selection.x);
+ }
+ }
+ }
}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/CEditorPreferencePage.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/CEditorPreferencePage.java
index ee4d593b5d2..a946b131191 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/CEditorPreferencePage.java
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/CEditorPreferencePage.java
@@ -120,6 +120,7 @@ public class CEditorPreferencePage extends AbstractPreferencePage implements IWo
overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, ICColorConstants.C_NUMBER + "_bold")); //$NON-NLS-1$
overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.STRING, ICColorConstants.C_OPERATOR));
overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, ICColorConstants.C_OPERATOR + "_bold")); //$NON-NLS-1$
+ overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, CEditor.SUB_WORD_NAVIGATION));
overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.STRING, CEditor.MATCHING_BRACKETS_COLOR));
overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, CEditor.MATCHING_BRACKETS));
overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.STRING, CEditor.INACTIVE_CODE_COLOR));
@@ -149,6 +150,8 @@ public class CEditorPreferencePage extends AbstractPreferencePage implements IWo
// JDT also enables this feature.
store.setDefault(AbstractTextEditor.PREFERENCE_NAVIGATION_SMART_HOME_END, true);
+ store.setDefault(CEditor.SUB_WORD_NAVIGATION, true);
+
store.setDefault(CEditor.MATCHING_BRACKETS, true);
PreferenceConverter.setDefault(store, CEditor.MATCHING_BRACKETS_COLOR, new RGB(170,170,170));
@@ -338,7 +341,10 @@ public class CEditorPreferencePage extends AbstractPreferencePage implements IWo
layout.numColumns = 2;
behaviorComposite.setLayout(layout);
- String label = PreferencesMessages.getString("CEditorPreferencePage.behaviorPage.matchingBrackets"); //$NON-NLS-1$
+ String label= PreferencesMessages.getString("CEditorPreferencePage.behaviorPage.subWordNavigation"); //$NON-NLS-1$;
+ addCheckBox(behaviorComposite, label, CEditor.SUB_WORD_NAVIGATION, 0);
+
+ label = PreferencesMessages.getString("CEditorPreferencePage.behaviorPage.matchingBrackets"); //$NON-NLS-1$
addCheckBox(behaviorComposite, label, CEditor.MATCHING_BRACKETS, 0);
label = PreferencesMessages.getString("CEditorPreferencePage.behaviorPage.inactiveCode"); //$NON-NLS-1$
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties
index ce3ebbd9692..d109638c9ad 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties
@@ -108,6 +108,7 @@ CEditorPreferencePage.colorPage.bold=&Bold
CEditorPreferencePage.colorPage.preview=Preview:
CEditorPreferencePage.behaviorPage.tabSpace=&Insert space for tabs
CEditorPreferencePage.behaviorPage.matchingBrackets=Highlight &matching brackets
+CEditorPreferencePage.behaviorPage.subWordNavigation=Smart &caret positioning in identifiers
CEditorPreferencePage.behaviorPage.inactiveCode=Highlight &inactive code
CEditorPreferencePage.behaviorPage.appearanceColorOptions=Appearance color options:
CEditorPreferencePage.behaviorPage.matchingBracketColor=Matching brackets highlight
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CBreakIterator.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CBreakIterator.java
new file mode 100644
index 00000000000..b9f62bb8d8b
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CBreakIterator.java
@@ -0,0 +1,449 @@
+/*******************************************************************************
+ * 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
+ * Sergey Prigogin, Google
+ *******************************************************************************/
+package org.eclipse.cdt.internal.ui.text;
+
+import com.ibm.icu.text.BreakIterator;
+import java.text.CharacterIterator;
+
+import org.eclipse.jface.text.Assert;
+
+
+/**
+ * A C break iterator. It returns all breaks, including before and after
+ * whitespace, and it returns all camel case breaks.
+ * <p>
+ * A line break may be any of "\n", "\r", "\r\n", "\n\r".
+ * </p>
+ *
+ * @since 4.0
+ */
+public class CBreakIterator extends BreakIterator {
+
+ /**
+ * A run of common characters.
+ */
+ protected static abstract class Run {
+ /** The length of this run. */
+ protected int length;
+
+ public Run() {
+ init();
+ }
+
+ /**
+ * Returns <code>true</code> if this run consumes <code>ch</code>,
+ * <code>false</code> otherwise. If <code>true</code> is returned,
+ * the length of the receiver is adjusted accordingly.
+ *
+ * @param ch the character to test
+ * @return <code>true</code> if <code>ch</code> was consumed
+ */
+ protected boolean consume(char ch) {
+ if (isValid(ch)) {
+ length++;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Whether this run accepts that character; does not update state. Called
+ * from the default implementation of <code>consume</code>.
+ *
+ * @param ch the character to test
+ * @return <code>true</code> if <code>ch</code> is accepted
+ */
+ protected abstract boolean isValid(char ch);
+
+ /**
+ * Resets this run to the initial state.
+ */
+ protected void init() {
+ length= 0;
+ }
+ }
+
+ static final class Whitespace extends Run {
+ protected boolean isValid(char ch) {
+ return Character.isWhitespace(ch) && ch != '\n' && ch != '\r';
+ }
+ }
+
+ static final class LineDelimiter extends Run {
+ /** State: INIT -> delimiter -> EXIT. */
+ private char fState;
+ private static final char INIT= '\0';
+ private static final char EXIT= '\1';
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.CBreakIterator.Run#init()
+ */
+ protected void init() {
+ super.init();
+ fState= INIT;
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.CBreakIterator.Run#consume(char)
+ */
+ protected boolean consume(char ch) {
+ if (!isValid(ch) || fState == EXIT)
+ return false;
+
+ if (fState == INIT) {
+ fState= ch;
+ length++;
+ return true;
+ } else if (fState != ch) {
+ fState= EXIT;
+ length++;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ protected boolean isValid(char ch) {
+ return ch == '\n' || ch == '\r';
+ }
+ }
+
+ static final class Identifier extends Run {
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.CBreakIterator.Run#isValid(char)
+ */
+ protected boolean isValid(char ch) {
+ return Character.isJavaIdentifierPart(ch);
+ }
+ }
+
+ static final class CamelCaseIdentifier extends Run {
+ /* states */
+ private static final int S_INIT= 0;
+ private static final int S_LOWER= 1;
+ private static final int S_ONE_CAP= 2;
+ private static final int S_ALL_CAPS= 3;
+ private static final int S_UNDERSCORE= 4;
+ private static final int S_EXIT= 5;
+ private static final int S_EXIT_MINUS_ONE= 6;
+
+ /* character types */
+ private static final int K_INVALID= 0;
+ private static final int K_LOWER= 1;
+ private static final int K_UPPER= 2;
+ private static final int K_UNDERSCORE= 3;
+ private static final int K_OTHER= 4;
+
+ private int fState;
+
+ private final static int[][] MATRIX= new int[][] {
+ // K_INVALID, K_LOWER, K_UPPER, K_UNDERSCORE, K_OTHER
+ { S_EXIT, S_LOWER, S_ONE_CAP, S_UNDERSCORE, S_LOWER }, // S_INIT
+ { S_EXIT, S_LOWER, S_EXIT, S_UNDERSCORE, S_LOWER }, // S_LOWER
+ { S_EXIT, S_LOWER, S_ALL_CAPS, S_UNDERSCORE, S_LOWER }, // S_ONE_CAP
+ { S_EXIT, S_EXIT_MINUS_ONE, S_ALL_CAPS, S_UNDERSCORE, S_LOWER }, // S_ALL_CAPS
+ { S_EXIT, S_EXIT, S_EXIT, S_UNDERSCORE, S_EXIT }, // S_UNDERSCORE
+ };
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.CBreakIterator.Run#init()
+ */
+ protected void init() {
+ super.init();
+ fState= S_INIT;
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.CBreakIterator.Run#consumes(char)
+ */
+ protected boolean consume(char ch) {
+ int kind= getKind(ch);
+ fState= MATRIX[fState][kind];
+ switch (fState) {
+ case S_LOWER:
+ case S_ONE_CAP:
+ case S_ALL_CAPS:
+ case S_UNDERSCORE:
+ length++;
+ return true;
+ case S_EXIT:
+ return false;
+ case S_EXIT_MINUS_ONE:
+ length--;
+ return false;
+ default:
+ Assert.isTrue(false);
+ return false;
+ }
+ }
+
+ /**
+ * Determines the kind of a character.
+ *
+ * @param ch the character to test
+ */
+ private int getKind(char ch) {
+ if (Character.isUpperCase(ch))
+ return K_UPPER;
+ if (Character.isLowerCase(ch))
+ return K_LOWER;
+ if (ch == '_')
+ return K_UNDERSCORE;
+ if (Character.isJavaIdentifierPart(ch)) // digits...
+ return K_OTHER;
+ return K_INVALID;
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.CBreakIterator.Run#isValid(char)
+ */
+ protected boolean isValid(char ch) {
+ return Character.isJavaIdentifierPart(ch);
+ }
+ }
+
+ static final class Other extends Run {
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.CBreakIterator.Run#isValid(char)
+ */
+ protected boolean isValid(char ch) {
+ return !Character.isWhitespace(ch) && !Character.isJavaIdentifierPart(ch);
+ }
+ }
+
+ private static final Run WHITESPACE= new Whitespace();
+ private static final Run DELIMITER= new LineDelimiter();
+ private static final Run IDENTIFIER= new Identifier();
+ private static final Run CAMELCASE= new CamelCaseIdentifier();
+ private static final Run OTHER= new Other();
+
+ /** The platform break iterator (word instance) used as a base. */
+ protected final BreakIterator fIterator;
+ /** The text we operate on. */
+ protected CharSequence fText;
+ /** our current position for the stateful methods. */
+ private int fIndex;
+ /** Break on camel case word boundaries */
+ private boolean fCamelCaseBreakEnabled = true;
+
+
+ /**
+ * Creates a new break iterator.
+ */
+ public CBreakIterator() {
+ fIterator= BreakIterator.getWordInstance();
+ fIndex= fIterator.current();
+ }
+
+ /*
+ * @see com.ibm.icu.text.BreakIterator#current()
+ */
+ public int current() {
+ return fIndex;
+ }
+
+ /*
+ * @see com.ibm.icu.text.BreakIterator#first()
+ */
+ public int first() {
+ fIndex= fIterator.first();
+ return fIndex;
+ }
+
+ /*
+ * @see com.ibm.icu.text.BreakIterator#following(int)
+ */
+ public int following(int offset) {
+ // work around too eager IAEs in standard implementation
+ if (offset == getText().getEndIndex())
+ return DONE;
+
+ int next= fIterator.following(offset);
+ if (next == DONE)
+ return DONE;
+
+ // TODO deal with complex script word boundaries
+ // Math.min(offset + run.length, next) does not work
+ // since BreakIterator.getWordInstance considers _ as boundaries
+ // seems to work fine, however
+ Run run= consumeRun(offset);
+ return offset + run.length;
+
+ }
+
+ /**
+ * Consumes a run of characters at the limits of which we introduce a break.
+ * @param offset the offset to start at
+ * @return the run that was consumed
+ */
+ private Run consumeRun(int offset) {
+ // assert offset < length
+
+ char ch= fText.charAt(offset);
+ int length= fText.length();
+ Run run= getRun(ch);
+ while (run.consume(ch) && offset < length - 1) {
+ offset++;
+ ch= fText.charAt(offset);
+ }
+
+ return run;
+ }
+
+ /**
+ * Returns a run based on a character.
+ *
+ * @param ch the character to test
+ * @return the correct character given <code>ch</code>
+ */
+ private Run getRun(char ch) {
+ Run run;
+ if (WHITESPACE.isValid(ch))
+ run= WHITESPACE;
+ else if (DELIMITER.isValid(ch))
+ run= DELIMITER;
+ else if (IDENTIFIER.isValid(ch)) {
+ if (fCamelCaseBreakEnabled)
+ run= CAMELCASE;
+ else
+ run= IDENTIFIER;
+ }
+ else if (OTHER.isValid(ch))
+ run= OTHER;
+ else {
+ Assert.isTrue(false);
+ return null;
+ }
+
+ run.init();
+ return run;
+ }
+
+ /*
+ * @see com.ibm.icu.text.BreakIterator#getText()
+ */
+ public CharacterIterator getText() {
+ return fIterator.getText();
+ }
+
+ /*
+ * @see com.ibm.icu.text.BreakIterator#isBoundary(int)
+ */
+ public boolean isBoundary(int offset) {
+ if (offset == getText().getBeginIndex())
+ return true;
+ else
+ return following(offset - 1) == offset;
+ }
+
+ /*
+ * @see com.ibm.icu.text.BreakIterator#last()
+ */
+ public int last() {
+ fIndex= fIterator.last();
+ return fIndex;
+ }
+
+ /*
+ * @see com.ibm.icu.text.BreakIterator#next()
+ */
+ public int next() {
+ fIndex= following(fIndex);
+ return fIndex;
+ }
+
+ /*
+ * @see com.ibm.icu.text.BreakIterator#next(int)
+ */
+ public int next(int n) {
+ return fIterator.next(n);
+ }
+
+ /*
+ * @see com.ibm.icu.text.BreakIterator#preceding(int)
+ */
+ public int preceding(int offset) {
+ if (offset == getText().getBeginIndex())
+ return DONE;
+
+ if (isBoundary(offset - 1))
+ return offset - 1;
+
+ int previous= offset - 1;
+ do {
+ previous= fIterator.preceding(previous);
+ } while (!isBoundary(previous));
+
+ int last= DONE;
+ while (previous < offset) {
+ last= previous;
+ previous= following(previous);
+ }
+
+ return last;
+ }
+
+ /*
+ * @see com.ibm.icu.text.BreakIterator#previous()
+ */
+ public int previous() {
+ fIndex= preceding(fIndex);
+ return fIndex;
+ }
+
+ /*
+ * @see com.ibm.icu.text.BreakIterator#setText(java.lang.String)
+ */
+ public void setText(String newText) {
+ setText((CharSequence) newText);
+ }
+
+ /**
+ * Creates a break iterator given a char sequence.
+ * @param newText the new text
+ */
+ public void setText(CharSequence newText) {
+ fText= newText;
+ fIterator.setText(new SequenceCharacterIterator(newText));
+ first();
+ }
+
+ /*
+ * @see com.ibm.icu.text.BreakIterator#setText(java.text.CharacterIterator)
+ */
+ public void setText(CharacterIterator newText) {
+ if (newText instanceof CharSequence) {
+ fText= (CharSequence) newText;
+ fIterator.setText(newText);
+ first();
+ } else {
+ throw new UnsupportedOperationException("CharacterIterator not supported"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Enables breaks at word boundaries inside a camel case identifier.
+ *
+ * @param camelCaseBreakEnabled <code>true</code> to enable, <code>false</code> to disable.
+ */
+ public void setCamelCaseBreakEnabled(boolean camelCaseBreakEnabled) {
+ fCamelCaseBreakEnabled = camelCaseBreakEnabled;
+ }
+
+ /**
+ * @return <code>true</code> if breaks at word boundaries inside
+ * a camel case identifier are enabled.
+ */
+ public boolean isCamelCaseBreakEnabled() {
+ return fCamelCaseBreakEnabled;
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CWordIterator.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CWordIterator.java
new file mode 100644
index 00000000000..9e310c91080
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CWordIterator.java
@@ -0,0 +1,239 @@
+/*******************************************************************************
+ * 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
+ * Sergey Prigogin, Google
+ *******************************************************************************/
+package org.eclipse.cdt.internal.ui.text;
+
+import com.ibm.icu.text.BreakIterator;
+import java.text.CharacterIterator;
+
+import org.eclipse.jface.text.Assert;
+
+
+/**
+ * Breaks C text into word starts, also stops at line start and end. No
+ * direction dependency.
+ *
+ * @since 4.0
+ */
+public class CWordIterator extends BreakIterator {
+
+ /**
+ * The underlying java break iterator. It returns all breaks, including
+ * before and after every whitespace.
+ */
+ private CBreakIterator fIterator;
+ /** The current index for the stateful operations. */
+ private int fIndex;
+
+ /**
+ * Creates a new word iterator.
+ */
+ public CWordIterator() {
+ fIterator= new CBreakIterator();
+ first();
+ }
+
+ /*
+ * @see com.ibm.icu.text.BreakIterator#first()
+ */
+ public int first() {
+ fIndex= fIterator.first();
+ return fIndex;
+ }
+
+ /*
+ * @see com.ibm.icu.text.BreakIterator#last()
+ */
+ public int last() {
+ fIndex= fIterator.last();
+ return fIndex;
+ }
+
+ /*
+ * @see com.ibm.icu.text.BreakIterator#next(int)
+ */
+ public int next(int n) {
+ int next= 0;
+ while (--n > 0 && next != DONE) {
+ next= next();
+ }
+ return next;
+ }
+
+ /*
+ * @see com.ibm.icu.text.BreakIterator#next()
+ */
+ public int next() {
+ fIndex= following(fIndex);
+ return fIndex;
+ }
+
+ /*
+ * @see com.ibm.icu.text.BreakIterator#previous()
+ */
+ public int previous() {
+ fIndex= preceding(fIndex);
+ return fIndex;
+ }
+
+
+ /*
+ * @see com.ibm.icu.text.BreakIterator#preceding(int)
+ */
+ public int preceding(int offset) {
+ int first= fIterator.preceding(offset);
+ if (isWhitespace(first, offset)) {
+ int second= fIterator.preceding(first);
+ if (second != DONE && !isDelimiter(second, first))
+ return second;
+ }
+ return first;
+ }
+
+ /*
+ * @see com.ibm.icu.text.BreakIterator#following(int)
+ */
+ public int following(int offset) {
+ int first= fIterator.following(offset);
+ if (eatFollowingWhitespace(offset, first)) {
+ int second= fIterator.following(first);
+ if (isWhitespace(first, second))
+ return second;
+ }
+ return first;
+ }
+
+ private boolean eatFollowingWhitespace(int offset, int exclusiveEnd) {
+ if (exclusiveEnd == DONE || offset == DONE)
+ return false;
+
+ if (isWhitespace(offset, exclusiveEnd))
+ return false;
+ if (isDelimiter(offset, exclusiveEnd))
+ return false;
+
+ return true;
+ }
+
+ /**
+ * Returns <code>true</code> if the given sequence into the underlying text
+ * represents a delimiter, <code>false</code> otherwise.
+ *
+ * @param offset the offset
+ * @param exclusiveEnd the end offset
+ * @return <code>true</code> if the given range is a delimiter
+ */
+ private boolean isDelimiter(int offset, int exclusiveEnd) {
+ if (exclusiveEnd == DONE || offset == DONE)
+ return false;
+
+ Assert.isTrue(offset >= 0);
+ Assert.isTrue(exclusiveEnd <= getText().getEndIndex());
+ Assert.isTrue(exclusiveEnd > offset);
+
+ CharSequence seq= fIterator.fText;
+
+ while (offset < exclusiveEnd) {
+ char ch= seq.charAt(offset);
+ if (ch != '\n' && ch != '\r')
+ return false;
+ offset++;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns <code>true</code> if the given sequence into the underlying text
+ * represents whitespace, but not a delimiter, <code>false</code> otherwise.
+ *
+ * @param offset the offset
+ * @param exclusiveEnd the end offset
+ * @return <code>true</code> if the given range is whitespace
+ */
+ private boolean isWhitespace(int offset, int exclusiveEnd) {
+ if (exclusiveEnd == DONE || offset == DONE)
+ return false;
+
+ Assert.isTrue(offset >= 0);
+ Assert.isTrue(exclusiveEnd <= getText().getEndIndex());
+ Assert.isTrue(exclusiveEnd > offset);
+
+ CharSequence seq= fIterator.fText;
+
+ while (offset < exclusiveEnd) {
+ char ch= seq.charAt(offset);
+ if (!Character.isWhitespace(ch))
+ return false;
+ if (ch == '\n' || ch == '\r')
+ return false;
+ offset++;
+ }
+
+ return true;
+ }
+
+ /*
+ * @see com.ibm.icu.text.BreakIterator#current()
+ */
+ public int current() {
+ return fIndex;
+ }
+
+ /*
+ * @see com.ibm.icu.text.BreakIterator#getText()
+ */
+ public CharacterIterator getText() {
+ return fIterator.getText();
+ }
+
+ /**
+ * Sets the text as <code>CharSequence</code>.
+ * @param newText the new text
+ */
+ public void setText(CharSequence newText) {
+ fIterator.setText(newText);
+ first();
+ }
+
+ /*
+ * @see com.ibm.icu.text.BreakIterator#setText(java.text.CharacterIterator)
+ */
+ public void setText(CharacterIterator newText) {
+ fIterator.setText(newText);
+ first();
+ }
+
+ /*
+ * @see com.ibm.icu.text.BreakIterator#setText(java.lang.String)
+ */
+ public void setText(String newText) {
+ setText((CharSequence) newText);
+ }
+
+ /**
+ * Enables breaks at word boundaries inside a camel case identifier.
+ *
+ * @param camelCaseBreakEnabled <code>true</code> to enable,
+ * <code>false</code> to disable.
+ */
+ public void setCamelCaseBreakEnabled(boolean camelCaseBreakEnabled) {
+ fIterator.setCamelCaseBreakEnabled(camelCaseBreakEnabled);
+ }
+
+ /**
+ * @return <code>true</code> if breaks at word boundaries inside
+ * a camel case identifier are enabled.
+ */
+ public boolean isCamelCaseBreakEnabled() {
+ return fIterator.isCamelCaseBreakEnabled();
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/DocumentCharacterIterator.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/DocumentCharacterIterator.java
new file mode 100644
index 00000000000..371832b62bb
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/DocumentCharacterIterator.java
@@ -0,0 +1,222 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.cdt.internal.ui.text;
+
+import java.text.CharacterIterator;
+
+import org.eclipse.jface.text.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+
+
+/**
+ * An <code>IDocument</code> based implementation of
+ * <code>CharacterIterator</code> and <code>CharSequence</code>. Note that
+ * the supplied document is not copied; if the document is modified during the
+ * lifetime of a <code>DocumentCharacterIterator</code>, the methods
+ * returning document content may not always return the same values. Also, if
+ * accessing the document fails with a {@link BadLocationException}, any of
+ * <code>CharacterIterator</code> methods as well as <code>charAt</code>may
+ * return {@link CharacterIterator#DONE}.
+ *
+ * @since 4.0
+ */
+public class DocumentCharacterIterator implements CharacterIterator, CharSequence {
+
+ private int fIndex= -1;
+ private final IDocument fDocument;
+ private final int fFirst;
+ private final int fLast;
+
+ private void invariant() {
+ Assert.isTrue(fIndex >= fFirst);
+ Assert.isTrue(fIndex <= fLast);
+ }
+
+ /**
+ * Creates an iterator for the entire document.
+ *
+ * @param document the document backing this iterator
+ */
+ public DocumentCharacterIterator(IDocument document) {
+ this(document, 0);
+ }
+
+ /**
+ * Creates an iterator, starting at offset <code>first</code>.
+ *
+ * @param document the document backing this iterator
+ * @param first the first character to consider
+ * @throws IllegalArgumentException if the indices are out of bounds
+ */
+ public DocumentCharacterIterator(IDocument document, int first) throws IllegalArgumentException {
+ this(document, first, document.getLength());
+ }
+
+ /**
+ * Creates an iterator for the document contents from <code>first</code>
+ * (inclusive) to <code>last</code> (exclusive).
+ *
+ * @param document the document backing this iterator
+ * @param first the first character to consider
+ * @param last the last character index to consider
+ * @throws IllegalArgumentException if the indices are out of bounds
+ */
+ public DocumentCharacterIterator(IDocument document, int first, int last) throws IllegalArgumentException {
+ if (document == null)
+ throw new NullPointerException();
+ if (first < 0 || first > last)
+ throw new IllegalArgumentException();
+ if (last > document.getLength())
+ throw new IllegalArgumentException();
+ fDocument= document;
+ fFirst= first;
+ fLast= last;
+ fIndex= first;
+ invariant();
+ }
+
+ /*
+ * @see java.text.CharacterIterator#first()
+ */
+ public char first() {
+ return setIndex(getBeginIndex());
+ }
+
+ /*
+ * @see java.text.CharacterIterator#last()
+ */
+ public char last() {
+ if (fFirst == fLast)
+ return setIndex(getEndIndex());
+ else
+ return setIndex(getEndIndex() - 1);
+ }
+
+ /*
+ * @see java.text.CharacterIterator#current()
+ */
+ public char current() {
+ if (fIndex >= fFirst && fIndex < fLast)
+ try {
+ return fDocument.getChar(fIndex);
+ } catch (BadLocationException e) {
+ // ignore
+ }
+ return DONE;
+ }
+
+ /*
+ * @see java.text.CharacterIterator#next()
+ */
+ public char next() {
+ return setIndex(Math.min(fIndex + 1, getEndIndex()));
+ }
+
+ /*
+ * @see java.text.CharacterIterator#previous()
+ */
+ public char previous() {
+ if (fIndex > getBeginIndex()) {
+ return setIndex(fIndex - 1);
+ } else {
+ return DONE;
+ }
+ }
+
+ /*
+ * @see java.text.CharacterIterator#setIndex(int)
+ */
+ public char setIndex(int position) {
+ if (position >= getBeginIndex() && position <= getEndIndex())
+ fIndex= position;
+ else
+ throw new IllegalArgumentException();
+
+ invariant();
+ return current();
+ }
+
+ /*
+ * @see java.text.CharacterIterator#getBeginIndex()
+ */
+ public int getBeginIndex() {
+ return fFirst;
+ }
+
+ /*
+ * @see java.text.CharacterIterator#getEndIndex()
+ */
+ public int getEndIndex() {
+ return fLast;
+ }
+
+ /*
+ * @see java.text.CharacterIterator#getIndex()
+ */
+ public int getIndex() {
+ return fIndex;
+ }
+
+ /*
+ * @see java.text.CharacterIterator#clone()
+ */
+ public Object clone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new InternalError();
+ }
+ }
+
+ /*
+ * @see java.lang.CharSequence#length()
+ */
+ public int length() {
+ return getEndIndex() - getBeginIndex();
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * Note that, if the document is modified concurrently, this method may
+ * return {@link CharacterIterator#DONE} if a {@link BadLocationException}
+ * was thrown when accessing the backing document.
+ * </p>
+ *
+ * @param index {@inheritDoc}
+ * @return {@inheritDoc}
+ */
+ public char charAt(int index) {
+ if (index >= 0 && index < length())
+ try {
+ return fDocument.getChar(getBeginIndex() + index);
+ } catch (BadLocationException e) {
+ // ignore and return DONE
+ return DONE;
+ }
+ else
+ throw new IndexOutOfBoundsException();
+ }
+
+ /*
+ * @see java.lang.CharSequence#subSequence(int, int)
+ */
+ public CharSequence subSequence(int start, int end) {
+ if (start < 0)
+ throw new IndexOutOfBoundsException();
+ if (end < start)
+ throw new IndexOutOfBoundsException();
+ if (end > length())
+ throw new IndexOutOfBoundsException();
+ return new DocumentCharacterIterator(fDocument, getBeginIndex() + start, getBeginIndex() + end);
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/SequenceCharacterIterator.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/SequenceCharacterIterator.java
new file mode 100644
index 00000000000..36ec9e9d98d
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/SequenceCharacterIterator.java
@@ -0,0 +1,166 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.cdt.internal.ui.text;
+
+import java.text.CharacterIterator;
+
+import org.eclipse.jface.text.Assert;
+
+
+/**
+ * A <code>CharSequence</code> based implementation of <code>CharacterIterator</code>.
+ *
+ * @since 4.0
+ */
+public class SequenceCharacterIterator implements CharacterIterator {
+
+ private int fIndex= -1;
+ private final CharSequence fSequence;
+ private final int fFirst;
+ private final int fLast;
+
+ private void invariant() {
+ Assert.isTrue(fIndex >= fFirst);
+ Assert.isTrue(fIndex <= fLast);
+ }
+
+ /**
+ * Creates an iterator for the entire sequence.
+ *
+ * @param sequence the sequence backing this iterator
+ */
+ public SequenceCharacterIterator(CharSequence sequence) {
+ this(sequence, 0);
+ }
+
+ /**
+ * Creates an iterator.
+ *
+ * @param sequence the sequence backing this iterator
+ * @param first the first character to consider
+ * @throws IllegalArgumentException if the indices are out of bounds
+ */
+ public SequenceCharacterIterator(CharSequence sequence, int first) throws IllegalArgumentException {
+ this(sequence, first, sequence.length());
+ }
+
+ /**
+ * Creates an iterator.
+ *
+ * @param sequence the sequence backing this iterator
+ * @param first the first character to consider
+ * @param last the last character index to consider
+ * @throws IllegalArgumentException if the indices are out of bounds
+ */
+ public SequenceCharacterIterator(CharSequence sequence, int first, int last) throws IllegalArgumentException {
+ if (sequence == null)
+ throw new NullPointerException();
+ if (first < 0 || first > last)
+ throw new IllegalArgumentException();
+ if (last > sequence.length())
+ throw new IllegalArgumentException();
+ fSequence= sequence;
+ fFirst= first;
+ fLast= last;
+ fIndex= first;
+ invariant();
+ }
+
+ /*
+ * @see java.text.CharacterIterator#first()
+ */
+ public char first() {
+ return setIndex(getBeginIndex());
+ }
+
+ /*
+ * @see java.text.CharacterIterator#last()
+ */
+ public char last() {
+ if (fFirst == fLast)
+ return setIndex(getEndIndex());
+ else
+ return setIndex(getEndIndex() - 1);
+ }
+
+ /*
+ * @see java.text.CharacterIterator#current()
+ */
+ public char current() {
+ if (fIndex >= fFirst && fIndex < fLast)
+ return fSequence.charAt(fIndex);
+ else
+ return DONE;
+ }
+
+ /*
+ * @see java.text.CharacterIterator#next()
+ */
+ public char next() {
+ return setIndex(Math.min(fIndex + 1, getEndIndex()));
+ }
+
+ /*
+ * @see java.text.CharacterIterator#previous()
+ */
+ public char previous() {
+ if (fIndex > getBeginIndex()) {
+ return setIndex(fIndex - 1);
+ } else {
+ return DONE;
+ }
+ }
+
+ /*
+ * @see java.text.CharacterIterator#setIndex(int)
+ */
+ public char setIndex(int position) {
+ if (position >= getBeginIndex() && position <= getEndIndex())
+ fIndex= position;
+ else
+ throw new IllegalArgumentException();
+
+ invariant();
+ return current();
+ }
+
+ /*
+ * @see java.text.CharacterIterator#getBeginIndex()
+ */
+ public int getBeginIndex() {
+ return fFirst;
+ }
+
+ /*
+ * @see java.text.CharacterIterator#getEndIndex()
+ */
+ public int getEndIndex() {
+ return fLast;
+ }
+
+ /*
+ * @see java.text.CharacterIterator#getIndex()
+ */
+ public int getIndex() {
+ return fIndex;
+ }
+
+ /*
+ * @see java.text.CharacterIterator#clone()
+ */
+ public Object clone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new InternalError();
+ }
+ }
+}

Back to the top