File indexing completed on 2024-04-21 15:24:06
0001 /* 0002 SPDX-FileCopyrightText: 2009 Milian Wolff <mail@milianw.de> 0003 Basec on Cpp ImplementationHelperItem 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "implementationitem.h" 0009 0010 #include <KLocalizedString> 0011 0012 #include <KTextEditor/Document> 0013 #include <KTextEditor/View> 0014 0015 #include <language/duchain/duchain.h> 0016 #include <language/duchain/duchainlock.h> 0017 #include <language/duchain/declaration.h> 0018 #include <language/duchain/types/functiontype.h> 0019 #include <language/duchain/duchainutils.h> 0020 #include <language/duchain/classdeclaration.h> 0021 #include <language/duchain/types/integraltype.h> 0022 0023 #include <language/codecompletion/codecompletionmodel.h> 0024 0025 #include "declarations/classmethoddeclaration.h" 0026 0027 #include "completiondebug.h" 0028 #include "helpers.h" 0029 0030 using namespace KDevelop; 0031 0032 namespace Php 0033 { 0034 0035 #define RETURN_CACHED_ICON(name) {static QIcon icon(QIcon::fromTheme(QStringLiteral(name)).pixmap(QSize(16, 16))); return icon;} 0036 0037 QVariant ImplementationItem::data(const QModelIndex& index, int role, const CodeCompletionModel* model) const 0038 { 0039 QVariant ret = NormalDeclarationCompletionItem::data(index, role, model); 0040 switch (role) { 0041 case Qt::DecorationRole: 0042 if (index.column() == KTextEditor::CodeCompletionModel::Icon) { 0043 switch (m_type) { 0044 case Override: 0045 case OverrideVar: 0046 RETURN_CACHED_ICON("CTparents"); 0047 case Implement: 0048 RETURN_CACHED_ICON("CTsuppliers"); 0049 } 0050 } 0051 break; 0052 case Qt::DisplayRole: 0053 if (index.column() == KTextEditor::CodeCompletionModel::Prefix) { 0054 QString prefix; 0055 switch (m_type) { 0056 case Override: 0057 case OverrideVar: 0058 prefix = i18n("Override"); 0059 break; 0060 case Implement: 0061 prefix = i18n("Implement"); 0062 break; 0063 } 0064 0065 ret = prefix + ' ' + ret.toString(); 0066 } 0067 //TODO column == Name - required? 0068 break; 0069 case KTextEditor::CodeCompletionModel::ItemSelected: { 0070 DUChainReadLocker lock(DUChain::lock()); 0071 if (declaration().data()) { 0072 QualifiedIdentifier parentScope = declaration()->context()->scopeIdentifier(true); 0073 return i18n("From %1", parentScope.toString()); 0074 } 0075 } 0076 break; 0077 case KTextEditor::CodeCompletionModel::InheritanceDepth: 0078 return QVariant(0); 0079 default: 0080 //pass 0081 break; 0082 } 0083 0084 return ret; 0085 } 0086 0087 void ImplementationItem::execute(KTextEditor::View* view, const KTextEditor::Range& word) 0088 { 0089 DUChainReadLocker lock(DUChain::lock()); 0090 KTextEditor::Document *document = view->document(); 0091 0092 QString replText; 0093 0094 if (m_declaration) { 0095 //TODO:respect custom code styles 0096 0097 // get existing modifiers so we can respect the user's choice of public/protected and final 0098 QStringList modifiers = getMethodTokens(document->text(KTextEditor::Range(KTextEditor::Cursor::start(), word.start()))); 0099 // get range to replace 0100 KTextEditor::Range replaceRange(word); 0101 if (!modifiers.isEmpty()) { 0102 // TODO: is there no easy API to map QString Index to a KTextEditor::Cursor ?! 0103 QString methodText = document->text(KTextEditor::Range(KTextEditor::Cursor::start(), word.start())); 0104 methodText = methodText.left(methodText.lastIndexOf(modifiers.last(), -1, Qt::CaseInsensitive)); 0105 replaceRange.start() = KTextEditor::Cursor(methodText.count('\n'), methodText.length() - methodText.lastIndexOf('\n') - 1); 0106 } 0107 0108 // get indentation 0109 QString indentation; 0110 { 0111 QString currentLine = document->line(replaceRange.start().line()); 0112 indentation = getIndentation(currentLine); 0113 0114 if ( !currentLine.isEmpty() && currentLine != indentation ) { 0115 // since theres some non-whitespace in this line, skip to the enxt one 0116 replText += '\n' + indentation; 0117 } 0118 0119 if (indentation.isEmpty()) { 0120 // use a minimal indentation 0121 // TODO: respect code style 0122 indentation = QStringLiteral(" "); 0123 replText += indentation; 0124 } 0125 } 0126 0127 #if 0 0128 //Disabled, because not everyone writes phpdoc for every function 0129 //TODO: move to a phpdoc helper 0130 // build phpdoc comment 0131 { 0132 QualifiedIdentifier parentClassIdentifier; 0133 if (DUContext* pctx = m_declaration->context()) { 0134 parentClassIdentifier = pctx->localScopeIdentifier(); 0135 } else { 0136 qCDebug(COMPLETION) << "completion item for implementation has no parent context!"; 0137 } 0138 0139 replText += "/**\n" + indentation + " * "; 0140 // insert old comment: 0141 const QString indentationWithExtra = "\n" + indentation + " *"; 0142 replText += m_declaration->comment().replace('\n', indentationWithExtra.toAscii().constData()); 0143 replText += "\n" + indentation + " * @overload " + m_declaration->internalContext()->scopeIdentifier(true).toString(); 0144 replText += "\n" + indentation + " **/\n" + indentation; 0145 } 0146 #endif 0147 0148 // write function signature 0149 0150 // copy existing modifiers 0151 if (!modifiers.isEmpty()) { 0152 // the tokens are in a bad order and there's no reverse method or similar, so we can't simply join the tokens 0153 QStringList::const_iterator i = modifiers.constEnd() - 1; 0154 while (true) { 0155 replText += (*i) + ' '; 0156 if (i == modifiers.constBegin()) { 0157 break; 0158 } else { 0159 --i; 0160 } 0161 } 0162 } 0163 0164 QString functionName; 0165 bool isConstructorOrDestructor = false; 0166 bool isInterface = false; 0167 0168 if (ClassMemberDeclaration* member = dynamic_cast<ClassMemberDeclaration*>(m_declaration.data())) { 0169 // NOTE: it should _never_ be private - but that's the completionmodel / context / worker's job 0170 if (!modifiers.contains(QStringLiteral("public")) && !modifiers.contains(QStringLiteral("protected"))) { 0171 if (member->accessPolicy() == Declaration::Protected) { 0172 replText += QLatin1String("protected "); 0173 } else { 0174 replText += QLatin1String("public "); 0175 } 0176 } 0177 if (!modifiers.contains(QStringLiteral("static")) && member->isStatic()) { 0178 replText += QLatin1String("static "); 0179 } 0180 functionName = member->identifier().toString(); 0181 0182 ClassMethodDeclaration* method = dynamic_cast<ClassMethodDeclaration*>(m_declaration.data()); 0183 if (method) { 0184 functionName = method->prettyName().str(); 0185 isConstructorOrDestructor = method->isConstructor() || method->isDestructor(); 0186 } 0187 0188 if (member->context() && member->context()->owner()) { 0189 ClassDeclaration* classDec = dynamic_cast<ClassDeclaration*>(member->context()->owner()); 0190 if (classDec) { 0191 isInterface = (classDec->classType() == ClassDeclarationData::Interface); 0192 } 0193 } 0194 } else { 0195 qCDebug(COMPLETION) << "completion item for implementation was not a classfunction declaration!"; 0196 functionName = m_declaration->identifier().toString(); 0197 } 0198 0199 if (m_type == ImplementationItem::OverrideVar) { 0200 replText += "$" + functionName + " = "; 0201 } else { 0202 if (!modifiers.contains(QStringLiteral("function"))) { 0203 replText += QLatin1String("function "); 0204 } 0205 0206 replText += functionName; 0207 0208 { 0209 // get argument list 0210 QString arguments; 0211 createArgumentList(*this, arguments, nullptr, true); 0212 replText += arguments; 0213 } 0214 0215 QString arguments; 0216 QVector<Declaration*> parameters; 0217 if (DUChainUtils::argumentContext(m_declaration.data())) 0218 parameters = DUChainUtils::argumentContext(m_declaration.data())->localDeclarations(); 0219 arguments = '('; 0220 bool first = true; 0221 foreach(Declaration* dec, parameters) { 0222 if (first) 0223 first = false; 0224 else 0225 arguments += QLatin1String(", "); 0226 0227 arguments += '$' + dec->identifier().toString(); 0228 } 0229 arguments += ')'; 0230 0231 bool voidReturnType = false; 0232 if (auto functionType = m_declaration->abstractType().dynamicCast<FunctionType>()) { 0233 AbstractType::Ptr retType = functionType->returnType(); 0234 if (retType->equals(new IntegralType(IntegralType::TypeVoid))) { 0235 voidReturnType = true; 0236 } 0237 } 0238 0239 replText += QStringLiteral("\n%1{\n%1 ").arg(indentation); 0240 if (isInterface || m_type == ImplementationItem::Implement) { 0241 } else if (!isConstructorOrDestructor && !voidReturnType) { 0242 replText += QStringLiteral("$ret = parent::%2%3;\n%1 return $ret;").arg(indentation, functionName, arguments); 0243 } else { 0244 replText += QStringLiteral("parent::%1%2;").arg(functionName, arguments); 0245 } 0246 replText += QStringLiteral("\n%1}\n%1") 0247 .arg(indentation); 0248 0249 } 0250 0251 0252 //TODO: properly place the cursor inside the {} part 0253 document->replaceText(replaceRange, replText); 0254 0255 } else { 0256 qCDebug(COMPLETION) << "Declaration disappeared"; 0257 } 0258 } 0259 0260 }