### Eclipse Workspace Patch 1.0 #P org.eclipse.cdt.ui Index: META-INF/MANIFEST.MF =================================================================== RCS file: /home/tools/org.eclipse.cdt-core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF,v retrieving revision 1.10 diff -u -r1.10 MANIFEST.MF --- META-INF/MANIFEST.MF 5 Jul 2006 14:23:52 -0000 1.10 +++ META-INF/MANIFEST.MF 15 Jul 2006 05:33:46 -0000 @@ -69,6 +69,7 @@ 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 Index: src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties =================================================================== RCS file: /home/tools/org.eclipse.cdt-core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties,v retrieving revision 1.44 diff -u -r1.44 PreferencesMessages.properties --- src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties 14 Jul 2006 13:21:46 -0000 1.44 +++ src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties 15 Jul 2006 05:33:46 -0000 @@ -108,6 +108,7 @@ 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 Index: src/org/eclipse/cdt/internal/ui/preferences/CEditorPreferencePage.java =================================================================== RCS file: /home/tools/org.eclipse.cdt-core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/CEditorPreferencePage.java,v retrieving revision 1.56 diff -u -r1.56 CEditorPreferencePage.java --- src/org/eclipse/cdt/internal/ui/preferences/CEditorPreferencePage.java 14 Jul 2006 13:21:46 -0000 1.56 +++ src/org/eclipse/cdt/internal/ui/preferences/CEditorPreferencePage.java 15 Jul 2006 05:33:46 -0000 @@ -118,6 +118,7 @@ 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)); @@ -147,6 +148,8 @@ // 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)); @@ -336,7 +339,10 @@ 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$ Index: src/org/eclipse/cdt/internal/ui/editor/CEditor.java =================================================================== RCS file: /home/tools/org.eclipse.cdt-core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java,v retrieving revision 1.105 diff -u -r1.105 CEditor.java --- src/org/eclipse/cdt/internal/ui/editor/CEditor.java 14 Jul 2006 13:21:46 -0000 1.105 +++ src/org/eclipse/cdt/internal/ui/editor/CEditor.java 15 Jul 2006 05:33:46 -0000 @@ -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.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.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.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.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 @@ /** 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 @@ 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 @@ System.arraycopy(parentPrefPageIds, 0, prefPageIds, nIds, parentPrefPageIds.length); return prefPageIds; } + + /** + * Text navigation action to navigate to the next sub-word. + * + * @since 3.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 position. + * + * @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 3.0 + */ + protected class NavigateNextSubWordAction extends NextSubWordAction { + + /** + * Creates a new navigate next sub-word action. + */ + public NavigateNextSubWordAction() { + super(ST.WORD_NEXT); + } + + /* + * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.NextSubWordAction#setCaretPosition(int) + */ + protected void setCaretPosition(final int position) { + getTextWidget().setCaretOffset(modelOffset2WidgetOffset(getSourceViewer(), position)); + } + } + + /** + * Text operation action to delete the next sub-word. + * + * @since 3.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.jdt.internal.ui.javaeditor.JavaEditor.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.jdt.internal.ui.javaeditor.JavaEditor.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 3.0 + */ + protected class SelectNextSubWordAction extends NextSubWordAction { + + /** + * Creates a new select next sub-word action. + */ + public SelectNextSubWordAction() { + super(ST.SELECT_WORD_NEXT); + } + + /* + * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.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 3.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 position. + * + * @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 3.0 + */ + protected class NavigatePreviousSubWordAction extends PreviousSubWordAction { + + /** + * Creates a new navigate previous sub-word action. + */ + public NavigatePreviousSubWordAction() { + super(ST.WORD_PREVIOUS); + } + + /* + * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.PreviousSubWordAction#setCaretPosition(int) + */ + protected void setCaretPosition(final int position) { + getTextWidget().setCaretOffset(modelOffset2WidgetOffset(getSourceViewer(), position)); + } + } + + /** + * Text operation action to delete the previous sub-word. + * + * @since 3.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.jdt.internal.ui.javaeditor.JavaEditor.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.jdt.internal.ui.javaeditor.JavaEditor.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 3.0 + */ + protected class SelectPreviousSubWordAction extends PreviousSubWordAction { + + /** + * Creates a new select previous sub-word action. + */ + public SelectPreviousSubWordAction() { + super(ST.SELECT_WORD_PREVIOUS); + } + + /* + * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.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); + } + } + } } Index: src/org/eclipse/cdt/internal/ui/text/CBreakIterator.java =================================================================== RCS file: src/org/eclipse/cdt/internal/ui/text/CBreakIterator.java diff -N src/org/eclipse/cdt/internal/ui/text/CBreakIterator.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/cdt/internal/ui/text/CBreakIterator.java 1 Jan 1970 00:00:00 -0000 @@ -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. + *

