String (@NotNull PyFunction function, @NotNull TypeEvalContext context, boolean forTooltip) { final StringBuilder result = new StringBuilder(); int firstParamOffset = 0; // TODO wrapping of long signatures if (function.isAsync()) { result.append(styledSpan("async ", PyHighlighter.PY_KEYWORD)); //NON-NLS firstParamOffset += "async ".length(); } result.append(styledSpan("def ", PyHighlighter.PY_KEYWORD)); //NON-NLS firstParamOffset += "def ".length(); final String funcName = StringUtil.notNullize(function.getName(), PyNames.UNNAMED_ELEMENT); firstParamOffset += funcName.length(); result.append(styledSpan(funcName, functionNameTextAttribute(function, funcName))); result.append(styledSpan("(", PyHighlighter.PY_PARENTHS)); firstParamOffset++; int lastLineOffset = 0; boolean first = true; boolean firstIsSelf = false; final List<PyCallableParameter> parameters = function.getParameters(context); for (PyCallableParameter parameter : parameters) { boolean isSelf = parameter.isSelf(); if (!first) { result.append(styledSpan(",", PyHighlighter.PY_COMMA)); if (forTooltip || firstIsSelf && parameters.size() == 2) { result.append(" "); } else { result.append("\n"); lastLineOffset = result.length(); // alignment StringUtil.repeatSymbol(result, ' ', firstParamOffset); } } else { firstIsSelf = isSelf; } String paramName = parameter.getName(); PyType paramType = parameter.getType(context); final PyNamedParameter named = as(parameter.getParameter(), PyNamedParameter.class); boolean showType = true; if (parameter.isPositionalContainer()) { paramName = "*" + StringUtil.notNullize(paramName, "args"); //NON-NLS final PyTupleType tupleType = as(paramType, PyTupleType.class); if (tupleType != null) { paramType = tupleType.getIteratedItemType(); } } else if (parameter.isKeywordContainer()) { paramName = "**" + StringUtil.notNullize(paramName, "kwargs"); //NON-NLS final PyCollectionType genericType = as(paramType, PyCollectionType.class); if (genericType != null && genericType.getPyClass() == PyBuiltinCache.getInstance(function).getClass("dict")) { final List<PyType> typeParams = genericType.getElementTypes(); paramType = typeParams.size() == 2 ? typeParams.get(1) : null; } } else if (parameter.getParameter() instanceof PySlashParameter) { paramName = PySlashParameter.TEXT; showType = false; } else if (parameter.getParameter() instanceof PySingleStarParameter) { paramName = PySingleStarParameter.TEXT; showType = false; } else { paramName = StringUtil.notNullize(paramName, PyNames.UNNAMED_ELEMENT); // Don't show type for "self" unless it's explicitly annotated showType = !isSelf || (named != null && new PyTypingTypeProvider().getParameterType(named, function, context) != null); } result.append(styledSpan(paramName, paramNameTextAttribute(isSelf))); if (showType) { result .append(styledSpan(": ", PyHighlighter.PY_OPERATION_SIGN)) .append(styledSpan(formatTypeWithLinks(paramType, named, function, context), PyHighlighter.PY_ANNOTATION)); } final String signature = ParamHelper.getDefaultValuePartInSignature(parameter.getDefaultValueText(), showType); if (signature != null) { @SuppressWarnings("RegExpRepeatedSpace") final String delimiter = showType ? " = " : "="; final String[] parts = signature.split(delimiter); if (parts.length == 2) { result.append(styledSpan(delimiter, PyHighlighter.PY_OPERATION_SIGN)); result.append(highlightExpressionText(parts[1], parameter.getDefaultValue())); } } first = false; } result.append(styledSpan(")", PyHighlighter.PY_PARENTHS)); if (!forTooltip && StringUtil.stripHtml(result.substring(lastLineOffset), false).length() > RETURN_TYPE_WRAPPING_THRESHOLD) { result.append("\n "); } final PyType returnType = context.getReturnType(function); result.append(HtmlChunk.text(" -> ")); result.append(styledSpan(formatTypeWithLinks(returnType, function, function, context), PyHighlighter.PY_ANNOTATION)); return result.toString(); }