/******************************************************************************* * Copyright (c) 2005, 2011 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 * Tom Eicher <eclipse@tom.eicher.name> - [content assist] prefix complete casted method proposals - https://bugs.eclipse * .org/bugs/show_bug.cgi?id=247547 *******************************************************************************/ package org.eclipse.che.ide.ext.java.jdt.codeassistant; import org.eclipse.che.ide.ext.java.jdt.core.CompletionProposal; import org.eclipse.che.ide.ext.java.jdt.core.Signature; import org.eclipse.che.ide.ext.java.jdt.core.formatter.CodeFormatter; import org.eclipse.che.ide.ext.java.jdt.internal.corext.util.CodeFormatterUtil; import org.eclipse.che.ide.ext.java.jdt.text.Document; import org.eclipse.che.ide.ext.java.jdt.text.TextUtilities; import org.eclipse.che.ide.api.text.BadLocationException; public class JavaMethodCompletionProposal extends LazyJavaCompletionProposal { /** Triggers for method proposals without parameters. Do not modify. */ protected final static char[] METHOD_TRIGGERS = new char[]{';', ',', '.', '\t', '['}; /** Triggers for method proposals. Do not modify. */ protected final static char[] METHOD_WITH_ARGUMENTS_TRIGGERS = new char[]{'(', '-', ' '}; /** Triggers for method name proposals (static imports). Do not modify. */ protected final static char[] METHOD_NAME_TRIGGERS = new char[]{';'}; private boolean fHasParameters; private boolean fHasParametersComputed = false; private FormatterPrefs fFormatterPrefs; public JavaMethodCompletionProposal(CompletionProposal proposal, JavaContentAssistInvocationContext context) { super(proposal, context); } @Override public void apply(Document document, char trigger, int offset) { if (trigger == ' ' || trigger == '(') trigger = '\0'; super.apply(document, trigger, offset); if (needsLinkedMode()) { setUpLinkedMode(document, ')'); } } protected boolean needsLinkedMode() { return hasArgumentList() && hasParameters(); } @Override public int getPrefixCompletionStart(Document document, int completionOffset) { if (fProposal.getKind() == CompletionProposal.METHOD_REF_WITH_CASTED_RECEIVER) { return fProposal.getTokenStart(); } else if (fProposal.getKind() == CompletionProposal.CONSTRUCTOR_INVOCATION) return fProposal.getRequiredProposals()[0].getReplaceStart(); return super.getPrefixCompletionStart(document, completionOffset); } @Override public CharSequence getPrefixCompletionText(Document document, int completionOffset) { if (hasArgumentList() || fProposal.getKind() == CompletionProposal.CONSTRUCTOR_INVOCATION) { String completion = String.valueOf(fProposal.getName()); if (isCamelCaseMatching()) { String prefix = getPrefix(document, completionOffset); return getCamelCaseCompound(prefix, completion); } return completion; } return super.getPrefixCompletionText(document, completionOffset); } // @Override // protected ContextInformation computeContextInformation() // { // // no context information for METHOD_NAME_REF proposals (e.g. for static imports) // // https://bugs.eclipse.org/bugs/show_bug.cgi?id=94654 // if ((fProposal.getKind() == CompletionProposal.METHOD_REF || fProposal.getKind() == CompletionProposal.CONSTRUCTOR_INVOCATION) // && hasParameters() && (getReplacementString().endsWith(RPAREN) || getReplacementString().length() == 0)) // { // ProposalContextInformation contextInformation = new ProposalContextInformation(fProposal); // if (fContextInformationPosition != 0 && fProposal.getCompletion().length == 0) // contextInformation.setContextInformationPosition(fContextInformationPosition); // return contextInformation; // } // return super.computeContextInformation(); // } @Override protected char[] computeTriggerCharacters() { if (fProposal.getKind() == CompletionProposal.METHOD_NAME_REFERENCE) return METHOD_NAME_TRIGGERS; if (hasParameters()) return METHOD_WITH_ARGUMENTS_TRIGGERS; return METHOD_TRIGGERS; } /** * Returns <code>true</code> if the method being inserted has at least one parameter. Note that this does not say anything * about whether the argument list should be inserted. This depends on the position in the document and the kind of proposal; * see {@link #hasArgumentList() }. * * @return <code>true</code> if the method has any parameters, <code>false</code> if it has no parameters */ protected final boolean hasParameters() { if (!fHasParametersComputed) { fHasParametersComputed = true; fHasParameters = computeHasParameters(); } return fHasParameters; } private boolean computeHasParameters() throws IllegalArgumentException { return Signature.getParameterCount(fProposal.getSignature()) > 0; } /** * Returns <code>true</code> if the argument list should be inserted by the proposal, <code>false</code> if not. * * @return <code>true</code> when the proposal is not in javadoc nor within an import and comprises the parameter list */ protected boolean hasArgumentList() { if (CompletionProposal.METHOD_NAME_REFERENCE == fProposal.getKind()) return false; // IPreferenceStore preferenceStore= JavaPlugin.getDefault().getPreferenceStore(); // boolean noOverwrite= preferenceStore.getBoolean(PreferenceConstants.CODEASSIST_INSERT_COMPLETION) ^ isToggleEating(); boolean noOverwrite = true; char[] completion = fProposal.getCompletion(); return !isInJavadoc() && completion.length > 0 && (noOverwrite || completion[completion.length - 1] == ')'); } /** * Returns the method formatter preferences. * * @return the formatter settings */ @Override protected final FormatterPrefs getFormatterPrefs() { if (fFormatterPrefs == null) fFormatterPrefs = new FormatterPrefs(); return fFormatterPrefs; } /* * @see org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal#computeReplacementString() */ @Override protected String computeReplacementString() { if (!hasArgumentList()) return super.computeReplacementString(); // we're inserting a method plus the argument list - respect formatter preferences StringBuffer buffer = new StringBuffer(); appendMethodNameReplacement(buffer); FormatterPrefs prefs = getFormatterPrefs(); if (hasParameters()) { setCursorPosition(buffer.length()); if (prefs.afterOpeningParen) buffer.append(SPACE); // don't add the trailing space, but let the user type it in himself - typing the closing paren will exit // if (prefs.beforeClosingParen) // buffer.append(SPACE); } else { if (prefs.inEmptyList) buffer.append(SPACE); } buffer.append(RPAREN); return buffer.toString(); } /** * Appends everything up to the method name including the opening parenthesis. * <p> * In case of {@link CompletionProposal#METHOD_REF_WITH_CASTED_RECEIVER} it add cast. * </p> * * @param buffer * the string buffer * @since 3.4 */ protected void appendMethodNameReplacement(StringBuffer buffer) { if (fProposal.getKind() == CompletionProposal.METHOD_REF_WITH_CASTED_RECEIVER) { String coreCompletion = String.valueOf(fProposal.getCompletion()); String lineDelimiter = TextUtilities.getDefaultLineDelimiter(fInvocationContext.getDocument()); String replacement = CodeFormatterUtil.format(CodeFormatter.K_EXPRESSION, coreCompletion, 0, lineDelimiter); buffer.append(replacement.substring(0, replacement.lastIndexOf('.') + 1)); } // TODO if (fProposal.getKind() != CompletionProposal.CONSTRUCTOR_INVOCATION) buffer.append(fProposal.getName()); FormatterPrefs prefs = getFormatterPrefs(); if (prefs.beforeOpeningParen) buffer.append(SPACE); buffer.append(LPAREN); } @Override protected ProposalInfo computeProposalInfo() { // IJavaProject project= fInvocationContext.getProject(); // if (project != null) return new MethodProposalInfo(fProposal, fInvocationContext.getProjectId(), fInvocationContext.getDocContext(), fInvocationContext.getVfsId()); // return super.computeProposalInfo(); } /* * @see org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal#computeSortString() */ @Override protected String computeSortString() { /* * Lexicographical sort order: 1) by relevance (done by the proposal sorter) 2) by method name 3) by parameter count 4) by * parameter type names */ char[] name = fProposal.getName(); char[] parameterList = Signature.toCharArray(fProposal.getSignature(), null, null, false, false); int parameterCount = Signature.getParameterCount(fProposal.getSignature()) % 10; // we don't care about insane methods with // >9 parameters StringBuffer buf = new StringBuffer(name.length + 2 + parameterList.length); buf.append(name); buf.append('\0'); // separator buf.append(parameterCount); buf.append(parameterList); return buf.toString(); } /* * @see org.eclipse.jdt.internal.ui.text.java.AbstractJavaCompletionProposal#isOffsetValid(int) * @since 3.5 */ @Override protected boolean isOffsetValid(int offset) { if (fProposal.getKind() != CompletionProposal.CONSTRUCTOR_INVOCATION) return super.isOffsetValid(offset); return fProposal.getRequiredProposals()[0].getReplaceStart() <= offset; } // /* // * @see org.eclipse.jdt.internal.ui.text.java.AbstractJavaCompletionProposal#isValidPrefix(java.lang.String) // */ // @Override // protected boolean isValidPrefix(String prefix) { // if (super.isValidPrefix(prefix)) // return true; // // String word= TextProcessor.deprocess(getDisplayString()); // if (fProposal.getKind() == CompletionProposal.CONSTRUCTOR_INVOCATION) { // int start= word.indexOf(JavaElementLabels.CONCAT_STRING) + JavaElementLabels.CONCAT_STRING.length(); // word= word.substring(start); // return isPrefix(prefix, word) || isPrefix(prefix, new String(fProposal.getName())); // } // // if (isInJavadoc()) { // int idx = word.indexOf("{@link "); //$NON-NLS-1$ // if (idx==0) { // word = word.substring(7); // } else { // idx = word.indexOf("{@value "); //$NON-NLS-1$ // if (idx==0) { // word = word.substring(8); // } // } // } // return isPrefix(prefix, word); // } /* * @see org.eclipse.jdt.internal.ui.text.java.AbstractJavaCompletionProposal#isPrefix(java.lang.String, java.lang.String) * @since 3.4 */ @Override protected boolean isPrefix(String prefix, String string) { if (fProposal.getKind() == CompletionProposal.METHOD_REF_WITH_CASTED_RECEIVER && prefix != null) prefix = prefix.substring(fProposal.getReceiverEnd() - fProposal.getReceiverStart() + 1); return super.isPrefix(prefix, string); } @Override protected String getPrefix(Document document, int offset) { if (fProposal.getKind() != CompletionProposal.CONSTRUCTOR_INVOCATION) return super.getPrefix(document, offset); int replacementOffset = fProposal.getRequiredProposals()[0].getReplaceStart(); try { int length = offset - replacementOffset; if (length > 0) return document.get(replacementOffset, length); } catch (BadLocationException x) { } return ""; //$NON-NLS-1$ } /* * @see * org.eclipse.jdt.internal.ui.text.java.AbstractJavaCompletionProposal#createRequiredTypeCompletionProposal(org.eclipse.jdt * .core.CompletionProposal, org.eclipse.jdt.ui.text.java.JavaContentAssistInvocationContext) * @since 3.7 */ @Override protected LazyJavaCompletionProposal createRequiredTypeCompletionProposal(CompletionProposal completionProposal, JavaContentAssistInvocationContext invocationContext) { LazyJavaCompletionProposal requiredProposal = super.createRequiredTypeCompletionProposal(completionProposal, invocationContext); if (fProposal.getKind() == CompletionProposal.CONSTRUCTOR_INVOCATION && requiredProposal instanceof LazyGenericTypeProposal) ((LazyGenericTypeProposal)requiredProposal).canUseDiamond(fProposal.canUseDiamond(fInvocationContext .getCoreContext())); return requiredProposal; } }