File indexing completed on 2024-05-05 16:42:26
0001 /* 0002 SPDX-FileCopyrightText: 2007 Piyush verma <piyush.verma@gmail.com> 0003 SPDX-FileCopyrightText: 2007 Andreas Pakulat <apaku@gmx.de> 0004 SPDX-FileCopyrightText: 2010-2014 Sven Brauch <svenbrauch@googlemail.com> 0005 0006 SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #ifndef PYTHON_CONTEXTBUILDER_H 0010 #define PYTHON_CONTEXTBUILDER_H 0011 0012 #include "astdefaultvisitor.h" 0013 0014 #include <language/duchain/builders/abstractcontextbuilder.h> 0015 #include <language/editor/rangeinrevision.h> 0016 #include <language/duchain/topducontext.h> 0017 0018 #include "pythonduchainexport.h" 0019 0020 using namespace KDevelop; 0021 0022 namespace Python 0023 { 0024 0025 class PythonEditorIntegrator; 0026 class FileIndentInformation; 0027 0028 typedef KDevelop::AbstractContextBuilder<Ast, Identifier> ContextBuilderBase; 0029 0030 /** 0031 * @brief The context builder, which calculates the scopes in a file. 0032 * 0033 * For practical reasons, some building of scopes also happens 0034 * in the declaration builder. 0035 */ 0036 class KDEVPYTHONDUCHAIN_EXPORT ContextBuilder: public ContextBuilderBase, public Python::AstDefaultVisitor 0037 { 0038 public: 0039 ContextBuilder() = default; 0040 0041 /** 0042 * @brief Entry function called by KDevPlatform API. 0043 */ 0044 ReferencedTopDUContext build(const KDevelop::IndexedString& url, Ast* node, 0045 const ReferencedTopDUContext& updateContext = ReferencedTopDUContext()) override; 0046 0047 /** 0048 * @brief Set the editor integrator. 0049 */ 0050 void setEditor(PythonEditorIntegrator* editor); 0051 0052 /** 0053 * @brief Set the modification revision which will be created by this builder. 0054 */ 0055 void setFutureModificationRevision(const ModificationRevision& rev); 0056 0057 /** 0058 * @brief Get the editor integrator. 0059 */ 0060 PythonEditorIntegrator* editor() const; 0061 0062 /** 0063 * @brief Find the URL which would be imported by the dotted name @p name. 0064 * 0065 * @param name a dotted name, such as PyQt4.QtCore.QWidget 0066 * @param currentDocument the current document, for resolving relative imports 0067 * @return QPair< QUrl, QStringList > the URL if found, and a list of components from 0068 * the end of the name which were not yet consumed 0069 */ 0070 static QPair<QUrl, QStringList> findModulePath(const QString& name, const QUrl& currentDocument); 0071 0072 /** 0073 * @brief Get the range which encompasses the given @p node. 0074 * @param moveRight true to make the range longer by one character 0075 */ 0076 static RangeInRevision rangeForNode(Ast* node, bool moveRight); 0077 0078 /** 0079 * @brief Get the range of @p identifier. 0080 * @param moveRight true to make the range longer by one character 0081 */ 0082 static RangeInRevision rangeForNode(Identifier* identifier, bool moveRight); 0083 0084 /** 0085 * @brief Find the range of a comprehension. 0086 * @param node Comprehension to find the range of, e.g. a ListComprehensionAst. 0087 */ 0088 RangeInRevision comprehensionRange(Ast* node); 0089 0090 /** 0091 * @brief Calculate the range of the arguments context of the given @p node. 0092 * @return Range the argument list of this function encompasses. 0093 */ 0094 RangeInRevision rangeForArgumentsContext(Python::FunctionDefinitionAst* node); 0095 0096 /** 0097 * @brief Add @p module to the list of unresolved imports in this builder. 0098 */ 0099 void addUnresolvedImport(const IndexedString& module); 0100 0101 /** 0102 * @brief Retrieve a list of imports not resolved by this builder pass. 0103 */ 0104 QList<IndexedString> unresolvedImports() const; 0105 0106 public: 0107 // ugly because this collides with currentDocument(), but we have to use it; 0108 // for some reason the UseBuilder does not have m_url set, and it's private (not even protected) to AbstractContextBuilder. 0109 // so at least keep this consistent within the plugin and use this everywhere. 0110 // maybe we can remove this hack later. TODO maybe change something in kdevplatform, or maybe we're doing something wrong here? 0111 IndexedString currentlyParsedDocument() const; 0112 void setCurrentlyParsedDocument(const IndexedString& document); 0113 0114 protected: 0115 /** 0116 * @brief Create a new top context and set it as this builder's active context. 0117 * 0118 * @param range Range to encompass 0119 * @return KDevelop::TopDUContext* weak pointer to the created top context. 0120 */ 0121 TopDUContext* newTopContext(const RangeInRevision& range, ParsingEnvironmentFile* file) override; 0122 0123 /** 0124 * @brief Create a new context. 0125 * Overridden to create instances of Python's specialized DUContext. 0126 */ 0127 KDevelop::DUContext* newContext(const KDevelop::RangeInRevision& range) override; 0128 0129 protected: 0130 // AST visitor functions 0131 void visitLambda(Python::LambdaAst * node) override; 0132 void visitFunctionDefinition( FunctionDefinitionAst* ) override; 0133 void visitClassDefinition( ClassDefinitionAst* ) override; 0134 void visitCode(CodeAst* node) override; 0135 void visitListComprehension(ListComprehensionAst* node) override; 0136 void visitDictionaryComprehension(DictionaryComprehensionAst* node) override; 0137 void visitGeneratorExpression(GeneratorExpressionAst* node) override; 0138 void visitComprehensionCommon(Ast* node); 0139 0140 void startVisiting(Ast* node) override; 0141 KDevelop::RangeInRevision editorFindRange(Ast* fromNode, Ast* toNode) override; 0142 virtual KDevelop::CursorInRevision editorFindPositionSafe(Ast* node); 0143 virtual KDevelop::CursorInRevision startPos(Ast* node); 0144 KDevelop::QualifiedIdentifier identifierForNode(Identifier* node) override; 0145 0146 /** 0147 * @brief Set @p context as the context of @p node. 0148 * The context is stored inside the AST itself. 0149 */ 0150 void setContextOnNode(Ast* node, KDevelop::DUContext* context) override; 0151 0152 /** 0153 * @brief Get the context set on @p node as previously set by @ref setContextOnNode. 0154 */ 0155 KDevelop::DUContext* contextFromNode(Ast* node) override; 0156 0157 /** 0158 * @brief Add the saved list of contexts to import to the current context, and clear it. 0159 */ 0160 void addImportedContexts(); 0161 0162 // helpers which need to be called separately from DeclarationBuilder 0163 virtual void visitFunctionArguments(FunctionDefinitionAst* node); 0164 virtual void visitFunctionBody(FunctionDefinitionAst* node); 0165 void openContextForClassDefinition(ClassDefinitionAst* node); 0166 0167 protected: 0168 // those functions can be used if you want to do something to a context you are in, 0169 // but which is not the current one. Example: You want to add a variable to a class context, 0170 // but the current context is inside that class context (method declaration, ...) 0171 bool contextAlreadyOpen(DUContextPointer context); 0172 void activateAlreadyOpenedContext(DUContextPointer context); 0173 void closeAlreadyOpenedContext(DUContextPointer context); 0174 QList<DUContextPointer> m_temporarilyClosedContexts; 0175 0176 protected: 0177 // true if the first of the two performed passes is currently active 0178 bool m_prebuilding = false; 0179 0180 // List of imports which were encountered, but could not be resolved 0181 QList<IndexedString> m_unresolvedImports; 0182 0183 // The ModificationRevision this context will be valid for 0184 ModificationRevision m_futureModificationRevision; 0185 0186 IndexedString m_currentlyParsedDocument; 0187 0188 private: 0189 // The top-context being built. 0190 ReferencedTopDUContext m_topContext; 0191 PythonEditorIntegrator* m_editor = nullptr; 0192 QList<KDevelop::DUContext*> m_importedParentContexts; 0193 QSharedPointer<FileIndentInformation> m_indentInformationCache; 0194 }; 0195 0196 } 0197 0198 #endif