/******************************************************************************* * Copyright (c) 2012-2015 Codenvy, S.A. * 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: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.eclipse.che.ide.ext.java.worker; import org.eclipse.che.ide.collections.js.JsoArray; import org.eclipse.che.ide.collections.js.JsoStringMap; import org.eclipse.che.ide.ext.java.jdt.codeassistant.AbstractJavaCompletionProposal; import org.eclipse.che.ide.ext.java.jdt.codeassistant.CompletionProposalCollector; import org.eclipse.che.ide.ext.java.jdt.codeassistant.FillArgumentNamesCompletionProposalCollector; import org.eclipse.che.ide.ext.java.jdt.codeassistant.JavaContentAssistInvocationContext; import org.eclipse.che.ide.ext.java.jdt.codeassistant.LazyGenericTypeProposal; import org.eclipse.che.ide.ext.java.jdt.codeassistant.TemplateCompletionProposalComputer; import org.eclipse.che.ide.ext.java.jdt.codeassistant.api.JavaCompletionProposal; import org.eclipse.che.ide.ext.java.jdt.core.CompletionProposal; import org.eclipse.che.ide.ext.java.jdt.core.IJavaElement; import org.eclipse.che.ide.ext.java.jdt.core.IType; import org.eclipse.che.ide.ext.java.jdt.core.JavaCore; import org.eclipse.che.ide.ext.java.jdt.core.Signature; import org.eclipse.che.ide.ext.java.jdt.core.dom.CompilationUnit; import org.eclipse.che.ide.ext.java.jdt.internal.codeassist.CompletionEngine; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.env.INameEnvironment; import org.eclipse.che.ide.ext.java.messages.ComputeCAProposalsMessage; import org.eclipse.che.ide.ext.java.messages.RoutingTypes; import org.eclipse.che.ide.ext.java.messages.WorkerProposal; import org.eclipse.che.ide.ext.java.messages.impl.MessagesImpls; import org.eclipse.che.ide.runtime.AssertionFailedException; import org.eclipse.che.ide.util.UUID; import com.google.gwt.webworker.client.messages.MessageFilter; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; /** * @author <a href="mailto:evidolob@codenvy.com">Evgen Vidolob</a> * @version $Id: */ public class WorkerCodeAssist { private final WorkerProposalApplier workerProposalApplier; private Comparator<JavaCompletionProposal> comparator = new Comparator<JavaCompletionProposal>() { @Override public int compare(JavaCompletionProposal o1, JavaCompletionProposal o2) { int r1 = o1.getRelevance(); int r2 = o2.getRelevance(); int relevanceDif = r2 - r1; if (relevanceDif != 0) { return relevanceDif; } return getSortKey(o1).compareToIgnoreCase(getSortKey(o2)); } private String getSortKey(JavaCompletionProposal p) { if (p instanceof AbstractJavaCompletionProposal) return ((AbstractJavaCompletionProposal) p).getSortString(); return p.getDisplayString(); } }; private JavaParserWorker worker; private INameEnvironment nameEnvironment; private TemplateCompletionProposalComputer templateCompletionProposalComputer; private String projectPath; private String docContext; private WorkerCuCache cuCache; private String vfsId; private String documentContent; private WorkerDocument document; public WorkerCodeAssist(JavaParserWorker worker, MessageFilter messageFilter, WorkerProposalApplier workerProposalApplier, INameEnvironment nameEnvironment, TemplateCompletionProposalComputer templateCompletionProposalComputer, String docContext, WorkerCuCache cuCache) { this.worker = worker; this.workerProposalApplier = workerProposalApplier; this.nameEnvironment = nameEnvironment; this.templateCompletionProposalComputer = templateCompletionProposalComputer; this.docContext = docContext; this.cuCache = cuCache; messageFilter.registerMessageRecipient(RoutingTypes.CA_COMPUTE_PROPOSALS, new MessageFilter.MessageRecipient<ComputeCAProposalsMessage>() { @Override public void onMessageReceived(final ComputeCAProposalsMessage message) { handleCAMessage(message); } }); } public void setProjectPath(String projectPath) { this.projectPath = projectPath; } private void handleCAMessage(ComputeCAProposalsMessage message) { setProjectPath(message.projectPath()); nameEnvironment.setProjectPath(message.projectPath()); JsoStringMap<JavaCompletionProposal> proposalMap = JsoStringMap.create(); documentContent = message.docContent(); JavaCompletionProposal[] proposals = computeCompletionProposals(cuCache.getCompilationUnit(message.filePath()), message.offset(), documentContent, message.fileName()); MessagesImpls.CAProposalsComputedMessageImpl caComputedMessage = MessagesImpls.CAProposalsComputedMessageImpl.make(); caComputedMessage.setId(message.id()); JsoArray<WorkerProposal> workerProposals = JsoArray.create(); for (JavaCompletionProposal proposal : proposals) { MessagesImpls.WorkerProposalImpl prop = MessagesImpls.WorkerProposalImpl.make(); prop.setAutoInsertable(proposal.isAutoInsertable()).setDisplayText(proposal.getDisplayString()) .setImage(proposal.getImage() == null ? null : proposal.getImage().name()); String uuid = UUID.uuid(); prop.setId(uuid); proposalMap.put(uuid, proposal); workerProposals.add(prop); } workerProposalApplier.setCaDocument(document); workerProposalApplier.setCaProposalMap(proposalMap); caComputedMessage.setProposals(workerProposals); worker.sendMessage(caComputedMessage.serialize()); } public JavaCompletionProposal[] computeCompletionProposals(CompilationUnit unit, int offset, String documentContent, String fileName) { if (unit == null) { return null; } document = new WorkerDocument(documentContent); CompletionProposalCollector collector = new FillArgumentNamesCompletionProposalCollector(unit, document, offset, projectPath, docContext, vfsId); collector .setAllowsRequiredProposals( CompletionProposal.CONSTRUCTOR_INVOCATION, CompletionProposal.TYPE_REF, true); collector.setAllowsRequiredProposals(CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION, CompletionProposal.TYPE_REF, true); collector.setAllowsRequiredProposals(CompletionProposal.ANONYMOUS_CLASS_DECLARATION, CompletionProposal.TYPE_REF, true); collector.setIgnored(CompletionProposal.ANNOTATION_ATTRIBUTE_REF, false); collector.setIgnored(CompletionProposal.ANONYMOUS_CLASS_DECLARATION, false); collector.setIgnored(CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION, false); collector.setIgnored(CompletionProposal.FIELD_REF, false); collector.setIgnored(CompletionProposal.FIELD_REF_WITH_CASTED_RECEIVER, false); collector.setIgnored(CompletionProposal.KEYWORD, false); collector.setIgnored(CompletionProposal.LABEL_REF, false); collector.setIgnored(CompletionProposal.LOCAL_VARIABLE_REF, false); collector.setIgnored(CompletionProposal.METHOD_DECLARATION, false); collector.setIgnored(CompletionProposal.METHOD_NAME_REFERENCE, false); collector.setIgnored(CompletionProposal.METHOD_REF, false); collector.setIgnored(CompletionProposal.CONSTRUCTOR_INVOCATION, false); collector.setIgnored(CompletionProposal.METHOD_REF_WITH_CASTED_RECEIVER, false); collector.setIgnored(CompletionProposal.PACKAGE_REF, false); collector.setIgnored(CompletionProposal.POTENTIAL_METHOD_DECLARATION, false); collector.setIgnored(CompletionProposal.VARIABLE_DECLARATION, false); collector.setIgnored(CompletionProposal.TYPE_REF, false); CompletionEngine e = new CompletionEngine(nameEnvironment, collector, JavaCore.getOptions()); try { e.complete(new org.eclipse.che.ide.ext.java.jdt.compiler.batch.CompilationUnit( documentContent.toCharArray(), fileName.substring(0, fileName.lastIndexOf('.')), "UTF-8"), offset, 0); JavaCompletionProposal[] javaCompletionProposals = collector.getJavaCompletionProposals(); List<JavaCompletionProposal> types = new ArrayList<>(Arrays.asList(javaCompletionProposals)); if (types.size() > 0 && collector.getInvocationContext().computeIdentifierPrefix().length() == 0) { IType expectedType = collector.getInvocationContext().getExpectedType(); if (expectedType != null) { // empty prefix completion - insert LRU types if known, but prune if they already occur in the core list // compute minmimum relevance and already proposed list int relevance = Integer.MAX_VALUE; Set<String> proposed = new HashSet<>(); for (Iterator<JavaCompletionProposal> it = types.iterator(); it.hasNext(); ) { AbstractJavaCompletionProposal p = (AbstractJavaCompletionProposal)it.next(); IJavaElement element = p.getJavaElement(); if (element instanceof IType) proposed.add(((IType)element).getFullyQualifiedName()); relevance = Math.min(relevance, p.getRelevance()); } // insert history types List<String> history = WorkerMessageHandler.get().getContentAssistHistory().getHistory(expectedType.getFullyQualifiedName()) .getTypes(); relevance -= history.size() + 1; for (Iterator<String> it = history.iterator(); it.hasNext(); ) { String type = it.next(); if (proposed.contains(type)) continue; JavaCompletionProposal proposal = createTypeProposal(relevance, type, collector.getInvocationContext()); if (proposal != null) types.add(proposal); relevance++; } } } List<JavaCompletionProposal> templateProposals = templateCompletionProposalComputer.computeCompletionProposals(collector.getInvocationContext()); JavaCompletionProposal[] array = templateProposals.toArray(new JavaCompletionProposal[templateProposals.size()]); javaCompletionProposals = types.toArray(new JavaCompletionProposal[0]); JavaCompletionProposal[] proposals = new JavaCompletionProposal[javaCompletionProposals.length + array.length]; System.arraycopy(javaCompletionProposals, 0, proposals, 0, javaCompletionProposals.length); System.arraycopy(array, 0, proposals, javaCompletionProposals.length, array.length); Arrays.sort(proposals, comparator); return proposals; } catch (AssertionFailedException ex) { //todo log errors // Log.error(getClass(), ex); throw new RuntimeException(ex); } catch (Exception ex) { //todo log errors // Log.error(getClass(), ex); throw new RuntimeException(ex); } // return new JavaCompletionProposal[0]; } private JavaCompletionProposal createTypeProposal(int relevance, String fullyQualifiedType, JavaContentAssistInvocationContext context) { IType type = WorkerTypeInfoStorage.get().getTypeByFqn(fullyQualifiedType); if (type == null) return null; org.eclipse.che.ide.ext.java.jdt.core.CompletionProposal proposal = org.eclipse.che.ide.ext.java.jdt.core.CompletionProposal.create( org.eclipse.che.ide.ext.java.jdt.core.CompletionProposal.TYPE_REF, context.getInvocationOffset()); proposal.setCompletion(fullyQualifiedType.toCharArray()); proposal.setDeclarationSignature(Signature.getQualifier(type.getFullyQualifiedName().toCharArray())); proposal.setFlags(type.getFlags()); proposal.setRelevance(relevance); proposal.setReplaceRange(context.getInvocationOffset(), context.getInvocationOffset()); proposal.setSignature(Signature.createTypeSignature(fullyQualifiedType, true).toCharArray()); return new LazyGenericTypeProposal(proposal, context); } }