+ * A line break may be any of "\n", "\r", "\r\n", "\n\r". + *

+ * + * @since 3.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 true if this run consumes ch, + * false otherwise. If true is returned, + * the length of the receiver is adjusted accordingly. + * + * @param ch the character to test + * @return true if ch 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 consume. + * + * @param ch the character to test + * @return true if ch 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.jdt.internal.ui.text.CBreakIterator.Run#init() + */ + protected void init() { + super.init(); + fState= INIT; + } + + /* + * @see org.eclipse.jdt.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.jdt.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.jdt.internal.ui.text.CBreakIterator.Run#init() + */ + protected void init() { + super.init(); + fState= S_INIT; + } + + /* + * @see org.eclipse.jdt.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.jdt.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.jdt.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 java.text.BreakIterator#current() + */ + public int current() { + return fIndex; + } + + /* + * @see java.text.BreakIterator#first() + */ + public int first() { + fIndex= fIterator.first(); + return fIndex; + } + + /* + * @see java.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 ch + */ + 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 java.text.BreakIterator#getText() + */ + public CharacterIterator getText() { + return fIterator.getText(); + } + + /* + * @see java.text.BreakIterator#isBoundary(int) + */ + public boolean isBoundary(int offset) { + if (offset == getText().getBeginIndex()) + return true; + else + return following(offset - 1) == offset; + } + + /* + * @see java.text.BreakIterator#last() + */ + public int last() { + fIndex= fIterator.last(); + return fIndex; + } + + /* + * @see java.text.BreakIterator#next() + */ + public int next() { + fIndex= following(fIndex); + return fIndex; + } + + /* + * @see java.text.BreakIterator#next(int) + */ + public int next(int n) { + return fIterator.next(n); + } + + /* + * @see java.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 java.text.BreakIterator#previous() + */ + public int previous() { + fIndex= preceding(fIndex); + return fIndex; + } + + /* + * @see java.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 java.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 enable true to enable, false to disable. + */ + public void setCamelCaseBreakEnabled(boolean camelCaseBreakEnabled) { + fCamelCaseBreakEnabled = camelCaseBreakEnabled; + } + + /** + * @return true if breaks at word boundaries inside + * a camel case identifier are enabled. + */ + public boolean isCamelCaseBreakEnabled() { + return fCamelCaseBreakEnabled; + } +} Index: src/org/eclipse/cdt/internal/ui/text/DocumentCharacterIterator.java =================================================================== RCS file: src/org/eclipse/cdt/internal/ui/text/DocumentCharacterIterator.java diff -N src/org/eclipse/cdt/internal/ui/text/DocumentCharacterIterator.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/cdt/internal/ui/text/DocumentCharacterIterator.java 1 Jan 1970 00:00:00 -0000 @@ -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 IDocument based implementation of + * CharacterIterator and CharSequence. Note that + * the supplied document is not copied; if the document is modified during the + * lifetime of a DocumentCharacterIterator, the methods + * returning document content may not always return the same values. Also, if + * accessing the document fails with a {@link BadLocationException}, any of + * CharacterIterator methods as well as charAtmay + * return {@link CharacterIterator#DONE}. + * + * @since 3.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 first. + * + * @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 first + * (inclusive) to last (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} + *

+ * 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. + *

