private void buildFromDocstring(@NotNull final PsiElement elementDefinition, boolean isProperty) { PyClass pyClass = null; final PyStringLiteralExpression docStringExpression = ((PyDocStringOwner) elementDefinition).getDocStringExpression(); if (elementDefinition instanceof PyClass) { pyClass = (PyClass) elementDefinition; myBody.add(PythonDocumentationProvider.describeDecorators(pyClass, TagItalic, BR, LCombUp)); myBody.add(PythonDocumentationProvider.describeClass(pyClass, TagBold, true, false)); } else if (elementDefinition instanceof PyFunction) { PyFunction pyFunction = (PyFunction) elementDefinition; if (!isProperty) { pyClass = pyFunction.getContainingClass(); if (pyClass != null) { myBody .addWith( TagSmall, PythonDocumentationProvider.describeClass(pyClass, TagCode, true, true)) .addItem(BR) .addItem(BR); } } myBody .add(PythonDocumentationProvider.describeDecorators(pyFunction, TagItalic, BR, LCombUp)) .add(PythonDocumentationProvider.describeFunction(pyFunction, TagBold, LCombUp)); if (docStringExpression == null) { addInheritedDocString(pyFunction, pyClass); } } else if (elementDefinition instanceof PyFile) { addModulePath((PyFile) elementDefinition); } if (docStringExpression != null) { myBody.addItem(BR); addFormattedDocString(myElement, docStringExpression.getStringValue(), myBody, myEpilog); } }
@Override public void visitPyStringLiteralExpression(PyStringLiteralExpression node) { final Pair<PsiElement, TextRange> data = node.getUserData(PyReplaceExpressionUtil.SELECTION_BREAKS_AST_NODE); if (data != null) { final PsiElement parent = data.getFirst(); final String text = parent.getText(); final Pair<String, String> detectedQuotes = PythonStringUtil.getQuotes(text); final Pair<String, String> quotes = detectedQuotes != null ? detectedQuotes : Pair.create("'", "'"); final TextRange range = data.getSecond(); final String substring = range.substring(text); myResult.append(quotes.getFirst() + substring + quotes.getSecond()); } else { ASTNode child = node.getNode().getFirstChildNode(); while (child != null) { String text = child.getText(); if (child.getElementType() == TokenType.WHITE_SPACE) { if (text.contains("\n")) { if (!text.contains("\\")) { myResult.append("\\"); } myResult.append(text); } } else { myResult.append(text); } child = child.getTreeNext(); } } }
private void addPredefinedMethodDoc(PyFunction fun, String mothodName) { PyClassType objectType = PyBuiltinCache.getInstance(fun) .getObjectType(); // old- and new-style classes share the __xxx__ stuff if (objectType != null) { PyClass objectClass = objectType.getPyClass(); PyFunction predefinedMethod = objectClass.findMethodByName(mothodName, false); if (predefinedMethod != null) { PyStringLiteralExpression predefinedDocstring = predefinedMethod.getDocStringExpression(); String predefinedDoc = predefinedDocstring != null ? predefinedDocstring.getStringValue() : null; if (predefinedDoc != null && predefinedDoc.length() > 1) { // only a real-looking doc string counts addFormattedDocString(fun, predefinedDoc, myBody, myBody); myEpilog.addItem(BR).addItem(BR).addItem(PyBundle.message("QDOC.copied.from.builtin")); } } } }
private void addInheritedDocString( @NotNull final PyFunction pyFunction, @Nullable final PyClass pyClass) { boolean notFound = true; final String methodName = pyFunction.getName(); if (pyClass == null || methodName == null) { return; } final boolean isConstructor = PyNames.INIT.equals(methodName); Iterable<PyClass> classes = pyClass.getAncestorClasses(null); if (isConstructor) { // look at our own class again and maybe inherit class's doc classes = new ChainIterable<PyClass>(pyClass).add(classes); } for (PyClass ancestor : classes) { PyStringLiteralExpression docstringElement = null; PyFunction inherited = null; boolean isFromClass = false; if (isConstructor) docstringElement = pyClass.getDocStringExpression(); if (docstringElement != null) { isFromClass = true; } else { inherited = ancestor.findMethodByName(methodName, false); } if (inherited != null) { docstringElement = inherited.getDocStringExpression(); } if (docstringElement != null) { final String inheritedDoc = docstringElement.getStringValue(); if (inheritedDoc.length() > 1) { myEpilog.addItem(BR).addItem(BR); String ancestor_name = ancestor.getName(); String marker = (pyClass == ancestor) ? PythonDocumentationProvider.LINK_TYPE_CLASS : PythonDocumentationProvider.LINK_TYPE_PARENT; final String ancestor_link = $().addWith(new LinkWrapper(marker + ancestor_name), $(ancestor_name)).toString(); if (isFromClass) { myEpilog.addItem(PyBundle.message("QDOC.copied.from.class.$0", ancestor_link)); } else { myEpilog.addItem(PyBundle.message("QDOC.copied.from.$0.$1", ancestor_link, methodName)); } myEpilog.addItem(BR).addItem(BR); ChainIterable<String> formatted = new ChainIterable<String>(); ChainIterable<String> unformatted = new ChainIterable<String>(); addFormattedDocString(pyFunction, inheritedDoc, formatted, unformatted); myEpilog.addWith(TagCode, formatted).add(unformatted); notFound = false; break; } } } if (notFound) { // above could have not worked because inheritance is not searched down to 'object'. // for well-known methods, copy built-in doc string. // TODO: also handle predefined __xxx__ that are not part of 'object'. if (PyNames.UnderscoredAttributes.contains(methodName)) { addPredefinedMethodDoc(pyFunction, methodName); } } }
private boolean buildFromProperty( PsiElement elementDefinition, @Nullable final PsiElement outerElement, @NotNull final TypeEvalContext context) { if (myOriginalElement == null) { return false; } final String elementName = myOriginalElement.getText(); if (!PyNames.isIdentifier(elementName)) { return false; } if (!(outerElement instanceof PyQualifiedExpression)) { return false; } final PyExpression qualifier = ((PyQualifiedExpression) outerElement).getQualifier(); if (qualifier == null) { return false; } final PyType type = context.getType(qualifier); if (!(type instanceof PyClassType)) { return false; } final PyClass cls = ((PyClassType) type).getPyClass(); final Property property = cls.findProperty(elementName, true, null); if (property == null) { return false; } final AccessDirection direction = AccessDirection.of((PyElement) outerElement); final Maybe<PyCallable> accessor = property.getByDirection(direction); myProlog .addItem("property ") .addWith(TagBold, $().addWith(TagCode, $(elementName))) .addItem(" of ") .add(PythonDocumentationProvider.describeClass(cls, TagCode, true, true)); if (accessor.isDefined() && property.getDoc() != null) { myBody.addItem(": ").addItem(property.getDoc()).addItem(BR); } else { final PyCallable getter = property.getGetter().valueOrNull(); if (getter != null && getter != myElement && getter instanceof PyFunction) { // not in getter, getter's doc comment may be useful final PyStringLiteralExpression docstring = ((PyFunction) getter).getDocStringExpression(); if (docstring != null) { myProlog .addItem(BR) .addWith(TagItalic, $("Copied from getter:")) .addItem(BR) .addItem(docstring.getStringValue()); } } myBody.addItem(BR); } myBody.addItem(BR); if (accessor.isDefined() && accessor.value() == null) elementDefinition = null; final String accessorKind = getAccessorKind(direction); if (elementDefinition != null) { myEpilog.addWith(TagSmall, $(BR, BR, accessorKind, " of property")).addItem(BR); } if (!(elementDefinition instanceof PyDocStringOwner)) { myBody .addWith( TagItalic, elementDefinition != null ? $("Declaration: ") : $(accessorKind + " is not defined.")) .addItem(BR); if (elementDefinition != null) { myBody.addItem(combUp(PyUtil.getReadableRepr(elementDefinition, false))); } } return true; }
public void performAction(IntroduceOperation operation) { final PsiFile file = operation.getFile(); if (!CommonRefactoringUtil.checkReadOnlyStatus(file)) { return; } final Editor editor = operation.getEditor(); if (editor.getSettings().isVariableInplaceRenameEnabled()) { final TemplateState templateState = TemplateManagerImpl.getTemplateState(operation.getEditor()); if (templateState != null && !templateState.isFinished()) { return; } } PsiElement element1 = null; PsiElement element2 = null; final SelectionModel selectionModel = editor.getSelectionModel(); boolean singleElementSelection = false; if (selectionModel.hasSelection()) { element1 = file.findElementAt(selectionModel.getSelectionStart()); element2 = file.findElementAt(selectionModel.getSelectionEnd() - 1); if (element1 instanceof PsiWhiteSpace) { int startOffset = element1.getTextRange().getEndOffset(); element1 = file.findElementAt(startOffset); } if (element2 instanceof PsiWhiteSpace) { int endOffset = element2.getTextRange().getStartOffset(); element2 = file.findElementAt(endOffset - 1); } if (element1 == element2) { singleElementSelection = true; } } else { if (smartIntroduce(operation)) { return; } final CaretModel caretModel = editor.getCaretModel(); final Document document = editor.getDocument(); int lineNumber = document.getLineNumber(caretModel.getOffset()); if ((lineNumber >= 0) && (lineNumber < document.getLineCount())) { element1 = file.findElementAt(document.getLineStartOffset(lineNumber)); element2 = file.findElementAt(document.getLineEndOffset(lineNumber) - 1); } } final Project project = operation.getProject(); if (element1 == null || element2 == null) { showCannotPerformError(project, editor); return; } element1 = PyRefactoringUtil.getSelectedExpression(project, file, element1, element2); if (element1 == null) { showCannotPerformError(project, editor); return; } if (singleElementSelection && element1 instanceof PyStringLiteralExpression) { final PyStringLiteralExpression literal = (PyStringLiteralExpression) element1; // Currently introduce for substrings of a multi-part string literals is not supported if (literal.getStringNodes().size() > 1) { showCannotPerformError(project, editor); return; } final int offset = element1.getTextOffset(); final TextRange selectionRange = TextRange.create(selectionModel.getSelectionStart(), selectionModel.getSelectionEnd()); final TextRange elementRange = element1.getTextRange(); if (!elementRange.equals(selectionRange) && elementRange.contains(selectionRange)) { final TextRange innerRange = literal.getStringValueTextRange(); final TextRange intersection = selectionRange.shiftRight(-offset).intersection(innerRange); final TextRange finalRange = intersection != null ? intersection : selectionRange; final String text = literal.getText(); if (getFormatValueExpression(literal) != null && breaksStringFormatting(text, finalRange) || getNewStyleFormatValueExpression(literal) != null && breaksNewStyleStringFormatting(text, finalRange) || breaksStringEscaping(text, finalRange)) { showCannotPerformError(project, editor); return; } element1.putUserData( PyReplaceExpressionUtil.SELECTION_BREAKS_AST_NODE, Pair.create(element1, finalRange)); } } if (!checkIntroduceContext(file, editor, element1)) { return; } operation.setElement(element1); performActionOnElement(operation); }