| /******************************************************************************* |
| * Copyright (c) 2012, 2017 NumberFour AG 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: |
| * NumberFour AG - initial API and Implementation (Alex Panchenko) |
| *******************************************************************************/ |
| package org.eclipse.dltk.core.tests; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.List; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.dltk.codeassist.ICompletionEngine; |
| import org.eclipse.dltk.codeassist.ISelectionEngine; |
| import org.eclipse.dltk.codeassist.ISelectionRequestor; |
| import org.eclipse.dltk.compiler.env.IModuleSource; |
| import org.eclipse.dltk.core.CompletionProposal; |
| import org.eclipse.dltk.core.CompletionRequestor; |
| import org.eclipse.dltk.core.DLTKCore; |
| import org.eclipse.dltk.core.IModelElement; |
| import org.eclipse.dltk.core.ISourceModule; |
| import org.eclipse.dltk.core.ISourceRange; |
| import org.eclipse.dltk.core.ModelException; |
| import org.eclipse.dltk.core.tests.util.StringList; |
| import org.eclipse.osgi.util.NLS; |
| import org.junit.Assert; |
| |
| public class CodeAssistUtil { |
| |
| static abstract class Module<M> { |
| final M module; |
| |
| public Module(M module) { |
| this.module = module; |
| } |
| |
| abstract String getSource(); |
| |
| abstract String getName(); |
| |
| abstract IModelElement[] codeSelect(int offset, int length) |
| throws ModelException; |
| |
| abstract void codeComplete(int offset, CompletionRequestor requestor) |
| throws ModelException; |
| |
| } |
| |
| private static class SourceModule extends Module<ISourceModule> { |
| |
| public SourceModule(ISourceModule module) { |
| super(module); |
| } |
| |
| @Override |
| String getSource() { |
| try { |
| return module.getSource(); |
| } catch (ModelException e) { |
| throw new IllegalStateException(e); |
| } |
| } |
| |
| @Override |
| String getName() { |
| return module.getElementName(); |
| } |
| |
| @Override |
| IModelElement[] codeSelect(int offset, int length) |
| throws ModelException { |
| return module.codeSelect(offset, length); |
| } |
| |
| @Override |
| void codeComplete(int offset, CompletionRequestor requestor) |
| throws ModelException { |
| module.codeComplete(offset, requestor); |
| } |
| } |
| |
| private static class Source extends Module<IModuleSource> { |
| public Source(IModuleSource source) { |
| super(source); |
| } |
| |
| @Override |
| String getSource() { |
| return module.getSourceContents(); |
| } |
| |
| @Override |
| String getName() { |
| return module.getFileName(); |
| } |
| |
| @Override |
| IModelElement[] codeSelect(int offset, int length) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| void codeComplete(int offset, CompletionRequestor requestor) |
| throws ModelException { |
| throw new UnsupportedOperationException(); |
| } |
| } |
| |
| private final Module<?> module; |
| private Integer offset; |
| private int length; |
| |
| private CodeAssistUtil(Module<?> module) { |
| this.module = module; |
| } |
| |
| public static CodeAssistUtil on(IFile file) { |
| return on(DLTKCore.createSourceModuleFrom(file)); |
| } |
| |
| public static CodeAssistUtil on(ISourceModule module) { |
| return new CodeAssistUtil(new SourceModule(module)); |
| } |
| |
| public static CodeAssistUtil on(IModuleSource module) { |
| return new CodeAssistUtil(new Source(module)); |
| } |
| |
| public CodeAssistUtil after(String marker) { |
| return calculateOffset(marker, false, true); |
| } |
| |
| public CodeAssistUtil afterLast(String marker) { |
| return calculateOffset(marker, true, true); |
| } |
| |
| public CodeAssistUtil before(String marker) { |
| return calculateOffset(marker, false, false); |
| } |
| |
| public CodeAssistUtil beforeLast(String marker) { |
| return calculateOffset(marker, true, false); |
| } |
| |
| private CodeAssistUtil calculateOffset(String marker, boolean last, |
| boolean after) { |
| final String text = module.getSource(); |
| final int offset = last ? text.lastIndexOf(marker) : text |
| .indexOf(marker); |
| Assert.assertTrue( |
| NLS.bind("Pattern \"{0}\" not found in {1}", marker, |
| module.getName()), offset != -1); |
| this.offset = offset + (after ? marker.length() : 0); |
| return this; |
| } |
| |
| public CodeAssistUtil length(int length) { |
| this.length = length; |
| return this; |
| } |
| |
| public int length() { |
| return length; |
| } |
| |
| public CodeAssistUtil offset(int offset) { |
| this.offset = offset; |
| return this; |
| } |
| |
| public int offset() { |
| return offset; |
| } |
| |
| public IModelElement[] codeSelect() throws ModelException { |
| return module.codeSelect(offset, length); |
| } |
| |
| private IModuleSource getModuleSource() { |
| return (IModuleSource) module.module; |
| } |
| |
| /** |
| * Value class for completion proposals returned by |
| * {@link CodeAssistUtil#codeComplete()} methods. |
| */ |
| public class CodeCompletionResult { |
| |
| private final List<CompletionProposal> proposals; |
| |
| public CodeCompletionResult(List<CompletionProposal> proposals) { |
| this.proposals = proposals; |
| } |
| |
| public int size() { |
| return proposals.size(); |
| } |
| |
| public CompletionProposal get(int index) { |
| return proposals.get(index); |
| } |
| |
| private boolean compareProposalNames(String[] names) { |
| if (names.length != proposals.size()) { |
| return false; |
| } |
| final CompletionProposal[] sorted = proposals |
| .toArray(new CompletionProposal[proposals.size()]); |
| Arrays.sort(sorted, (pr, pr1) -> pr.getName().compareTo(pr1.getName())); |
| final String[] sortedNames = new String[names.length]; |
| System.arraycopy(names, 0, sortedNames, 0, names.length); |
| Arrays.sort(sortedNames); |
| for (int i = 0, size = proposals.size(); i < size; ++i) { |
| if (!names[i].equals(proposals.get(i).getName())) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private StringList exractProposalNames(boolean withKinds) { |
| final StringList list = new StringList(proposals.size()); |
| for (int i = 0, size = proposals.size(); i < size; ++i) { |
| final CompletionProposal proposal = proposals.get(i); |
| String name = proposal.getName(); |
| if (withKinds |
| && proposal.getKind() == CompletionProposal.METHOD_REF) { |
| name += "()"; |
| } |
| list.add(name); |
| } |
| return list; |
| } |
| |
| public void assertEquals(String... expectedProposalNames) { |
| if (!compareProposalNames(expectedProposalNames)) { |
| Assert.assertEquals(new StringList(expectedProposalNames) |
| .sort().toString(), exractProposalNames(false).sort() |
| .toString()); |
| } |
| } |
| |
| /** |
| * Finds the proposal with the specified name or throws exception if no |
| * such proposal found. |
| */ |
| public CompletionProposal get(String name) |
| throws IllegalArgumentException { |
| for (CompletionProposal proposal : proposals) { |
| if (name.equals(proposal.getName())) { |
| return proposal; |
| } |
| } |
| throw new IllegalArgumentException(NLS.bind( |
| "\"{0}\" completion proposal not found", name)); |
| } |
| |
| /** |
| * Finds the proposal with the specified kind and name or throws |
| * exception if no such proposal found. |
| */ |
| public CompletionProposal get(int kind, String name) |
| throws IllegalArgumentException { |
| for (CompletionProposal proposal : proposals) { |
| if (kind == proposal.getKind() |
| && name.equals(proposal.getName())) { |
| return proposal; |
| } |
| } |
| throw new IllegalArgumentException(NLS.bind( |
| "\"{0}:{1}\" completion proposal not found", kind, name)); |
| } |
| } |
| |
| public CodeCompletionResult codeComplete(ICompletionEngine engine) { |
| final List<CompletionProposal> proposals = new ArrayList<CompletionProposal>(); |
| engine.setRequestor(new TestCompletionRequestor(proposals)); |
| engine.complete(getModuleSource(), offset(), 0); |
| return new CodeCompletionResult(proposals); |
| } |
| |
| /** |
| * Performs code completion in this source module. |
| */ |
| public CodeCompletionResult codeComplete() throws ModelException { |
| final List<CompletionProposal> proposals = new ArrayList<CompletionProposal>(); |
| module.codeComplete(offset(), new TestCompletionRequestor(proposals)); |
| return new CodeCompletionResult(proposals); |
| } |
| |
| public Object[] codeSelectAll(ISelectionEngine engine) { |
| final List<Object> elements = new ArrayList<Object>(); |
| engine.setRequestor(new ISelectionRequestor() { |
| @Override |
| public void acceptModelElement(IModelElement element) { |
| elements.add(element); |
| } |
| |
| @Override |
| public void acceptForeignElement(Object element) { |
| elements.add(element); |
| } |
| |
| @Override |
| public void acceptElement(Object element, ISourceRange range) { |
| elements.add(element); |
| } |
| }); |
| final IModelElement[] result = engine.select(getModuleSource(), offset, |
| offset); |
| if (result != null) { |
| Collections.addAll(elements, result); |
| } |
| return elements.toArray(); |
| } |
| |
| public IModelElement[] codeSelect(ISelectionEngine engine) { |
| final List<IModelElement> elements = new ArrayList<IModelElement>(); |
| engine.setRequestor(new ISelectionRequestor() { |
| |
| @Override |
| public void acceptModelElement(IModelElement element) { |
| elements.add(element); |
| } |
| |
| @Override |
| public void acceptForeignElement(Object element) { |
| if (element instanceof IModelElement) { |
| elements.add((IModelElement) element); |
| } |
| } |
| |
| @Override |
| public void acceptElement(Object element, ISourceRange range) { |
| acceptForeignElement(element); |
| } |
| }); |
| final IModelElement[] result = engine.select(getModuleSource(), offset, |
| offset); |
| if (result != null) { |
| Collections.addAll(elements, result); |
| } |
| return elements.toArray(new IModelElement[elements.size()]); |
| } |
| |
| } |