+ * + * @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); + } +} Index: src/org/eclipse/cdt/internal/ui/text/CWordIterator.java =================================================================== RCS file: src/org/eclipse/cdt/internal/ui/text/CWordIterator.java diff -N src/org/eclipse/cdt/internal/ui/text/CWordIterator.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/cdt/internal/ui/text/CWordIterator.java 1 Jan 1970 00:00:00 -0000 @@ -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 3.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 java.text.BreakIterator#first() + */ + public int first() { + fIndex= fIterator.first(); + return fIndex; + } + + /* + * @see java.text.BreakIterator#last() + */ + public int last() { + fIndex= fIterator.last(); + return fIndex; + } + + /* + * @see java.text.BreakIterator#next(int) + */ + public int next(int n) { + int next= 0; + while (--n > 0 && next != DONE) { + next= next(); + } + return next; + } + + /* + * @see java.text.BreakIterator#next() + */ + public int next() { + fIndex= following(fIndex); + return fIndex; + } + + /* + * @see java.text.BreakIterator#previous() + */ + public int previous() { + fIndex= preceding(fIndex); + return fIndex; + } + + + /* + * @see java.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 java.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 true if the given sequence into the underlying text + * represents a delimiter, false otherwise. + * + * @param offset the offset + * @param exclusiveEnd the end offset + * @return true 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 true if the given sequence into the underlying text + * represents whitespace, but not a delimiter, false otherwise. + * + * @param offset the offset + * @param exclusiveEnd the end offset + * @return true 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 java.text.BreakIterator#current() + */ + public int current() { + return fIndex; + } + + /* + * @see java.text.BreakIterator#getText() + */ + public CharacterIterator getText() { + return fIterator.getText(); + } + + /** + * Sets the text as CharSequence. + * @param newText the new text + */ + public void setText(CharSequence newText) { + fIterator.setText(newText); + first(); + } + + /* + * @see java.text.BreakIterator#setText(java.text.CharacterIterator) + */ + public void setText(CharacterIterator newText) { + fIterator.setText(newText); + first(); + } + + /* + * @see java.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 true to enable, + * false to disable. + */ + public void setCamelCaseBreakEnabled(boolean camelCaseBreakEnabled) { + fIterator.setCamelCaseBreakEnabled(camelCaseBreakEnabled); + } + + /** + * @return true if breaks at word boundaries inside + * a camel case identifier are enabled. + */ + public boolean isCamelCaseBreakEnabled() { + return fIterator.isCamelCaseBreakEnabled(); + } +} Index: src/org/eclipse/cdt/internal/ui/text/SequenceCharacterIterator.java =================================================================== RCS file: src/org/eclipse/cdt/internal/ui/text/SequenceCharacterIterator.java diff -N src/org/eclipse/cdt/internal/ui/text/SequenceCharacterIterator.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/cdt/internal/ui/text/SequenceCharacterIterator.java 1 Jan 1970 00:00:00 -0000 @@ -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 CharSequence based implementation of CharacterIterator. + * + * @since 3.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(); + } + } +} #P org.eclipse.cdt.ui.tests Index: ui/org/eclipse/cdt/ui/tests/AutomatedSuite.java =================================================================== RCS file: /home/tools/org.eclipse.cdt/all/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/AutomatedSuite.java,v retrieving revision 1.35 diff -u -r1.35 AutomatedSuite.java --- ui/org/eclipse/cdt/ui/tests/AutomatedSuite.java 3 Jul 2006 13:01:42 -0000 1.35 +++ ui/org/eclipse/cdt/ui/tests/AutomatedSuite.java 15 Jul 2006 05:33:47 -0000 @@ -15,6 +15,8 @@ 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 @@ addTest( CSelectionTestsDOMIndexer.suite() ); addTest( CPPSelectionTestsCTagsIndexer.suite() ); addTest( CSelectionTestsCTagsIndexer.suite() ); + + // Break iterator tests. + addTest(CBreakIteratorTest.suite()); + addTest(CWordIteratorTest.suite()); } } Index: META-INF/MANIFEST.MF =================================================================== RCS file: /home/tools/org.eclipse.cdt/all/org.eclipse.cdt.ui.tests/META-INF/MANIFEST.MF,v retrieving revision 1.5 diff -u -r1.5 MANIFEST.MF --- META-INF/MANIFEST.MF 24 May 2006 18:54:03 -0000 1.5 +++ META-INF/MANIFEST.MF 15 Jul 2006 05:33:47 -0000 @@ -32,6 +32,7 @@ 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 Index: ui/org/eclipse/cdt/ui/tests/text/CBreakIteratorTest.java =================================================================== RCS file: ui/org/eclipse/cdt/ui/tests/text/CBreakIteratorTest.java diff -N ui/org/eclipse/cdt/ui/tests/text/CBreakIteratorTest.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ ui/org/eclipse/cdt/ui/tests/text/CBreakIteratorTest.java 1 Jan 1970 00:00:00 -0000 @@ -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 }); + } +} Index: ui/org/eclipse/cdt/ui/tests/text/BreakIteratorTest.java =================================================================== RCS file: ui/org/eclipse/cdt/ui/tests/text/BreakIteratorTest.java diff -N ui/org/eclipse/cdt/ui/tests/text/BreakIteratorTest.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ ui/org/eclipse/cdt/ui/tests/text/BreakIteratorTest.java 1 Jan 1970 00:00:00 -0000 @@ -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--; + } + } + } + +} Index: ui/org/eclipse/cdt/ui/tests/text/CWordIteratorTest.java =================================================================== RCS file: ui/org/eclipse/cdt/ui/tests/text/CWordIteratorTest.java diff -N ui/org/eclipse/cdt/ui/tests/text/CWordIteratorTest.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ ui/org/eclipse/cdt/ui/tests/text/CWordIteratorTest.java 1 Jan 1970 00:00:00 -0000 @@ -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 }); + } + +}