| /******************************************************************************* |
| * Copyright (c) 2000, 2011 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.text.tests; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotEquals; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.rules.TestName; |
| |
| import org.eclipse.jdt.testplugin.JavaProjectHelper; |
| import org.eclipse.jdt.text.tests.performance.DisplayHelper; |
| import org.eclipse.jdt.text.tests.performance.EditorTestHelper; |
| |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.custom.StyledText; |
| import org.eclipse.swt.widgets.Event; |
| |
| 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.core.resources.IFile; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| |
| import org.eclipse.text.tests.Accessor; |
| |
| 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.ui.PartInitException; |
| |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.IPackageFragment; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.JavaModelException; |
| |
| import org.eclipse.jdt.ui.PreferenceConstants; |
| |
| import org.eclipse.jdt.internal.ui.JavaPlugin; |
| import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor; |
| |
| /** |
| * Tests the automatic bracket insertion feature of the CUEditor. Also tests |
| * linked mode along the way. |
| * |
| * @since 3.1 |
| */ |
| public class BracketInserterTest { |
| @Rule |
| public TestName tn= new TestName(); |
| |
| private static final String SRC= "src"; |
| private static final String SEP= "/"; |
| private static final String CU_NAME= "PR75423.java"; |
| private static final String CU_CONTENTS= "package com.example.bugs;\n" + |
| "\n" + |
| "import java.lang.String;\n" + |
| "import java.lang.Integer;\n" + |
| "\n" + |
| "public class PR75423 {\n" + |
| " String string;\n" + |
| " Integer integer;\n" + |
| "\n" + |
| " public static void main(String[] args) {\n" + |
| " \n" + |
| " }\n" + |
| " void foo(String[] args) {\n" + |
| " \n" + |
| " }\n" + |
| " \n" + |
| " HashMap hm= new HashMap();\n" + |
| "}\n"; |
| |
| // document offsets |
| private static final int BODY_OFFSET= 196; |
| private static final int FIRST_COLUMN_OFFSET= 188; |
| private static final int ARGS_OFFSET= 171; |
| private static final int BRACKETS_OFFSET= 178; |
| private static final int MAIN_VOID_OFFSET= 161; |
| private static final int FOO_VOID_OFFSET= 207; |
| private static final int FIELD_OFFSET= 264; |
| |
| private JavaEditor fEditor; |
| private StyledText fTextWidget; |
| private IDocument fDocument; |
| private Accessor fAccessor; |
| private IJavaProject fProject; |
| |
| @Before |
| public void setUp() throws Exception { |
| IPreferenceStore store= JavaPlugin.getDefault().getPreferenceStore(); |
| store.setValue(PreferenceConstants.EDITOR_CLOSE_BRACKETS, true); |
| } |
| |
| private void setUpProject(String sourceLevel) throws CoreException, JavaModelException { |
| fProject= JavaProjectHelper.createJavaProject(tn.getMethodName(), "bin"); |
| fProject.setOption(JavaCore.COMPILER_SOURCE, sourceLevel); |
| JavaProjectHelper.addSourceContainer(fProject, SRC); |
| IPackageFragment fragment= fProject.findPackageFragment(new Path(SEP + tn.getMethodName() + SEP + SRC)); |
| fragment.createCompilationUnit(CU_NAME, CU_CONTENTS, true, new NullProgressMonitor()); |
| } |
| |
| private void setUpEditor() { |
| fEditor= openJavaEditor(new Path(SEP + tn.getMethodName() + SEP + SRC + SEP + CU_NAME)); |
| assertNotNull(fEditor); |
| fTextWidget= fEditor.getViewer().getTextWidget(); |
| assertNotNull(fTextWidget); |
| fAccessor= new Accessor(fTextWidget, StyledText.class); |
| fDocument= fEditor.getDocumentProvider().getDocument(fEditor.getEditorInput()); |
| assertNotNull(fDocument); |
| assertEquals(CU_CONTENTS, fDocument.get()); |
| } |
| |
| private JavaEditor openJavaEditor(IPath path) { |
| IFile file= ResourcesPlugin.getWorkspace().getRoot().getFile(path); |
| assertNotNull(file); |
| assertTrue(file.exists()); |
| try { |
| return (JavaEditor)EditorTestHelper.openInEditor(file, true); |
| } catch (PartInitException e) { |
| fail(); |
| return null; |
| } |
| } |
| |
| @After |
| public void tearDown() throws Exception { |
| EditorTestHelper.closeEditor(fEditor); |
| fEditor= null; |
| if (fProject != null) { |
| JavaProjectHelper.delete(fProject); |
| fProject= null; |
| } |
| |
| // reset to defaults |
| IPreferenceStore store= JavaPlugin.getDefault().getPreferenceStore(); |
| store.setValue(PreferenceConstants.EDITOR_CLOSE_BRACKETS, true); |
| } |
| |
| @Test |
| public void testInsertClosingParenthesis() throws BadLocationException, JavaModelException, CoreException, JavaModelException, CoreException { |
| use14(); |
| setCaret(BODY_OFFSET); |
| type('('); |
| |
| assertEquals("()", fDocument.get(BODY_OFFSET, 2)); |
| assertSingleLinkedPosition(BODY_OFFSET + 1); |
| } |
| |
| @Test |
| public void testDeletingParenthesis() throws JavaModelException, CoreException { |
| use14(); |
| setCaret(BODY_OFFSET); |
| type('('); |
| type(SWT.BS); |
| |
| assertEquals(CU_CONTENTS, fDocument.get()); |
| assertFalse(LinkedModeModel.hasInstalledModel(fDocument)); |
| } |
| |
| @Test |
| public void testMultipleParenthesisInsertion() throws BadLocationException, JavaModelException, CoreException { |
| use14(); |
| setCaret(BODY_OFFSET); |
| type("(((("); |
| |
| assertEquals("(((())))", fDocument.get(BODY_OFFSET, 8)); |
| assertEquals(BODY_OFFSET + 4, getCaret()); |
| |
| assertModel(true); |
| } |
| |
| @Test |
| public void testDeletingMultipleParenthesisInertion() throws BadLocationException, JavaModelException, CoreException { |
| use14(); |
| 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(CU_CONTENTS, fDocument.get()); |
| assertEquals(BODY_OFFSET, getCaret()); |
| |
| assertEquals(CU_CONTENTS, fDocument.get()); |
| assertFalse(LinkedModeModel.hasInstalledModel(fDocument)); |
| } |
| |
| @Test |
| public void testNoInsertInsideText() throws BadLocationException, JavaModelException, CoreException { |
| use14(); |
| setCaret(ARGS_OFFSET); |
| type('('); |
| |
| assertEquals("(St", fDocument.get(ARGS_OFFSET, 3)); |
| assertEquals(ARGS_OFFSET + 1, getCaret()); |
| assertFalse(LinkedModeModel.hasInstalledModel(fDocument)); |
| } |
| |
| @Test |
| public void testInsertInsideBrackets() throws BadLocationException, JavaModelException, CoreException { |
| use14(); |
| setCaret(BRACKETS_OFFSET); |
| type('('); |
| |
| assertEquals("()", fDocument.get(BRACKETS_OFFSET, 2)); |
| assertSingleLinkedPosition(BRACKETS_OFFSET + 1); |
| } |
| |
| @Test |
| public void testPeerEntry() throws BadLocationException, JavaModelException, CoreException { |
| use14(); |
| setCaret(BODY_OFFSET); |
| type("()"); |
| |
| assertEquals("()", fDocument.get(BODY_OFFSET, 2)); |
| assertEquals(BODY_OFFSET + 2, getCaret()); |
| |
| assertFalse(LinkedModeModel.hasInstalledModel(fDocument)); |
| } |
| |
| // public void testLoop() throws Exception { |
| // for (int i= 0; i < 50; i++) { |
| // setUp(); |
| // testExitOnTab(); |
| // tearDown(); |
| // } |
| // } |
| // |
| @Test |
| public void testMultiplePeerEntry() throws BadLocationException, JavaModelException, CoreException { |
| use14(); |
| 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)); |
| } |
| |
| @Test |
| public void testExitOnTab() throws BadLocationException, JavaModelException, CoreException { |
| use14(); |
| 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)); |
| } |
| |
| @Test |
| public void testExitOnReturn() throws BadLocationException, JavaModelException, CoreException { |
| use14(); |
| 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)); |
| } |
| |
| @Test |
| public void testExitOnEsc() throws BadLocationException, JavaModelException, CoreException { |
| use14(); |
| 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)); |
| } |
| |
| @Test |
| public void testInsertClosingQuote() throws BadLocationException, JavaModelException, CoreException { |
| use14(); |
| setCaret(BODY_OFFSET); |
| type('"'); |
| |
| assertEquals("\"\"", fDocument.get(BODY_OFFSET, 2)); |
| |
| assertSingleLinkedPosition(BODY_OFFSET + 1); |
| } |
| |
| @Test |
| public void testPreferences() throws BadLocationException, JavaModelException, CoreException { |
| use14(); |
| IPreferenceStore store= JavaPlugin.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)); |
| } |
| |
| @Test |
| public void testAngleBracketsAsOperator() throws Exception { |
| use15(); |
| setCaret(BODY_OFFSET); |
| type("test<"); |
| |
| assertEquals("test<", fDocument.get(BODY_OFFSET, 5)); |
| assertNotEquals(">", fDocument.get(BODY_OFFSET + 5, 1)); |
| assertEquals(BODY_OFFSET + 5, getCaret()); |
| |
| assertFalse(LinkedModeModel.hasInstalledModel(fDocument)); |
| } |
| |
| @Test |
| public void testAngleBracketsIn14Project() throws BadLocationException, JavaModelException, CoreException { |
| use14(); |
| setCaret(BODY_OFFSET); |
| type("Test<"); |
| |
| assertEquals("Test<", fDocument.get(BODY_OFFSET, 5)); |
| assertNotEquals(">", fDocument.get(BODY_OFFSET + 5, 1)); |
| assertEquals(BODY_OFFSET + 5, getCaret()); |
| |
| assertFalse(LinkedModeModel.hasInstalledModel(fDocument)); |
| } |
| |
| @Test |
| public void testAngleBracketsIn15Project() throws Exception { |
| use15(); |
| |
| setCaret(BODY_OFFSET); |
| type("Test<"); |
| |
| assertEquals("Test<>", fDocument.get(BODY_OFFSET, 6)); |
| assertSingleLinkedPosition(BODY_OFFSET + 5); |
| } |
| |
| @Test |
| public void testAngleBracketsInFieldDecl15() throws Exception { |
| use15(); |
| |
| setCaret(FIELD_OFFSET); |
| type('<'); |
| |
| assertEquals("HashMap<> hm", fDocument.get(FIELD_OFFSET - 7, 12)); |
| assertSingleLinkedPosition(FIELD_OFFSET + 1); |
| } |
| |
| @Test |
| public void testAngleBracketsInsideMethodDecl15() throws Exception { |
| use15(); |
| |
| setCaret(MAIN_VOID_OFFSET); |
| type('<'); |
| |
| assertEquals("public static <>void", fDocument.get(MAIN_VOID_OFFSET - 14, 20)); |
| assertSingleLinkedPosition(MAIN_VOID_OFFSET + 1); |
| } |
| |
| @Test |
| public void testAngleBracketsBeforeMethodDecl15() throws Exception { |
| use15(); |
| |
| setCaret(FOO_VOID_OFFSET); |
| |
| type('<'); |
| |
| assertEquals("<>void foo", fDocument.get(FOO_VOID_OFFSET, 10)); |
| assertSingleLinkedPosition(FOO_VOID_OFFSET + 1); |
| } |
| |
| @Test |
| public void testAngleBracketsBeforeTypeArgument15() throws Exception { |
| use15(); |
| |
| String PRE= "new ArrayList"; |
| String POST= "String>();"; |
| |
| fDocument.replace(BODY_OFFSET, 0, PRE + POST); |
| setCaret(BODY_OFFSET + PRE.length()); |
| |
| type('<'); |
| |
| assertEquals(PRE + '<' + POST, fDocument.get(BODY_OFFSET, PRE.length() + 1 + POST.length())); |
| assertFalse(LinkedModeModel.hasInstalledModel(fDocument)); |
| } |
| |
| @Test |
| public void testAngleBracketsBeforeWildcard15() throws Exception { |
| use15(); |
| |
| String PRE= "new ArrayList"; |
| String POST= "? extends Number>();"; |
| |
| fDocument.replace(BODY_OFFSET, 0, PRE + POST); |
| setCaret(BODY_OFFSET + PRE.length()); |
| |
| type('<'); |
| |
| assertEquals(PRE + '<' + POST, fDocument.get(BODY_OFFSET, PRE.length() + 1 + POST.length())); |
| assertFalse(LinkedModeModel.hasInstalledModel(fDocument)); |
| } |
| |
| @Test |
| public void testAngleBracketsAfterIdentifierOnFirstColumn1_15() throws Exception { |
| //https://bugs.eclipse.org/bugs/show_bug.cgi?id=347734 |
| use15(); |
| |
| String PRE= "x"; |
| |
| fDocument.replace(FIRST_COLUMN_OFFSET, 0, PRE); |
| setCaret(FIRST_COLUMN_OFFSET + PRE.length()); |
| |
| type('<'); |
| |
| assertEquals(PRE + "< ", fDocument.get(FIRST_COLUMN_OFFSET, PRE.length() + 2)); |
| assertFalse(LinkedModeModel.hasInstalledModel(fDocument)); |
| } |
| |
| @Test |
| public void testAngleBracketsAfterIdentifierOnFirstColumn2_15() throws Exception { |
| //https://bugs.eclipse.org/bugs/show_bug.cgi?id=347734 |
| use15(); |
| |
| String PRE= "List"; |
| |
| fDocument.replace(FIRST_COLUMN_OFFSET, 0, PRE); |
| setCaret(FIRST_COLUMN_OFFSET + PRE.length()); |
| |
| type('<'); |
| |
| assertEquals(PRE + "<>", fDocument.get(FIRST_COLUMN_OFFSET, PRE.length() + 2)); |
| assertSingleLinkedPosition(FIRST_COLUMN_OFFSET + PRE.length() + 1); |
| } |
| |
| /* 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()); |
| } |
| |
| private void use15() throws CoreException, JavaModelException { |
| setUpProject(JavaCore.VERSION_1_5); |
| setUpEditor(); |
| } |
| |
| private void use14() throws CoreException, JavaModelException { |
| setUpProject(JavaCore.VERSION_1_4); |
| setUpEditor(); |
| } |
| |
| /** |
| * 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() { |
| @Override |
| public void left(LinkedModeModel model, int flags) { |
| exitFlags[0]= flags; |
| } |
| @Override |
| public void resume(LinkedModeModel model, int flags) { |
| } |
| @Override |
| 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); |
| } |
| } |