File indexing completed on 2024-05-12 04:39:09
0001 /* 0002 SPDX-FileCopyrightText: 2009 David Nolden <david.nolden.kdevelop@art-master.de> 0003 SPDX-FileCopyrightText: 2015 Sergey Kalinichev <kalinichev.so.0@gmail.com> 0004 0005 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0006 */ 0007 0008 #include "sourcemanipulation.h" 0009 0010 #include <interfaces/icore.h> 0011 #include <interfaces/isourceformattercontroller.h> 0012 0013 #include <language/codegen/coderepresentation.h> 0014 0015 #include <language/duchain/abstractfunctiondeclaration.h> 0016 #include <language/duchain/classdeclaration.h> 0017 #include <language/duchain/classfunctiondeclaration.h> 0018 #include <language/duchain/classmemberdeclaration.h> 0019 #include <language/duchain/types/enumeratortype.h> 0020 #include <language/duchain/types/functiontype.h> 0021 0022 #include "codegenhelper.h" 0023 #include "adaptsignatureaction.h" 0024 #include "util/clangdebug.h" 0025 0026 using namespace KDevelop; 0027 0028 namespace 0029 { 0030 QualifiedIdentifier stripPrefixes(const DUContextPointer& ctx, const QualifiedIdentifier& id) 0031 { 0032 if (!ctx) { 0033 return id; 0034 } 0035 0036 auto imports = ctx->fullyApplyAliases({}, ctx->topContext()); 0037 if (imports.contains(id)) { 0038 return {}; /// The id is a namespace that is imported into the current context 0039 } 0040 0041 auto basicDecls = ctx->findDeclarations(id, CursorInRevision::invalid(), {}, nullptr, 0042 (DUContext::SearchFlags)(DUContext::NoSelfLookUp | DUContext::NoFiltering)); 0043 0044 if (basicDecls.isEmpty()) { 0045 return id; 0046 } 0047 0048 auto newId = id.mid(1); 0049 auto result = id; 0050 while (!newId.isEmpty()) { 0051 auto foundDecls 0052 = ctx->findDeclarations(newId, CursorInRevision::invalid(), {}, nullptr, 0053 (DUContext::SearchFlags)(DUContext::NoSelfLookUp | DUContext::NoFiltering)); 0054 0055 if (foundDecls == basicDecls) { 0056 result = newId; // must continue to find the shortest possible identifier 0057 // esp. for cases where nested namespaces are used (e.g. using namespace a::b::c;) 0058 newId = newId.mid(1); 0059 } 0060 } 0061 0062 return result; 0063 } 0064 0065 // Re-indents the code so the leftmost line starts at zero 0066 QString zeroIndentation(const QString& str, int fromLine = 0) 0067 { 0068 QStringList lines = str.split(QLatin1Char('\n')); 0069 QStringList ret; 0070 0071 if (fromLine < lines.size()) { 0072 ret = lines.mid(0, fromLine); 0073 lines = lines.mid(fromLine); 0074 } 0075 0076 QRegExp nonWhiteSpace(QStringLiteral("\\S")); 0077 int minLineStart = 10000; 0078 for (const auto& line : qAsConst(lines)) { 0079 int lineStart = line.indexOf(nonWhiteSpace); 0080 if (lineStart < minLineStart) { 0081 minLineStart = lineStart; 0082 } 0083 } 0084 0085 ret.reserve(ret.size() + lines.size()); 0086 for (const auto& line : qAsConst(lines)) { 0087 ret << line.mid(minLineStart); 0088 } 0089 0090 return ret.join(QLatin1Char('\n')); 0091 } 0092 } 0093 0094 DocumentChangeSet SourceCodeInsertion::changes() 0095 { 0096 return m_changeSet; 0097 } 0098 0099 void SourceCodeInsertion::setSubScope(const QualifiedIdentifier& scope) 0100 { 0101 m_scope = scope; 0102 0103 if (!m_context) { 0104 return; 0105 } 0106 0107 QStringList needNamespace = m_scope.toStringList(); 0108 0109 bool foundChild = true; 0110 while (!needNamespace.isEmpty() && foundChild) { 0111 foundChild = false; 0112 0113 const auto childContexts = m_context->childContexts(); 0114 for (DUContext* child : childContexts) { 0115 clangDebug() << "checking child" << child->localScopeIdentifier().toString() << "against" 0116 << needNamespace.first(); 0117 if (child->localScopeIdentifier().toString() == needNamespace.first() && child->type() == DUContext::Namespace) { 0118 clangDebug() << "taking"; 0119 m_context = child; 0120 foundChild = true; 0121 needNamespace.pop_front(); 0122 break; 0123 } 0124 } 0125 } 0126 0127 m_scope = stripPrefixes(m_context, QualifiedIdentifier(needNamespace.join(QLatin1String("::")))); 0128 } 0129 0130 QString SourceCodeInsertion::applySubScope(const QString& decl) const 0131 { 0132 if (m_scope.isEmpty()) { 0133 return decl; 0134 } 0135 0136 const bool isClassContext = (m_context && m_context->type() == DUContext::Class); 0137 const QLatin1String scopeType = isClassContext ? QLatin1String("struct ") : QLatin1String("namespace "); 0138 const QLatin1String scopeClose = isClassContext ? QLatin1String(";") : QLatin1String(""); 0139 0140 QString ret; 0141 const auto scopes = m_scope.toStringList(); 0142 for (const QString& scope : scopes) { 0143 ret += scopeType + scope + QLatin1String(" {\n"); 0144 } 0145 0146 ret += decl; 0147 ret += QString(QLatin1Char('}') + scopeClose + QLatin1Char('\n')).repeated(m_scope.count()); 0148 0149 return ret; 0150 } 0151 0152 SourceCodeInsertion::SourceCodeInsertion(TopDUContext* topContext) 0153 : m_context(topContext) 0154 , m_topContext(topContext) 0155 , m_codeRepresentation(createCodeRepresentation(m_topContext->url())) 0156 { 0157 } 0158 0159 SourceCodeInsertion::~SourceCodeInsertion() 0160 { 0161 } 0162 0163 KTextEditor::Cursor SourceCodeInsertion::end() const 0164 { 0165 auto ret = m_context->rangeInCurrentRevision().end(); 0166 if (m_codeRepresentation && m_codeRepresentation->lines() && dynamic_cast<TopDUContext*>(m_context.data())) { 0167 ret.setLine(m_codeRepresentation->lines() - 1); 0168 ret.setColumn(m_codeRepresentation->line(ret.line()).size()); 0169 } 0170 return ret; 0171 } 0172 0173 KTextEditor::Range SourceCodeInsertion::insertionRange(int line) 0174 { 0175 if (line == 0 || !m_codeRepresentation) { 0176 return KTextEditor::Range(line, 0, line, 0); 0177 } 0178 0179 KTextEditor::Range range(line - 1, m_codeRepresentation->line(line - 1).size(), line - 1, 0180 m_codeRepresentation->line(line - 1).size()); 0181 // If the context finishes on that line, then this will need adjusting 0182 if (!m_context->rangeInCurrentRevision().contains(range)) { 0183 range.start() = m_context->rangeInCurrentRevision().end(); 0184 if (range.start().column() > 0) { 0185 range.start() = range.start() - KTextEditor::Cursor(0, 1); 0186 } 0187 range.end() = range.start(); 0188 } 0189 0190 return range; 0191 } 0192 0193 bool SourceCodeInsertion::insertFunctionDeclaration(KDevelop::Declaration* declaration, const Identifier& id, const QString& body) 0194 { 0195 if (!m_context) { 0196 return false; 0197 } 0198 0199 Signature signature; 0200 const auto localDeclarations = declaration->internalContext()->localDeclarations(); 0201 signature.parameters.reserve(localDeclarations.count()); 0202 std::transform(localDeclarations.begin(), localDeclarations.end(), 0203 std::back_inserter(signature.parameters), 0204 [] (Declaration* argument) -> ParameterItem 0205 { return {IndexedType(argument->indexedType()), argument->identifier().toString()}; }); 0206 0207 auto funcType = declaration->type<FunctionType>(); 0208 auto returnType = funcType->returnType(); 0209 if (auto classFunDecl = dynamic_cast<const ClassFunctionDeclaration*>(declaration)) { 0210 if (classFunDecl->isConstructor() || classFunDecl->isDestructor()) { 0211 returnType = nullptr; 0212 } 0213 } 0214 signature.returnType = IndexedType(returnType); 0215 signature.isConst = funcType->modifiers() & AbstractType::ConstModifier; 0216 0217 QString decl = CodegenHelper::makeSignatureString(declaration, signature, true); 0218 decl.replace(declaration->qualifiedIdentifier().toString(), id.toString()); 0219 0220 if (body.isEmpty()) { 0221 decl += QLatin1Char(';'); 0222 } else { 0223 if (!body.startsWith(QLatin1Char(' ')) && !body.startsWith(QLatin1Char('\n'))) { 0224 decl += QLatin1Char(' '); 0225 } 0226 decl += zeroIndentation(body); 0227 } 0228 0229 int line = findInsertionPoint(); 0230 0231 decl = QLatin1String("\n\n") + applySubScope(decl); 0232 const auto formatter = ICore::self()->sourceFormatterController()->fileFormatter(declaration->url().toUrl()); 0233 if (formatter) { 0234 decl = formatter->format(decl); 0235 } 0236 0237 return m_changeSet.addChange(DocumentChange(m_context->url(), insertionRange(line), QString(), decl)); 0238 } 0239 0240 int SourceCodeInsertion::findInsertionPoint() const 0241 { 0242 int line = end().line(); 0243 0244 const auto localDeclarations = m_context->localDeclarations(); 0245 for (auto* decl : localDeclarations) { 0246 if (m_context->type() == DUContext::Class) { 0247 continue; 0248 } 0249 0250 if (!dynamic_cast<AbstractFunctionDeclaration*>(decl)) { 0251 continue; 0252 } 0253 0254 line = decl->range().end.line + 1; 0255 if (decl->internalContext()) { 0256 line = decl->internalContext()->range().end.line + 1; 0257 } 0258 } 0259 0260 clangDebug() << line << m_context->scopeIdentifier(true) << m_context->rangeInCurrentRevision() 0261 << m_context->url().toUrl() << m_context->parentContext(); 0262 clangDebug() << "count of declarations:" << m_context->topContext()->localDeclarations().size(); 0263 0264 return line; 0265 }