File indexing completed on 2024-05-19 15:41:37

0001 /*
0002     SPDX-FileCopyrightText: 2011 Sven Brauch <svenbrauch@googlemail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "functiondeclaration.h"
0008 
0009 #include <language/codecompletion/normaldeclarationcompletionitem.h>
0010 #include <language/codecompletion/codecompletionmodel.h>
0011 #include <language/duchain/types/functiontype.h>
0012 #include <language/duchain/aliasdeclaration.h>
0013 #include <language/duchain/types/containertypes.h>
0014 
0015 #include <KTextEditor/View>
0016 #include <KTextEditor/Document>
0017 #include <KLocalizedString>
0018 
0019 #include "duchain/navigation/navigationwidget.h"
0020 #include "codecompletion/helpers.h"
0021 #include "declaration.h"
0022 #include "declarations/functiondeclaration.h"
0023 #include "duchain/helpers.h"
0024 
0025 #include <QDebug>
0026 #include <codecompletiondebug.h>
0027 
0028 using namespace KDevelop;
0029 using namespace KTextEditor;
0030 
0031 namespace Python {
0032 
0033 FunctionDeclarationCompletionItem::FunctionDeclarationCompletionItem(DeclarationPointer decl, CodeCompletionContext::Ptr context) 
0034     : PythonDeclarationCompletionItem(decl, context)
0035     , m_atArgument(-1)
0036     , m_depth(0)
0037     , m_doNotCall(false)
0038 {
0039 
0040 }
0041 
0042 int FunctionDeclarationCompletionItem::atArgument() const
0043 {
0044     return m_atArgument;
0045 }
0046 
0047 void FunctionDeclarationCompletionItem::setDepth(int d)
0048 {
0049     m_depth = d;
0050 }
0051 
0052 void FunctionDeclarationCompletionItem::setAtArgument(int d)
0053 {
0054     m_atArgument = d;
0055 }
0056 
0057 int FunctionDeclarationCompletionItem::argumentHintDepth() const
0058 {
0059     return m_depth;
0060 }
0061 
0062 QVariant FunctionDeclarationCompletionItem::data(const QModelIndex& index, int role, const KDevelop::CodeCompletionModel* model) const
0063 {
0064     DUChainReadLocker lock;
0065     FunctionDeclaration* dec = dynamic_cast<FunctionDeclaration*>(m_declaration.data());
0066     switch ( role ) {
0067         case Qt::DisplayRole: {
0068             if ( ! dec ) {
0069                 break; // use the default
0070             }
0071             if ( index.column() == KDevelop::CodeCompletionModel::Arguments ) {
0072                 if (FunctionType::Ptr functionType = dec->type<FunctionType>()) {
0073                     QString ret;
0074                     createArgumentList(dec, ret, nullptr, 0, false);
0075                     return ret;
0076                 }
0077             }
0078             if ( index.column() == KDevelop::CodeCompletionModel::Prefix ) {
0079                 FunctionType::Ptr type = dec->type<FunctionType>();
0080                 if ( type && type->returnType() ) {
0081                     return i18n("function") + " -> " + type->returnType()->toString();
0082                 }
0083             }
0084             break;
0085         }
0086         case KDevelop::CodeCompletionModel::HighlightingMethod: {
0087             if ( index.column() == KDevelop::CodeCompletionModel::Arguments )
0088                 return QVariant(KDevelop::CodeCompletionModel::CustomHighlighting);
0089             break;
0090         }
0091         case KDevelop::CodeCompletionModel::CustomHighlight: {
0092             if ( index.column() == KDevelop::CodeCompletionModel::Arguments ) {
0093                 if ( ! dec ) return QVariant();
0094                 QString ret;
0095                 QList<QVariant> highlight;
0096                 if ( atArgument() ) {
0097                     createArgumentList(dec, ret, &highlight, atArgument(), false);
0098                 }
0099                 else {
0100                     createArgumentList(dec, ret, nullptr, false);
0101                 }
0102                 return QVariant(highlight);
0103             }
0104             break;
0105         }
0106         case KDevelop::CodeCompletionModel::MatchQuality: {
0107             if (    m_typeHint == PythonCodeCompletionContext::IterableRequested
0108                  && dec && dec->type<FunctionType>()
0109                  && dynamic_cast<ListType*>(dec->type<FunctionType>()->returnType().data()) )
0110             {
0111                 return 2 + PythonDeclarationCompletionItem::data(index, role, model).toInt();
0112             }
0113             return PythonDeclarationCompletionItem::data(index, role, model);
0114         }
0115     }
0116     return Python::PythonDeclarationCompletionItem::data(index, role, model);
0117 }
0118 
0119 void FunctionDeclarationCompletionItem::setDoNotCall(bool doNotCall)
0120 {
0121     m_doNotCall = doNotCall;
0122 }
0123 
0124 void FunctionDeclarationCompletionItem::executed(KTextEditor::View* view, const KTextEditor::Range& word)
0125 {
0126     qCDebug(KDEV_PYTHON_CODECOMPLETION) << "FunctionDeclarationCompletionItem executed";
0127     KTextEditor::Document* document = view->document();
0128     auto resolvedDecl = Helper::resolveAliasDeclaration(declaration().data());
0129     DUChainReadLocker lock;
0130     auto functionDecl = Helper::functionForCalled(resolvedDecl).declaration;
0131     lock.unlock();
0132     if ( ! functionDecl && (! resolvedDecl || ! resolvedDecl->abstractType()
0133                            || resolvedDecl->abstractType()->whichType() != AbstractType::TypeStructure) ) {
0134         qCritical(KDEV_PYTHON_CODECOMPLETION) << "ERROR: could not get declaration data, not executing completion item!";
0135         return;
0136     }
0137     QString suffix = "()";
0138     KTextEditor::Range checkPrefix(word.start().line(), 0, word.start().line(), word.start().column());
0139     KTextEditor::Range checkSuffix(word.end().line(), word.end().column(), word.end().line(), document->lineLength(word.end().line()));
0140     if ( m_doNotCall || document->text(checkSuffix).trimmed().startsWith('(')
0141          || document->text(checkPrefix).trimmed().endsWith('@')
0142          || (functionDecl && functionDecl->isProperty()) )
0143     {
0144         // don't insert brackets if they're already there,
0145         // the item is a decorator, or if it's an import item.
0146         suffix.clear();
0147     }
0148     // place cursor behind bracktes by default
0149     int skip = 2;
0150     if ( functionDecl ) {
0151         bool needsArguments = false;
0152         int argumentCount = functionDecl->type<FunctionType>()->arguments().length();
0153         if ( functionDecl->context()->type() == KDevelop::DUContext::Class ) {
0154             // it's a member function, so it has the implicit self
0155             // TODO static methods
0156             needsArguments = argumentCount > 1;
0157         }
0158         else {
0159             // it's a free function
0160             needsArguments = argumentCount > 0;
0161         }
0162         if ( needsArguments ) {
0163             // place cursor in brackets if there's parameters
0164             skip = 1;
0165         }
0166     }
0167     document->replaceText(word, declaration()->identifier().toString() + suffix);
0168     view->setCursorPosition( Cursor(word.end().line(), word.end().column() + skip) );
0169 }
0170 
0171 FunctionDeclarationCompletionItem::~FunctionDeclarationCompletionItem() { }
0172 
0173 }