/** * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. * Licensed under the terms of the Eclipse Public License (EPL). * Please see the license.txt included with this distribution for details. * Any modifications to this file must keep this entire header intact. */ package org.python.pydev.editor.codecompletion; import java.util.Iterator; import java.util.List; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.text.contentassist.IContextInformation; import org.python.pydev.core.ICompletionState; import org.python.pydev.core.IToken; import org.python.pydev.core.ICodeCompletionASTManager.ImportInfo; import org.python.pydev.core.docutils.ImportsSelection; import org.python.pydev.editor.codecompletion.revisited.AbstractToken; import com.aptana.shared_core.string.FastStringBuffer; public abstract class AbstractPyCodeCompletion implements IPyCodeCompletion { /* (non-Javadoc) * @see org.python.pydev.editor.codecompletion.IPyCodeCompletion#getImportsTipperStr(org.python.pydev.editor.codecompletion.CompletionRequest) */ public ImportInfo getImportsTipperStr(CompletionRequest request) { IDocument doc = request.doc; int documentOffset = request.documentOffset; return ImportsSelection.getImportsTipperStr(doc, documentOffset); } /** * This is the place where we change the tokens we've gathered so far with the 'inference' engine and transform those * tokens to actual completions as requested by the Eclipse infrastructure. * @param lookingForInstance if looking for instance, we should not add the 'self' as parameter. */ protected void changeItokenToCompletionPropostal(ITextViewer viewer, CompletionRequest request, List<ICompletionProposal> convertedProposals, List<Object> iTokenList, boolean importsTip, ICompletionState state) { FastStringBuffer result = new FastStringBuffer(); FastStringBuffer temp = new FastStringBuffer(); int replacementOffset = request.documentOffset - request.qlen; int forcedContextInformationOffset = -1; //that's negated so that we can use it as an integer later on (to sum it) int notInCalltip = 1; int onApplyAction = PyCompletionProposal.ON_APPLY_DEFAULT; if (request.isInCalltip) { notInCalltip = 0; //when we're in the calltip, we don't have to add a char '(' to the start of the context information. if (request.alreadyHasParams) { onApplyAction = PyCompletionProposal.ON_APPLY_JUST_SHOW_CTX_INFO; forcedContextInformationOffset = request.calltipOffset; } else { onApplyAction = PyCompletionProposal.ON_APPLY_SHOW_CTX_INFO_AND_ADD_PARAMETETRS; } } for (Iterator<Object> iter = iTokenList.iterator(); iter.hasNext();) { Object obj = iter.next(); if (obj instanceof IToken) { IToken element = (IToken) obj; String name = element.getRepresentation(); //GET the ARGS int l = name.length(); String args = ""; if (!importsTip) { boolean getIt = true; if (AbstractToken.isClassDef(element)) { if (!request.isInCalltip) { getIt = false; } } if (getIt) { args = getArgs(element, state); if (args.length() > 0) { l++; //cursor position is name + '(' } } } //END if (name.equals(request.fullQualifier) && args.trim().length() == 0) { //we don't want to get the tokens that are equal to the current 'full' qualifier //...unless it adds the parameters to a call... continue; } int type = element.getType(); int priority = IPyCompletionProposal.PRIORITY_DEFAULT; if (type == IToken.TYPE_PARAM || type == IToken.TYPE_LOCAL || type == IToken.TYPE_OBJECT_FOUND_INTERFACE) { priority = IPyCompletionProposal.PRIORITY_LOCALS; } IContextInformation pyContextInformation = null; if (args.length() > 2) { int contextInformationOffset; if (forcedContextInformationOffset < 0) { contextInformationOffset = replacementOffset + name.length() + notInCalltip; } else { contextInformationOffset = forcedContextInformationOffset; } pyContextInformation = new PyCalltipsContextInformationFromIToken(element, args, contextInformationOffset); //just after the parenthesis } String replacementString = name + makeArgsForDocumentReplacement(args, result, temp); String displayString = name + args; PyCompletionProposal proposal = new PyLinkedModeCompletionProposal(replacementString, replacementOffset, request.qlen, l, element, displayString, pyContextInformation, priority, onApplyAction, args); convertedProposals.add(proposal); } else if (obj instanceof Object[]) { Object element[] = (Object[]) obj; String name = (String) element[0]; String docStr = (String) element[1]; int type = -1; if (element.length > 2) { type = ((Integer) element[2]).intValue(); } int priority = IPyCompletionProposal.PRIORITY_DEFAULT; if (type == IToken.TYPE_PARAM) { priority = IPyCompletionProposal.PRIORITY_LOCALS; } PyCompletionProposal proposal = new PyCompletionProposal(name, request.documentOffset - request.qlen, request.qlen, name.length(), PyCodeCompletionImages.getImageForType(type), null, null, docStr, priority); convertedProposals.add(proposal); } else if (obj instanceof ICompletionProposal) { //no need to convert convertedProposals.add((ICompletionProposal) obj); } } } private static int STATE_INITIAL = 0; private static int STATE_FOUND_CHAR = 1; private static int STATE_FOUND_WHITESPACE = 2; private static int STATE_FOUND_WHITESPACE_AFTER_CHAR = 3; /** * Converts the arguments received to arguments to be added to the document. See tests for examples. * * result and temp are the buffers that are used in this function to build the arguments. They are cleared * before use (this is an optimization so that we don't need to recreate them at each time here as it's * called within a loop). */ public static String makeArgsForDocumentReplacement(String args, FastStringBuffer result, FastStringBuffer temp) { result = result.clear(); temp = temp.clear(); int state = STATE_INITIAL; int starsToAdd = 0; for (char c : args.toCharArray()) { if (c == '*') { starsToAdd++; continue; } if (c == ',' || c == '(' || c == ')') { appendTempToResult(result, temp, starsToAdd); result.append(c); temp.clear(); starsToAdd = 0; state = STATE_INITIAL; } else { if (Character.isWhitespace(c)) { if (state == STATE_FOUND_CHAR) { state = STATE_FOUND_WHITESPACE_AFTER_CHAR; } else if (state != STATE_FOUND_WHITESPACE_AFTER_CHAR) { state = STATE_FOUND_WHITESPACE; } continue; } else { if (state == STATE_FOUND_WHITESPACE_AFTER_CHAR) { temp.clear(); } state = STATE_FOUND_CHAR; } temp.append(c); } } appendTempToResult(result, temp, starsToAdd); return result.toString(); } private static void appendTempToResult(FastStringBuffer result, FastStringBuffer temp, int starsToAdd) { if (result.toString().trim().endsWith(",")) { result.append(' '); } result.appendN('*', starsToAdd); result.append(temp); } protected static String getArgs(IToken element, ICompletionState state) { int lookingFor = state.getLookingFor(); return getArgs(element, lookingFor); } private static String getArgs(IToken element, int lookingFor) { return getArgs(element.getArgs(), element.getType(), lookingFor); } /** * @return a string with the arguments to be shown for the given element. * * E.g.: >>(self, a, b)<< Returns (a, b) */ public static String getArgs(String argsReceived, int type, int lookingFor) { String args = ""; boolean lookingForInstance = lookingFor == ICompletionState.LOOKING_FOR_INSTANCE_UNDEFINED || lookingFor == ICompletionState.LOOKING_FOR_INSTANCED_VARIABLE || lookingFor == ICompletionState.LOOKING_FOR_ASSIGN; String trimmed = argsReceived.trim(); if (trimmed.length() > 0) { FastStringBuffer buffer = new FastStringBuffer("(", 128); char c = trimmed.charAt(0); if (c == '(') { trimmed = trimmed.substring(1); } if (trimmed.length() > 0) { c = trimmed.charAt(trimmed.length() - 1); if (c == ')') { trimmed = trimmed.substring(0, trimmed.length() - 1); } } trimmed = trimmed.trim(); //Now, if it starts with self or cls, we may have to remove it. String temp; if (lookingForInstance && trimmed.startsWith("self")) { temp = trimmed.substring(4); } else if (trimmed.startsWith("cls")) { temp = trimmed.substring(3); } else { temp = trimmed; } temp = temp.trim(); if (temp.length() > 0) { //but only if it wasn't a self or cls followed by a valid identifier part. if (!Character.isJavaIdentifierPart(temp.charAt(0))) { trimmed = temp; } } else { trimmed = temp; } trimmed = trimmed.trim(); if (trimmed.startsWith(",")) { trimmed = trimmed.substring(1); } trimmed = trimmed.trim(); buffer.append(trimmed); buffer.append(")"); args = buffer.toString(); } else if (type == IToken.TYPE_FUNCTION) { args = "()"; } return args; } }