mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-04-23 14:42:11 +02:00
Patch from Sergey Prigogin for bug 140489 - Support "smart caret positioning" similar to Java
This commit is contained in:
parent
e9c33c53c8
commit
51e04549cc
13 changed files with 1841 additions and 3 deletions
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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 });
|
||||
}
|
||||
}
|
|
@ -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 });
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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$
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue