/******************************************************************************* * Copyright (c) 2000, 2015 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.jdt.text.tests.contentassist; import java.util.Hashtable; import org.eclipse.jdt.testplugin.JavaProjectHelper; import org.eclipse.jdt.testplugin.TestOptions; import org.eclipse.jdt.text.tests.performance.EditorTestHelper; import org.eclipse.swt.graphics.Point; import org.eclipse.core.runtime.CoreException; import org.eclipse.text.tests.Accessor; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextOperationTarget; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.contentassist.ContentAssistant; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; import org.eclipse.jface.text.contentassist.IContentAssistProcessor; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.ui.texteditor.ITextEditor; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants; import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility; import org.eclipse.jdt.internal.corext.template.java.CodeTemplateContextType; import org.eclipse.jdt.ui.PreferenceConstants; import org.eclipse.jdt.ui.text.IJavaPartitions; import org.eclipse.jdt.internal.ui.JavaPlugin; import org.eclipse.jdt.internal.ui.JavaUIMessages; import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility; import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor; import org.eclipse.jdt.internal.ui.text.java.ContentAssistProcessor; import org.eclipse.jdt.internal.ui.text.java.JavaCompletionProcessor; import junit.framework.TestCase; public class AbstractCompletionTest extends TestCase { protected static String suiteName(Class<?> fqn) { String name= fqn.toString(); name= name.substring(name.lastIndexOf('.') + 1); return name; } protected static final String CARET= "|"; private ICompilationUnit fCU; private JavaEditor fEditor; private String fBeforeImports; private String fAfterImports; private String fMembers; private String fLocals; private char fTrigger; private boolean fWaitBeforeCompleting; @Override protected void setUp() throws Exception { Hashtable<String, String> options= TestOptions.getDefaultOptions(); configureCoreOptions(options); JavaCore.setOptions(options); IPreferenceStore store= getJDTUIPrefs(); store.setValue(PreferenceConstants.CODEGEN_ADD_COMMENTS, true); store.setValue(PreferenceConstants.CODEASSIST_GUESS_METHOD_ARGUMENTS, false); store.setValue(PreferenceConstants.CODEASSIST_FILL_ARGUMENT_NAMES, false); store.setValue(PreferenceConstants.CODEASSIST_ADDIMPORT, true); store.setValue(PreferenceConstants.CODEASSIST_INSERT_COMPLETION, true); StubUtility.setCodeTemplate(CodeTemplateContextType.OVERRIDECOMMENT_ID, "/* (non-Javadoc)\n * ${see_to_overridden}\n */", null); StubUtility.setCodeTemplate(CodeTemplateContextType.METHODSTUB_ID, "//TODO\n${body_statement}", null); StubUtility.setCodeTemplate(CodeTemplateContextType.CONSTRUCTORCOMMENT_ID, "/**\n * Constructor.\n */", null); StubUtility.setCodeTemplate(CodeTemplateContextType.METHODCOMMENT_ID, "/**\n * Method.\n */", null); StubUtility.setCodeTemplate(CodeTemplateContextType.CONSTRUCTORSTUB_ID, "//TODO\n${body_statement}", null); fBeforeImports= ""; fAfterImports= ""; fMembers= ""; fLocals= ""; fTrigger= '\0'; fWaitBeforeCompleting= false; } protected IPackageFragment getAnonymousTestPackage() throws CoreException { return CompletionTestSetup.getAnonymousTestPackage(); } protected void configureCoreOptions(Hashtable<String, String> options) { options.put(DefaultCodeFormatterConstants.FORMATTER_NUMBER_OF_EMPTY_LINES_TO_PRESERVE, "1"); options.put(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, JavaCore.SPACE); options.put(DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE, "4"); options.put(JavaCore.CODEASSIST_CAMEL_CASE_MATCH, JavaCore.DISABLED); options.put(JavaCore.CODEASSIST_FIELD_PREFIXES, "f"); } protected void setCoreOption(String key, String value) { Hashtable<String, String> options= JavaCore.getOptions(); options.put(key, value); JavaCore.setOptions(options); } protected void waitBeforeCompleting(boolean wait) { fWaitBeforeCompleting= wait; } protected IPreferenceStore getJDTUIPrefs() { IPreferenceStore store= JavaPlugin.getDefault().getPreferenceStore(); return store; } private ICompilationUnit createCU(IPackageFragment pack1, String contents) throws JavaModelException { ICompilationUnit cu= pack1.createCompilationUnit("Completion_" + getName() + ".java", contents, false, null); return cu; } @Override protected void tearDown() throws Exception { IPreferenceStore store= getJDTUIPrefs(); store.setToDefault(PreferenceConstants.CODEGEN_ADD_COMMENTS); store.setToDefault(PreferenceConstants.CODEASSIST_GUESS_METHOD_ARGUMENTS); store.setToDefault(PreferenceConstants.CODEASSIST_FILL_ARGUMENT_NAMES); store.setToDefault(PreferenceConstants.CODEASSIST_INSERT_COMPLETION); store.setToDefault(PreferenceConstants.CODEASSIST_ADDIMPORT); store.setToDefault(PreferenceConstants.EDITOR_CLOSE_BRACKETS); store.setToDefault(PreferenceConstants.CODEASSIST_INSERT_COMPLETION); store.setToDefault(PreferenceConstants.CODEASSIST_PREFIX_COMPLETION); store.setToDefault(PreferenceConstants.CODEASSIST_AUTOINSERT); if (fEditor != null) { EditorTestHelper.closeEditor(fEditor); fEditor= null; } if (fCU != null) { JavaProjectHelper.delete(fCU); fCU= null; } JavaProjectHelper.emptyDisplayLoop(); } protected void addImport(String imp) { imp= normalizeImport(imp); if (fBeforeImports.length() == 0) fBeforeImports= "\n" + imp + "\n"; else fBeforeImports += imp + "\n"; } protected void expectImport(String imp) { imp= normalizeImport(imp); if (fAfterImports.length() == 0) fAfterImports= "\n" + imp + "\n"; else fAfterImports += imp + "\n"; } private String normalizeImport(String imp) { if (!imp.startsWith("import ")) imp= "import " + imp; if (!imp.endsWith(";")) imp+= ";"; return imp; } protected void addMembers(String member) { fMembers += "\n " + member + "\n"; } protected void addLocalVariables(String variable) { fLocals += "\n " + variable + "\n"; } protected void setTrigger(char trigger) { fTrigger= trigger; } /** * Creates a CU with a method containing <code>before</code>, then runs code assist and applies * the first proposal whose display name matches <code>selector</code> and asserts that the * method's body now has the content of <code>expected</code>. * * @param before the contents of the javadoc line before code completion is run * @param expected the expected contents of the type javadoc line * @param selector the prefix to match a proposal with * @throws CoreException if asserting the proposal failed */ protected void assertMethodBodyProposal(String before, String selector, String expected) throws CoreException { StringBuffer contents= new StringBuffer(); IRegion preSelection= assembleMethodBodyTestCUExtractSelection(contents, before, fBeforeImports); StringBuffer result= new StringBuffer(); IRegion expectedSelection= assembleMethodBodyTestCUExtractSelection(result, expected, fAfterImports); assertProposal(selector, contents, preSelection, result, expectedSelection); } /** * Creates a CU with a method containing <code>before</code>, then runs incremental code assist * and asserts that the method's body now has the content of <code>expected</code>. * * @param expected the expected contents of the type javadoc line * @param before the contents of the javadoc line before code completion is run * @throws CoreException if a failure happened during assertion */ protected void assertMethodBodyIncrementalCompletion(String before, String expected) throws CoreException { StringBuffer contents= new StringBuffer(); IRegion preSelection= assembleMethodBodyTestCUExtractSelection(contents, before, fBeforeImports); StringBuffer result= new StringBuffer(); IRegion expectedSelection= assembleMethodBodyTestCUExtractSelection(result, expected, fAfterImports); assertIncrementalCompletion(contents, preSelection, result, expectedSelection); } /** * Creates a CU with a method containing <code>before</code>, then runs code assist and applies * the first proposal whose display name matches <code>selector</code> and asserts that the * method's body now has the content of <code>expected</code>. * * @param before the contents of the javadoc line before code completion is run * @param expected the expected contents of the type javadoc line * @param selector the prefix to match a proposal with * @throws CoreException if asserting the proposal failed */ protected void assertMemberJavadocProposal(String before, String selector, String expected) throws CoreException { StringBuffer contents= new StringBuffer(); IRegion preSelection= assembleMethodJavadocTestCUExtractSelection(contents, before); StringBuffer result= new StringBuffer(); IRegion expectedSelection= assembleMethodJavadocTestCUExtractSelection(result, expected); assertProposal(selector, contents, preSelection, result, expectedSelection); } /** * Creates a CU with a class containing <code>before</code>, then runs code assist and applies * the first proposal whose display name matches <code>selector</code> and asserts that the * method's body now has the content of <code>expected</code>. * * @param before the contents of the class body line before code completion is run * @param expected the expected contents of the class body * @param selector the prefix to match a proposal with * @throws CoreException if asserting the proposal failed */ protected void assertTypeBodyProposal(String before, String selector, String expected) throws CoreException { StringBuffer contents= new StringBuffer(); IRegion preSelection= assembleClassBodyTestCUExtractSelection(contents, before, fBeforeImports); StringBuffer result= new StringBuffer(); IRegion expectedSelection= assembleClassBodyTestCUExtractSelection(result, expected, fAfterImports); assertProposal(selector, contents, preSelection, result, expectedSelection); } /** * Creates a CU with a class containing <code>before</code>, then runs code assist and asserts * that there is no proposal. * * @param before the contents of the class body line before code completion is run * @param selector the prefix to match a proposal with * @throws CoreException if asserting the proposal failed * @since 3.6 */ protected void assertNoTypeBodyProposal(String before, String selector) throws CoreException { StringBuffer contents= new StringBuffer(); IRegion preSelection= assembleClassBodyTestCUExtractSelection(contents, before, fBeforeImports); assertNoProposal(selector, contents, preSelection); } /** * Creates a CU with a method containing <code>before</code>, then runs code assist and applies * the first proposal whose display name matches <code>selector</code> and asserts that the * method's body now has the content of <code>expected</code>. * * @param expected the expected contents of the type javadoc line * @param before the contents of the line where before code completion is run * @param selector the prefix to match a proposal with * @throws CoreException if asserting the proposal failed */ protected void assertTypeJavadocProposal(String before, String selector, String expected) throws CoreException { StringBuffer contents= new StringBuffer(); IRegion preSelection= assembleClassJavadocTestCUExtractSelection(contents, before, fBeforeImports); StringBuffer result= new StringBuffer(); IRegion expectedSelection= assembleClassJavadocTestCUExtractSelection(result, expected, fAfterImports); assertProposal(selector, contents, preSelection, result, expectedSelection); } /** * Creates a CU with a method containing <code>before</code>, then runs code assist and asserts * that there is no proposal starting with selector. * * @param before the contents of the javadoc line before code completion is run * @param selector the prefix to match a proposal with * @throws CoreException if a failure happened during assertion */ protected void assertNoMethodBodyProposals(String before, String selector) throws CoreException { StringBuffer contents= new StringBuffer(); IRegion preSelection= assembleMethodBodyTestCUExtractSelection(contents, before, fBeforeImports); assertNoProposal(selector, contents, preSelection); } private void assertProposal(String selector, StringBuffer contents, IRegion preSelection, StringBuffer result, IRegion expectedSelection) throws CoreException { fCU= createCU(getAnonymousTestPackage(), contents.toString()); fEditor= (JavaEditor) EditorUtility.openInEditor(fCU); IDocument doc; ITextSelection postSelection; try { ICompletionProposal proposal= findNonNullProposal(selector, preSelection); doc= fEditor.getDocumentProvider().getDocument(fEditor.getEditorInput()); apply(fEditor, doc, proposal, preSelection); postSelection= (ITextSelection) fEditor.getSelectionProvider().getSelection(); } finally { EditorTestHelper.closeEditor(fEditor); fEditor= null; } assertEquals(result.toString(), doc.get()); assertEquals(expectedSelection.getOffset(), postSelection.getOffset()); assertEquals(expectedSelection.getLength(), postSelection.getLength()); } private void assertIncrementalCompletion(StringBuffer contents, IRegion preSelection, StringBuffer result, IRegion expectedSelection) throws CoreException { fCU= createCU(getAnonymousTestPackage(), contents.toString()); fEditor= (JavaEditor) EditorUtility.openInEditor(fCU); IDocument doc; ITextSelection postSelection; try { incrementalAssist(preSelection); doc= fEditor.getDocumentProvider().getDocument(fEditor.getEditorInput()); postSelection= (ITextSelection) fEditor.getSelectionProvider().getSelection(); } finally { EditorTestHelper.closeEditor(fEditor); fEditor= null; } assertEquals(result.toString(), doc.get()); assertEquals(expectedSelection.getOffset(), postSelection.getOffset()); assertEquals(expectedSelection.getLength(), postSelection.getLength()); } private void assertNoProposal(String selector, StringBuffer contents, IRegion preSelection) throws CoreException { fCU= createCU(getAnonymousTestPackage(), contents.toString()); fEditor= (JavaEditor) EditorUtility.openInEditor(fCU); try { assertNull(findNamedProposal(selector, preSelection)); } finally { EditorTestHelper.closeEditor(fEditor); fEditor= null; } } private IRegion assembleMethodBodyTestCUExtractSelection(StringBuffer buffer, String javadocLine, String imports) { String prefix= "package test1;\n" + imports + "\n" + "public class Completion_" + getName() + "<T> {\n" + " public void testMethod(int param) {\n" + fLocals + " "; String postfix= "\n" + " }\n" + fMembers + "}\n"; StringBuffer lineBuffer= new StringBuffer(javadocLine); int firstPipe= lineBuffer.indexOf(CARET); int secondPipe; if (firstPipe == -1) { firstPipe= lineBuffer.length(); secondPipe= firstPipe; } else { lineBuffer.replace(firstPipe, firstPipe + CARET.length(), ""); secondPipe= lineBuffer.indexOf(CARET, firstPipe); if (secondPipe ==-1) secondPipe= firstPipe; else lineBuffer.replace(secondPipe, secondPipe + CARET.length(), ""); } buffer.append(prefix + lineBuffer + postfix); return new Region(firstPipe + prefix.length(), secondPipe - firstPipe); } private IRegion assembleClassBodyTestCUExtractSelection(StringBuffer buffer, String javadocLine, String imports) { String prefix= "package test1;\n" + imports + "\n" + "public class Completion_" + getName() + "<T> {\n" + fLocals + " "; String postfix= "\n" + "\n" + fMembers + "}\n"; StringBuffer lineBuffer= new StringBuffer(javadocLine); int firstPipe= lineBuffer.indexOf(CARET); int secondPipe; if (firstPipe == -1) { firstPipe= lineBuffer.length(); secondPipe= firstPipe; } else { lineBuffer.replace(firstPipe, firstPipe + CARET.length(), ""); secondPipe= lineBuffer.indexOf(CARET, firstPipe); if (secondPipe ==-1) secondPipe= firstPipe; else lineBuffer.replace(secondPipe, secondPipe + CARET.length(), ""); } buffer.append(prefix + lineBuffer + postfix); return new Region(firstPipe + prefix.length(), secondPipe - firstPipe); } private IRegion assembleClassJavadocTestCUExtractSelection(StringBuffer buffer, String javadocLine, String imports) { String prefix= "package test1;\n" + imports + "\n" + "/**\n"; String postfix= "\n" + " */\n" + "public class Completion_" + getName() + "<T> {\n" + "\n" + fMembers + "}\n"; StringBuffer lineBuffer= new StringBuffer(javadocLine); int firstPipe= lineBuffer.indexOf(CARET); int secondPipe; if (firstPipe == -1) { firstPipe= lineBuffer.length(); secondPipe= firstPipe; } else { lineBuffer.replace(firstPipe, firstPipe + CARET.length(), ""); secondPipe= lineBuffer.indexOf(CARET, firstPipe); if (secondPipe ==-1) secondPipe= firstPipe; else lineBuffer.replace(secondPipe, secondPipe + CARET.length(), ""); } buffer.append(prefix + lineBuffer + postfix); return new Region(firstPipe + prefix.length(), secondPipe - firstPipe); } private IRegion assembleMethodJavadocTestCUExtractSelection(StringBuffer buffer, String javadocLine) { String prefix= "package test1;\n" + "\n" + "public class Completion_" + getName() + "<T> {\n" + " /**\n "; String postfix= "\n" + " */\n" + fMembers + "}\n"; StringBuffer lineBuffer= new StringBuffer(javadocLine); int firstPipe= lineBuffer.indexOf(CARET); int secondPipe; if (firstPipe == -1) { firstPipe= lineBuffer.length(); secondPipe= firstPipe; } else { lineBuffer.replace(firstPipe, firstPipe + CARET.length(), ""); secondPipe= lineBuffer.indexOf(CARET, firstPipe); if (secondPipe ==-1) secondPipe= firstPipe; else lineBuffer.replace(secondPipe, secondPipe + CARET.length(), ""); } buffer.append(prefix + lineBuffer + postfix); return new Region(firstPipe + prefix.length(), secondPipe - firstPipe); } private ICompletionProposal findNonNullProposal(String prefix, IRegion selection) { ICompletionProposal proposal= findNamedProposal(prefix, selection); assertNotNull("no proposal starting with \"" + prefix + "\"", proposal); return proposal; } private ICompletionProposal findNamedProposal(String prefix, IRegion selection) { ICompletionProposal[] proposals= collectProposals(selection); ICompletionProposal found= null; for (int i= 0; i < proposals.length; i++) { String displayString= proposals[i].getDisplayString(); if (displayString.startsWith(prefix)) { if (found == null || displayString.equals(prefix)) found= proposals[i]; } } return found; } private ICompletionProposal[] collectProposals(IRegion selection) { waitBeforeCoreCompletion(); ContentAssistant assistant= new ContentAssistant(); assistant.setDocumentPartitioning(IJavaPartitions.JAVA_PARTITIONING); JavaCompletionProcessor javaProcessor= new JavaCompletionProcessor(fEditor, assistant, getContentType()); // Must ensure same setup of the processor as when using via UI Accessor accessor= new Accessor(javaProcessor, ContentAssistProcessor.class); accessor.invoke("setCategoryIteration", new Object[0]); accessor.set("fRepetition", 0); ICompletionProposal[] proposals= javaProcessor.computeCompletionProposals(fEditor.getViewer(), selection.getOffset()); final String errorMessage= javaProcessor.getErrorMessage(); if (errorMessage != null && !JavaUIMessages.JavaEditor_codeassist_noCompletions.equals(errorMessage)) fail(errorMessage); return proposals; } private void incrementalAssist(IRegion selection) { waitBeforeCoreCompletion(); ContentAssistant assistant= new ContentAssistant(); assistant.enableAutoInsert(true); final ISourceViewer viewer= fEditor.getViewer(); viewer.setSelectedRange(selection.getOffset(), selection.getLength()); assistant.install(viewer); assistant.setDocumentPartitioning(IJavaPartitions.JAVA_PARTITIONING); IContentAssistProcessor javaProcessor= new JavaCompletionProcessor(fEditor, assistant, getContentType()); assistant.setContentAssistProcessor(javaProcessor, getContentType()); assistant.completePrefix(); final String errorMessage= javaProcessor.getErrorMessage(); if (errorMessage != null && !JavaUIMessages.JavaEditor_codeassist_noCompletions.equals(errorMessage)) fail(errorMessage); assistant.uninstall(); } /** * Invokes {@link Thread#sleep(long)} if {@link #waitBeforeCompleting(boolean)} was set to * <code>true</code> or camel case completions are enabled. For some reasons, inner types and * camel case matches don't show up otherwise. * * @since 3.2 */ private void waitBeforeCoreCompletion() { // Make sure indexer is up to date try { JavaProjectHelper.mustPerformDummySearch(); } catch (JavaModelException e) { fail("could not synchronize with indexer"); } if (fWaitBeforeCompleting || JavaCore.ENABLED.equals(JavaCore.getOption(JavaCore.CODEASSIST_CAMEL_CASE_MATCH))) { try { Thread.sleep(2000); } catch (InterruptedException x) { } } } protected String getContentType() { return IDocument.DEFAULT_CONTENT_TYPE; } private void apply(ITextEditor editor, IDocument doc, ICompletionProposal proposal, IRegion selection) { if (proposal instanceof ICompletionProposalExtension2) { ICompletionProposalExtension2 ext= (ICompletionProposalExtension2) proposal; ITextViewer viewer= (ITextViewer) editor.getAdapter(ITextOperationTarget.class); ext.selected(viewer, false); viewer.setSelectedRange(selection.getOffset(), selection.getLength()); ext.unselected(viewer); ext.apply(viewer, fTrigger, 0, selection.getOffset()); Point range= proposal.getSelection(doc); if (range != null) viewer.setSelectedRange(range.x, range.y); } else if (proposal instanceof ICompletionProposalExtension) { ICompletionProposalExtension ext= (ICompletionProposalExtension) proposal; ext.apply(doc, fTrigger, selection.getOffset() + selection.getLength()); } else { proposal.apply(doc); } } }