/*******************************************************************************
* Copyright (c) 2000, 2010 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
* Anton Leherbauer (Wind River Systems)
*******************************************************************************/
package org.eclipse.cdt.ui.tests.text;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.text.link.ILinkedModeListener;
import org.eclipse.jface.text.link.LinkedModeModel;
import org.eclipse.jface.text.link.LinkedPosition;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.widgets.Event;
import org.eclipse.ui.PartInitException;
import org.eclipse.cdt.core.dom.IPDOMManager;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.ICContainer;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.testplugin.CProjectHelper;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.PreferenceConstants;
import org.eclipse.cdt.ui.testplugin.Accessor;
import org.eclipse.cdt.ui.testplugin.DisplayHelper;
import org.eclipse.cdt.ui.testplugin.EditorTestHelper;
import org.eclipse.cdt.internal.ui.editor.CEditor;
/**
* Tests the automatic bracket insertion feature of the CEditor. Also tests
* linked mode along the way.
*/
public class BracketInserterTest extends TestCase {
private static final String SRC= "src";
private static final String SEP= "/";
private static final String TU_NAME= "smartedit.cpp";
private static final String TU_CONTENTS=
"#include \n" +
"class Application {\n" +
" char* string;\n" +
" int integer;\n" +
"\n" +
"public:\n" +
" static void main(int argc, char** args);\n" +
"protected:\n" +
" void foo(char** args);\n" +
"};\n" +
"\n" +
"void Application::main(int argc, char** args) {\n" +
" \n" +
"}\n" +
"void Application::foo(char** args) {\n" +
" char[] t= args[0];" +
"}\n";
// document offsets
private static final int INCLUDE_OFFSET= 9;
private static final int BODY_OFFSET= 212;
private static final int ARGS_OFFSET= 184;
private static final int BRACKETS_OFFSET= 262;
public static Test suite() {
TestSuite suite= new TestSuite(BracketInserterTest.class);
return suite;
}
private CEditor fEditor;
private StyledText fTextWidget;
private IDocument fDocument;
private Accessor fAccessor;
private ICProject fProject;
@Override
protected void setUp() throws Exception {
IPreferenceStore store= CUIPlugin.getDefault().getPreferenceStore();
store.setValue(PreferenceConstants.EDITOR_CLOSE_BRACKETS, true);
setUpProject();
setUpEditor();
}
private void setUpProject() throws CoreException {
fProject= CProjectHelper.createCProject(getName(), "bin", IPDOMManager.ID_NO_INDEXER);
ICContainer cContainer= CProjectHelper.addCContainer(fProject, SRC);
IFile file= EditorTestHelper.createFile((IContainer)cContainer.getResource(), TU_NAME, TU_CONTENTS, new NullProgressMonitor());
assertNotNull(file);
assertTrue(file.exists());
}
private void setUpEditor() {
fEditor= openCEditor(new Path(SEP + getName() + SEP + SRC + SEP + TU_NAME));
assertNotNull(fEditor);
fTextWidget= fEditor.getViewer().getTextWidget();
assertNotNull(fTextWidget);
fAccessor= new Accessor(fTextWidget, StyledText.class);
fDocument= fEditor.getDocumentProvider().getDocument(fEditor.getEditorInput());
assertNotNull(fDocument);
assertEquals(TU_CONTENTS, fDocument.get());
}
private CEditor openCEditor(IPath path) {
IFile file= ResourcesPlugin.getWorkspace().getRoot().getFile(path);
assertTrue(file != null && file.exists());
try {
return (CEditor)EditorTestHelper.openInEditor(file, true);
} catch (PartInitException e) {
fail();
return null;
}
}
@Override
protected void tearDown() throws Exception {
EditorTestHelper.closeEditor(fEditor);
fEditor= null;
if (fProject != null) {
CProjectHelper.delete(fProject);
fProject= null;
}
// reset to defaults
IPreferenceStore store= CUIPlugin.getDefault().getPreferenceStore();
store.setToDefault(PreferenceConstants.EDITOR_CLOSE_BRACKETS);
}
public void testInsertClosingParenthesis() throws BadLocationException, CModelException, CoreException, CModelException, CoreException {
setCaret(BODY_OFFSET);
type('(');
assertEquals("()", fDocument.get(BODY_OFFSET, 2));
assertSingleLinkedPosition(BODY_OFFSET + 1);
}
public void testDeletingParenthesis() throws CModelException, CoreException {
setCaret(BODY_OFFSET);
type('(');
type(SWT.BS);
assertEquals(TU_CONTENTS, fDocument.get());
assertFalse(LinkedModeModel.hasInstalledModel(fDocument));
}
public void testMultipleParenthesisInsertion() throws BadLocationException, CModelException, CoreException {
setCaret(BODY_OFFSET);
type("((((");
assertEquals("(((())))", fDocument.get(BODY_OFFSET, 8));
assertEquals(BODY_OFFSET + 4, getCaret());
assertModel(true);
}
public void testDeletingMultipleParenthesisInertion() throws BadLocationException, CModelException, CoreException {
setCaret(BODY_OFFSET);
type("((((");
// delete two levels
linkedType(SWT.BS, true, ILinkedModeListener.EXTERNAL_MODIFICATION);
linkedType(SWT.BS, true, ILinkedModeListener.EXTERNAL_MODIFICATION);
assertEquals("(())", fDocument.get(BODY_OFFSET, 4));
assertEquals(BODY_OFFSET + 2, getCaret());
// delete the second-last level
linkedType(SWT.BS, true, ILinkedModeListener.EXTERNAL_MODIFICATION);
assertEquals("()", fDocument.get(BODY_OFFSET, 2));
assertEquals(BODY_OFFSET + 1, getCaret());
// delete last level
linkedType(SWT.BS, false, ILinkedModeListener.EXTERNAL_MODIFICATION);
assertEquals(TU_CONTENTS, fDocument.get());
assertEquals(BODY_OFFSET, getCaret());
assertEquals(TU_CONTENTS, fDocument.get());
assertFalse(LinkedModeModel.hasInstalledModel(fDocument));
}
public void testNoInsertInsideText() throws BadLocationException, CModelException, CoreException {
setCaret(ARGS_OFFSET);
type('(');
assertEquals("(in", fDocument.get(ARGS_OFFSET, 3));
assertEquals(ARGS_OFFSET + 1, getCaret());
assertFalse(LinkedModeModel.hasInstalledModel(fDocument));
}
public void testInsertInsideBrackets() throws BadLocationException, CModelException, CoreException {
setCaret(BRACKETS_OFFSET);
type('(');
assertEquals("()", fDocument.get(BRACKETS_OFFSET, 2));
assertSingleLinkedPosition(BRACKETS_OFFSET + 1);
}
public void testPeerEntry() throws BadLocationException, CModelException, CoreException {
setCaret(BODY_OFFSET);
type("()");
assertEquals("()", fDocument.get(BODY_OFFSET, 2));
assertEquals(BODY_OFFSET + 2, getCaret());
assertFalse(LinkedModeModel.hasInstalledModel(fDocument));
}
public void testMultiplePeerEntry() throws BadLocationException, CModelException, CoreException {
setCaret(BODY_OFFSET);
type("((((");
linkedType(')', true, ILinkedModeListener.UPDATE_CARET);
linkedType(')', true, ILinkedModeListener.UPDATE_CARET);
linkedType(')', true, ILinkedModeListener.UPDATE_CARET);
assertEquals("(((())))", fDocument.get(BODY_OFFSET, 8));
assertEquals(BODY_OFFSET + 7, getCaret());
LinkedPosition position= assertModel(false).findPosition(new LinkedPosition(fDocument, BODY_OFFSET + 1, 0));
assertNotNull(position);
assertEquals(BODY_OFFSET + 1, position.getOffset());
assertEquals(6, position.getLength());
linkedType(')', false, ILinkedModeListener.UPDATE_CARET);
assertEquals("(((())))", fDocument.get(BODY_OFFSET, 8));
assertEquals(BODY_OFFSET + 8, getCaret());
assertFalse(LinkedModeModel.hasInstalledModel(fDocument));
}
public void testExitOnTab() throws BadLocationException, CModelException, CoreException {
setCaret(BODY_OFFSET);
type("((((");
linkedType('\t', true, ILinkedModeListener.NONE);
assertEquals("(((())))", fDocument.get(BODY_OFFSET, 8));
assertEquals(BODY_OFFSET + 5, getCaret());
linkedType('\t', true, ILinkedModeListener.NONE);
linkedType('\t', true, ILinkedModeListener.NONE);
assertEquals("(((())))", fDocument.get(BODY_OFFSET, 8));
assertEquals(BODY_OFFSET + 7, getCaret());
linkedType('\t', false, ILinkedModeListener.NONE);
assertEquals("(((())))", fDocument.get(BODY_OFFSET, 8));
assertEquals(BODY_OFFSET + 8, getCaret());
assertFalse(LinkedModeModel.hasInstalledModel(fDocument));
}
public void testExitOnReturn() throws BadLocationException, CModelException, CoreException {
setCaret(BODY_OFFSET);
type("((((");
linkedType(SWT.CR, true, ILinkedModeListener.UPDATE_CARET | ILinkedModeListener.EXIT_ALL);
assertEquals("(((())))", fDocument.get(BODY_OFFSET, 8));
assertEquals(BODY_OFFSET + 8, getCaret());
assertFalse(LinkedModeModel.hasInstalledModel(fDocument));
}
public void testExitOnEsc() throws BadLocationException, CModelException, CoreException {
setCaret(BODY_OFFSET);
type("((((");
linkedType(SWT.ESC, true, ILinkedModeListener.EXIT_ALL);
assertEquals("(((())))", fDocument.get(BODY_OFFSET, 8));
assertEquals(BODY_OFFSET + 4, getCaret());
assertFalse(LinkedModeModel.hasInstalledModel(fDocument));
}
public void testInsertClosingQuote() throws BadLocationException, CModelException, CoreException {
setCaret(BODY_OFFSET);
type('"');
assertEquals("\"\"", fDocument.get(BODY_OFFSET, 2));
assertSingleLinkedPosition(BODY_OFFSET + 1);
}
// bug 270916
public void testInsertClosingQuoteInMacroDefinition() throws BadLocationException, CModelException, CoreException {
setCaret(BODY_OFFSET);
type("#define MACRO ");
int offset = getCaret();
// enter opening quote (should be closed again)
type('"');
assertEquals("\"\"", fDocument.get(offset, 2));
assertSingleLinkedPosition(offset + 1);
// enter closing quote (should not add a quote, but proceed cursor)
type('"');
assertEquals("\"\"", fDocument.get(offset, 2));
assertEquals(offset + 2, getCaret());
// delete closing quote and enter quote again
type(SWT.BS);
assertEquals("\"", fDocument.get(offset, 1));
int length = fDocument.getLength();
type('"');
assertEquals("\"\"", fDocument.get(offset, 2));
assertEquals(offset + 2, getCaret());
assertEquals(length + 1, fDocument.getLength());
}
public void testPreferences() throws BadLocationException, CModelException, CoreException {
IPreferenceStore store= CUIPlugin.getDefault().getPreferenceStore();
store.setValue(PreferenceConstants.EDITOR_CLOSE_BRACKETS, false);
setCaret(BODY_OFFSET);
type('(');
assertEquals("(", fDocument.get(BODY_OFFSET, 1));
assertEquals(BODY_OFFSET + 1, getCaret());
assertFalse(LinkedModeModel.hasInstalledModel(fDocument));
}
public void testAngleBracketsAsOperator() throws Exception {
setCaret(BODY_OFFSET);
type("test<");
assertEquals("test<", fDocument.get(BODY_OFFSET, 5));
assertFalse(">".equals(fDocument.get(BODY_OFFSET + 5, 1)));
assertEquals(BODY_OFFSET + 5, getCaret());
assertFalse(LinkedModeModel.hasInstalledModel(fDocument));
}
public void testAngleBrackets() throws Exception {
setCaret(BODY_OFFSET);
type("Test<");
assertEquals("Test<>", fDocument.get(BODY_OFFSET, 6));
assertSingleLinkedPosition(BODY_OFFSET + 5);
}
public void testAngleBracketsInInclude() throws Exception {
setCaret(INCLUDE_OFFSET);
type('<');
assertEquals("#include <>", fDocument.get(INCLUDE_OFFSET - 9, 11));
assertSingleLinkedPosition(INCLUDE_OFFSET + 1);
}
public void testInsertClosingQuoteInInclude() throws Exception {
setCaret(INCLUDE_OFFSET);
type('"');
assertEquals("#include \"\"", fDocument.get(INCLUDE_OFFSET - 9, 11));
assertSingleLinkedPosition(INCLUDE_OFFSET + 1);
}
public void testInsertClosingQuoteInIncludeAtDocumentEnd_Bug309099() throws Exception {
int startOffset = TU_CONTENTS.length();
setCaret(startOffset);
type("#include ");
type('"');
assertEquals(startOffset + 11, fDocument.getLength());
assertEquals("#include \"\"", fDocument.get(startOffset, 11));
assertSingleLinkedPosition(startOffset + 10);
}
public void testAngleBrackets_165837() throws Exception {
setCaret(BODY_OFFSET);
type("cout << \n\"aaa\" ");
type('<');
int caretOffset= getCaret();
assertFalse(">".equals(fDocument.get(caretOffset, 1)));
assertFalse(LinkedModeModel.hasInstalledModel(fDocument));
}
/* utilities */
private void assertSingleLinkedPosition(int offset) {
assertEquals(offset, getCaret());
LinkedPosition position= assertModel(false).findPosition(new LinkedPosition(fDocument, offset, 0));
assertNotNull(position);
assertEquals(offset, position.getOffset());
assertEquals(0, position.getLength());
}
/**
* Type characters into the styled text.
*
* @param characters the characters to type
*/
private void type(CharSequence characters) {
for (int i= 0; i < characters.length(); i++)
type(characters.charAt(i), 0, 0);
}
/**
* Type a character into the styled text.
*
* @param character the character to type
*/
private void type(char character) {
type(character, 0, 0);
}
/**
* Ensure there is a linked mode and type a character into the styled text.
*
* @param character the character to type
* @param nested whether the linked mode is expected to be nested or not
* @param expectedExitFlags the expected exit flags for the current linked mode after typing the character, -1 for no exit
*/
private void linkedType(char character, boolean nested, int expectedExitFlags) {
final int[] exitFlags= { -1 };
assertModel(nested).addLinkingListener(new ILinkedModeListener() {
public void left(LinkedModeModel model, int flags) {
exitFlags[0]= flags;
}
public void resume(LinkedModeModel model, int flags) {
}
public void suspend(LinkedModeModel model) {
}
});
type(character, 0, 0);
assertEquals(expectedExitFlags, exitFlags[0]);
}
private LinkedModeModel assertModel(boolean nested) {
LinkedModeModel model= LinkedModeModel.getModel(fDocument, 0); // offset does not matter
assertNotNull(model);
assertEquals(nested, model.isNested());
return model;
}
/**
* Type a character into the styled text.
*
* @param character the character to type
* @param keyCode the key code
* @param stateMask the state mask
*/
private void type(char character, int keyCode, int stateMask) {
Event event= new Event();
event.character= character;
event.keyCode= keyCode;
event.stateMask= stateMask;
fAccessor.invoke("handleKeyDown", new Object[] {event});
new DisplayHelper() {
@Override
protected boolean condition() {
return false;
}
}.waitForCondition(EditorTestHelper.getActiveDisplay(), 200);
}
private int getCaret() {
return ((ITextSelection) fEditor.getSelectionProvider().getSelection()).getOffset();
}
private void setCaret(int offset) {
fEditor.getSelectionProvider().setSelection(new TextSelection(offset, 0));
int newOffset= ((ITextSelection)fEditor.getSelectionProvider().getSelection()).getOffset();
assertEquals(offset, newOffset);
}
}