/* * Copyright (c) 2012, the Dart project authors. * * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.eclipse.org/legal/epl-v10.html * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package com.google.dart.tools.ui.internal.text.completion; import com.google.dart.tools.core.completion.CompletionProposal; import com.google.dart.tools.ui.DartElementLabels; import com.google.dart.tools.ui.DartToolsPlugin; import com.google.dart.tools.ui.PreferenceConstants; import com.google.dart.tools.ui.internal.text.dart.ProposalContextInformation; import com.google.dart.tools.ui.text.dart.DartContentAssistInvocationContext; import com.google.dart.tools.ui.text.editor.tmp.Signature; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.contentassist.IContextInformation; import org.eclipse.osgi.util.TextProcessor; public class DartMethodCompletionProposal extends LazyDartCompletionProposal { /** 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 DartMethodCompletionProposal(CompletionProposal proposal, DartContentAssistInvocationContext context) { super(proposal, context); } @Override public void apply(IDocument document, char trigger, int offset) { if (trigger == ' ' || trigger == '(') { trigger = '\0'; } super.apply(document, trigger, offset); if (needsLinkedMode()) { setUpLinkedMode(document, ')'); } } @Override public int getPrefixCompletionStart(IDocument document, int completionOffset) { if (fProposal.getKind() == CompletionProposal.CONSTRUCTOR_INVOCATION) { return fProposal.getRequiredProposals()[0].getReplaceStart(); } return super.getPrefixCompletionStart(document, completionOffset); } @Override public CharSequence getPrefixCompletionText(IDocument 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); } /** * Appends everything up to the method name including the opening parenthesis. * <p> * * @param buffer the string buffer */ protected void appendMethodNameReplacement(StringBuffer buffer) { if (fProposal.getKind() != CompletionProposal.CONSTRUCTOR_INVOCATION) { buffer.append(fProposal.getName()); } FormatterPrefs prefs = getFormatterPrefs(); if (prefs.beforeOpeningParen) { buffer.append(SPACE); } buffer.append(LPAREN); } @Override protected IContextInformation computeContextInformation() { // no context information for METHOD_NAME_REF proposals (e.g. for static imports) 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 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); } } else { if (prefs.inEmptyList) { buffer.append(SPACE); } } buffer.append(RPAREN); return buffer.toString(); } @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 = fProposal.getParameterNames().length % 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(); } @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 the method formatter preferences. * * @return the formatter settings */ @Override protected final FormatterPrefs getFormatterPrefs() { if (fFormatterPrefs == null) { fFormatterPrefs = new FormatterPrefs(null); } return fFormatterPrefs; } @Override protected String getPrefix(IDocument 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$ } /** * 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 = DartToolsPlugin.getDefault().getPreferenceStore(); boolean noOverwrite = preferenceStore.getBoolean(PreferenceConstants.CODEASSIST_INSERT_COMPLETION) ^ isToggleEating(); char[] completion = fProposal.getCompletion(); return !isInDartDoc() && completion.length > 0 && (noOverwrite || completion[completion.length - 1] == ')'); } /** * 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; } @Override protected boolean isOffsetValid(int offset) { if (fProposal.getKind() != CompletionProposal.CONSTRUCTOR_INVOCATION) { return super.isOffsetValid(offset); } return fProposal.getRequiredProposals()[0].getReplaceStart() <= offset; } @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(DartElementLabels.CONCAT_STRING) + DartElementLabels.CONCAT_STRING.length(); word = word.substring(start); return isPrefix(prefix, word) || isPrefix(prefix, new String(fProposal.getName())); } if (isInDartDoc()) { 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); } protected boolean needsLinkedMode() { return hasArgumentList() && hasParameters(); } private boolean computeHasParameters() throws IllegalArgumentException { return getProposal().getParameterNames().length > 0; } }