1
0
Fork 0
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:
Anton Leherbauer 2006-07-17 14:26:49 +00:00
parent e9c33c53c8
commit 51e04549cc
13 changed files with 1841 additions and 3 deletions

View file

@ -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

View file

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

View file

@ -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--;
}
}
}
}

View file

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

View file

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

View file

@ -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

View file

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

View file

@ -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$

View file

@ -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

View file

@ -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;
}
}

View file

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

View file

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

View file

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