Index: duchain/helpers.h =================================================================== --- duchain/helpers.h +++ duchain/helpers.h @@ -45,183 +45,179 @@ using namespace KDevelop; namespace Python { +namespace Helper { + +/** get search paths for python files **/ +KDEVPYTHONDUCHAIN_EXPORT QVector getSearchPaths(const QUrl& workingOnDocument); +DUChainPointer& documentationFileContext(); + +QStringList getDataDirs(); +IndexedString getDocumentationFile(); +KDEVPYTHONDUCHAIN_EXPORT ReferencedTopDUContext getDocumentationFileContext(); + +KDEVPYTHONDUCHAIN_EXPORT QUrl getCorrectionFile(const QUrl& document); +KDEVPYTHONDUCHAIN_EXPORT QUrl getLocalCorrectionFile(const QUrl& document); + +KDEVPYTHONDUCHAIN_EXPORT QMutex& cacheMutex(); +KDEVPYTHONDUCHAIN_EXPORT QMap>& cachedCustomIncludes(); +KDEVPYTHONDUCHAIN_EXPORT QMap>& cachedSearchPaths(); + +KDEVPYTHONDUCHAIN_EXPORT QMutex& projectPathLock(); +KDEVPYTHONDUCHAIN_EXPORT QVector& projectSearchPaths(); + +AbstractType::Ptr extractTypeHints(AbstractType::Ptr type); + +/** + * @brief Get the declaration of 'accessed.attribute', or return null. + * + * @param accessed Type (Structure or Unsure) that should have this attribute. + * @param attribute Which attribute to look for. + * @param topContext Top context (for this file?) + * @return Declaration* of the attribute, or null. + * If UnsureType with >1 matching attributes, returns an arbitrary choice. + **/ +KDEVPYTHONDUCHAIN_EXPORT KDevelop::Declaration* accessAttribute(const KDevelop::AbstractType::Ptr accessed, + const KDevelop::IndexedIdentifier& attribute, + const KDevelop::TopDUContext* topContext); + +inline KDevelop::Declaration* accessAttribute(const KDevelop::AbstractType::Ptr accessed, + const QString& attribute, const KDevelop::TopDUContext* topContext) +{ + return accessAttribute(accessed, IndexedIdentifier(KDevelop::Identifier(attribute)), topContext); +} -class KDEVPYTHONDUCHAIN_EXPORT Helper { -public: - /** get search paths for python files **/ - static QVector getSearchPaths(const QUrl& workingOnDocument); - static QStringList dataDirs; - static IndexedString documentationFile; - static QStringList correctionFileDirs; - static QString localCorrectionFileDir; - static DUChainPointer documentationFileContext; - - static QStringList getDataDirs(); - static IndexedString getDocumentationFile(); - static ReferencedTopDUContext getDocumentationFileContext(); - - static QUrl getCorrectionFile(const QUrl& document); - static QUrl getLocalCorrectionFile(const QUrl& document); - - static QMutex cacheMutex; - static QMap> cachedCustomIncludes; - static QMap> cachedSearchPaths; - - static QMutex projectPathLock; - static QVector projectSearchPaths; - - static AbstractType::Ptr extractTypeHints(AbstractType::Ptr type); - - /** - * @brief Get the declaration of 'accessed.attribute', or return null. - * - * @param accessed Type (Structure or Unsure) that should have this attribute. - * @param attribute Which attribute to look for. - * @param topContext Top context (for this file?) - * @return Declaration* of the attribute, or null. - * If UnsureType with >1 matching attributes, returns an arbitrary choice. - **/ - static KDevelop::Declaration* accessAttribute(const KDevelop::AbstractType::Ptr accessed, - const KDevelop::IndexedIdentifier& attribute, - const KDevelop::TopDUContext* topContext); - - static KDevelop::Declaration* accessAttribute(const KDevelop::AbstractType::Ptr accessed, - const QString& attribute, const KDevelop::TopDUContext* topContext) { - return accessAttribute(accessed, IndexedIdentifier(KDevelop::Identifier(attribute)), topContext); +KDEVPYTHONDUCHAIN_EXPORT AbstractType::Ptr resolveAliasType(const AbstractType::Ptr eventualAlias); + +/** + * @brief Get the content type(s) of something that is an iterable. + * + * @param iterable Type to get the contents of. Can be an unsure. + * @return KDevelop::AbstractType::Ptr Content type. Might be an unsure. + */ +AbstractType::Ptr contentOfIterable(const AbstractType::Ptr iterable, const TopDUContext* topContext); + +/** + * @brief Get a list of types inside the passed type which match the specified filter. + * The filter will be matched against the type only if it is not an unsure type, + * or else against all types inside that unsure type. + * @param type The type to search + * @param accept Filter function, return true if you want the type. + * @return QList< KDevelop::AbstractType::Ptr > list of types accepted by the filter. + */ +template +QList filterType(AbstractType::Ptr type, std::function accept, + std::function map = + std::function()) +{ + QList types; + if ( ! type ) { + return types; } - - static AbstractType::Ptr resolveAliasType(const AbstractType::Ptr eventualAlias); - - /** - * @brief Get the content type(s) of something that is an iterable. - * - * @param iterable Type to get the contents of. Can be an unsure. - * @return KDevelop::AbstractType::Ptr Content type. Might be an unsure. - */ - static AbstractType::Ptr contentOfIterable(const AbstractType::Ptr iterable, const TopDUContext* topContext); - - /** - * @brief Get a list of types inside the passed type which match the specified filter. - * The filter will be matched against the type only if it is not an unsure type, - * or else against all types inside that unsure type. - * @param type The type to search - * @param accept Filter function, return true if you want the type. - * @return QList< KDevelop::AbstractType::Ptr > list of types accepted by the filter. - */ - template - static QList filterType(AbstractType::Ptr type, std::function accept, - std::function map = - std::function()) - { - QList types; - if ( ! type ) { - return types; - } - if ( type->whichType() == KDevelop::AbstractType::TypeUnsure ) { - UnsureType::Ptr unsure(type.cast()); - for ( unsigned int i = 0; i < unsure->typesSize(); i++ ) { - AbstractType::Ptr t = unsure->types()[i].abstractType(); - if ( accept(t) ) { - types << ( map ? map(t) : t.cast() ); - } + if ( type->whichType() == KDevelop::AbstractType::TypeUnsure ) { + UnsureType::Ptr unsure(type.cast()); + for ( unsigned int i = 0; i < unsure->typesSize(); i++ ) { + AbstractType::Ptr t = unsure->types()[i].abstractType(); + if ( accept(t) ) { + types << ( map ? map(t) : t.cast() ); } } - else if ( accept(type) ) { - types << ( map ? map(type) : type.cast() ); - } - return types; } + else if ( accept(type) ) { + types << ( map ? map(type) : type.cast() ); + } + return types; +} - static void scheduleDependency(const IndexedString& dependency, int betterThanPriority); - - static KDevelop::IndexedDeclaration declarationUnderCursor(bool allowUse = true); - - struct FuncInfo { FunctionDeclaration* declaration; bool isConstructor; }; - /** - * @brief Finds whether the specified called declaration is a function declaration, and if not, - * checks for a class declaration; then returns the constructor - * - * @param called the declaration to check - * @param isAlias whether the called declaration aliases a class or function definition. - * @return the function pointer which was found, or an invalid pointer, and a bool - * which is true when it is a constructor - **/ - static FuncInfo functionForCalled(Declaration* called, bool isAlias=true); - - static bool docstringContainsHint(const QString& comment, const QString& hintName, QStringList* args = nullptr) { - // TODO cache types! this is horribly inefficient - const QString search = "! " + hintName + " !"; - int index = comment.indexOf(search); - if ( index >= 0 ) { - if ( args ) { - int eol = comment.indexOf('\n', index); - int start = index+search.size()+1; - QString decl = comment.mid(start, eol-start); - *args = decl.split(' '); - } - return true; +void scheduleDependency(const IndexedString& dependency, int betterThanPriority); + +KDEVPYTHONDUCHAIN_EXPORT KDevelop::IndexedDeclaration declarationUnderCursor(bool allowUse = true); + +struct FuncInfo { FunctionDeclaration* declaration; bool isConstructor; }; +/** + * @brief Finds whether the specified called declaration is a function declaration, and if not, + * checks for a class declaration; then returns the constructor + * + * @param called the declaration to check + * @param isAlias whether the called declaration aliases a class or function definition. + * @return the function pointer which was found, or an invalid pointer, and a bool + * which is true when it is a constructor + **/ +KDEVPYTHONDUCHAIN_EXPORT FuncInfo functionForCalled(Declaration* called, bool isAlias=true); + +inline bool docstringContainsHint(const QString& comment, const QString& hintName, QStringList* args = nullptr) { + // TODO cache types! this is horribly inefficient + const QString search = "! " + hintName + " !"; + int index = comment.indexOf(search); + if ( index >= 0 ) { + if ( args ) { + int eol = comment.indexOf('\n', index); + int start = index+search.size()+1; + QString decl = comment.mid(start, eol-start); + *args = decl.split(' '); } - return false; + return true; } + return false; +} - /** - * @copydoc TypeUtils::mergeTypes - */ - static AbstractType::Ptr mergeTypes(AbstractType::Ptr type, const AbstractType::Ptr newType); - - /** - * @brief Like mergeTypes(), but merges a list of types into a newly allocated type. - * Returns mixed if the list is empty. - * @return KDevelop::AbstractType::Ptr an unsure type consisting of all types in the list. - */ - template - static AbstractType::Ptr foldTypes(QList types, std::function transform - = std::function()) - { - AbstractType::Ptr result(new IntegralType(IntegralType::TypeMixed)); - for ( T type : types ) { - result = Helper::mergeTypes(result, transform ? transform(type) : AbstractType::Ptr::staticCast(type)); - } - return result; - }; - - /** check whether the argument is a null, mixed, or none integral type **/ - static bool isUsefulType(AbstractType::Ptr type); - - enum ContextSearchFlags { - NoFlags, - PublicOnly - }; - - /** - * @brief Find all internal contexts for this class and its base classes recursively - * - * @param classType Type object for the class to search contexts - * @param context TopContext for finding the declarations for types - * @return list of contexts which were found - **/ - static QVector internalContextsForClass(const KDevelop::StructureType::Ptr classType, - const TopDUContext* context, - ContextSearchFlags flags = NoFlags, int depth = 0); - /** - * @brief Resolve the given declaration if it is an alias declaration. - * - * @param decl the declaration to resolve - * @return :Declaration* decl if not an alias declaration, decl->aliasedDeclaration().data otherwise - * DUChain must be read locked - **/ - static Declaration* resolveAliasDeclaration(Declaration* decl); - - static Declaration* declarationForName(const QString& name, const CursorInRevision& location, - DUChainPointer context); - - static Declaration* declarationForName(const Python::NameAst* name, CursorInRevision location, - DUChainPointer context); - - static QString getPythonExecutablePath(IProject* project); +/** + * @copydoc TypeUtils::mergeTypes + */ +AbstractType::Ptr mergeTypes(AbstractType::Ptr type, const AbstractType::Ptr newType); + +/** + * @brief Like mergeTypes(), but merges a list of types into a newly allocated type. + * Returns mixed if the list is empty. + * @return KDevelop::AbstractType::Ptr an unsure type consisting of all types in the list. + */ +template +AbstractType::Ptr foldTypes(QList types, std::function transform + = std::function()) +{ + AbstractType::Ptr result(new IntegralType(IntegralType::TypeMixed)); + for ( T type : types ) { + result = Helper::mergeTypes(result, transform ? transform(type) : AbstractType::Ptr::staticCast(type)); + } + return result; }; -} +/** check whether the argument is a null, mixed, or none integral type **/ +bool isUsefulType(AbstractType::Ptr type); -#endif +enum ContextSearchFlags { + NoFlags, + PublicOnly +}; +/** + * @brief Find all internal contexts for this class and its base classes recursively + * + * @param classType Type object for the class to search contexts + * @param context TopContext for finding the declarations for types + * @return list of contexts which were found + **/ +KDEVPYTHONDUCHAIN_EXPORT QVector internalContextsForClass(const KDevelop::StructureType::Ptr classType, + const TopDUContext* context, + ContextSearchFlags flags = NoFlags, int depth = 0); +/** + * @brief Resolve the given declaration if it is an alias declaration. + * + * @param decl the declaration to resolve + * @return :Declaration* decl if not an alias declaration, decl->aliasedDeclaration().data otherwise + * DUChain must be read locked + **/ +KDEVPYTHONDUCHAIN_EXPORT Declaration* resolveAliasDeclaration(Declaration* decl); + +KDEVPYTHONDUCHAIN_EXPORT Declaration* declarationForName(const QString& name, const CursorInRevision& location, + DUChainPointer context); + +Declaration* declarationForName(const Python::NameAst* name, CursorInRevision location, + DUChainPointer context); + +KDEVPYTHONDUCHAIN_EXPORT QString getPythonExecutablePath(IProject* project); + + +} // namespace Helper +} // namespace Python + +#endif Index: duchain/helpers.cpp =================================================================== --- duchain/helpers.cpp +++ duchain/helpers.cpp @@ -62,16 +62,35 @@ namespace Python { -QMap> Helper::cachedCustomIncludes; -QMap> Helper::cachedSearchPaths; -QVector Helper::projectSearchPaths; -QStringList Helper::dataDirs; -IndexedString Helper::documentationFile; -DUChainPointer Helper::documentationFileContext = DUChainPointer(nullptr); -QStringList Helper::correctionFileDirs; -QString Helper::localCorrectionFileDir; -QMutex Helper::cacheMutex; -QMutex Helper::projectPathLock; +QMap>& Helper::cachedCustomIncludes() { + static QMap> value; + return value; +} + +QMap>& Helper::cachedSearchPaths() { + static QMap> value; + return value; +} + +QVector& Helper::projectSearchPaths() { + static QVector value; + return value; +} + +DUChainPointer& Helper::documentationFileContext() { + static DUChainPointer value = DUChainPointer(nullptr); + return value; +} + +QMutex& Helper::cacheMutex() { + static QMutex value; + return value; +} + +QMutex& Helper::projectPathLock() { + static QMutex value; + return value; +} void Helper::scheduleDependency(const IndexedString& dependency, int betterThanPriority) { @@ -282,31 +301,29 @@ } QStringList Helper::getDataDirs() { - if ( Helper::dataDirs.isEmpty() ) { - Helper::dataDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, "kdevpythonsupport/documentation_files", QStandardPaths::LocateDirectory); - } - return Helper::dataDirs; + static QStringList dataDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, + "kdevpythonsupport/documentation_files", QStandardPaths::LocateDirectory); + return dataDirs; } KDevelop::IndexedString Helper::getDocumentationFile() { - if ( Helper::documentationFile.isEmpty() ) { - auto path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kdevpythonsupport/documentation_files/builtindocumentation.py"); - Helper::documentationFile = IndexedString(path); - } - return Helper::documentationFile; + static auto documentationFile = IndexedString(QStandardPaths::locate(QStandardPaths::GenericDataLocation, + "kdevpythonsupport/documentation_files/builtindocumentation.py")); + return documentationFile; } ReferencedTopDUContext Helper::getDocumentationFileContext() { - if ( Helper::documentationFileContext ) { - return ReferencedTopDUContext(Helper::documentationFileContext.data()); + QMutexLocker lock(&cacheMutex()); + if ( Helper::documentationFileContext() ) { + return ReferencedTopDUContext(Helper::documentationFileContext().data()); } else { DUChainReadLocker lock; auto file = Helper::getDocumentationFile(); ReferencedTopDUContext ctx = ReferencedTopDUContext(DUChain::self()->chainForDocument(file)); - Helper::documentationFileContext = DUChainPointer(ctx.data()); + Helper::documentationFileContext() = DUChainPointer(ctx.data()); return ctx; } } @@ -348,10 +365,12 @@ QUrl Helper::getCorrectionFile(const QUrl& document) { - if ( Helper::correctionFileDirs.isEmpty() ) { - Helper::correctionFileDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, - "kdevpythonsupport/correction_files/", - QStandardPaths::LocateDirectory); + static QStringList correctionFileDirs; + if ( correctionFileDirs.isEmpty() ) { + QMutexLocker lock(&cacheMutex()); + correctionFileDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, + "kdevpythonsupport/correction_files/", + QStandardPaths::LocateDirectory); } foreach (QString correctionFileDir, correctionFileDirs) { @@ -373,17 +392,20 @@ QUrl Helper::getLocalCorrectionFile(const QUrl& document) { - if ( Helper::localCorrectionFileDir.isNull() ) { - Helper::localCorrectionFileDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "kdevpythonsupport/correction_files/"; + static QString localCorrectionFileDir; + if ( localCorrectionFileDir.isEmpty() ) { + QMutexLocker lock(&cacheMutex()); + localCorrectionFileDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + + QLatin1Char('/') + "kdevpythonsupport/correction_files/"; } auto absolutePath = QUrl(); foreach ( const auto& basePath, Helper::getSearchPaths(QUrl()) ) { if ( ! basePath.isParentOf(document) ) { continue; } auto path = QDir(basePath.path()).relativeFilePath(document.path()); - absolutePath = QUrl::fromLocalFile(Helper::localCorrectionFileDir + path); + absolutePath = QUrl::fromLocalFile(localCorrectionFileDir + path); break; } return absolutePath; @@ -452,23 +474,23 @@ QVector Helper::getSearchPaths(const QUrl& workingOnDocument) { - QMutexLocker lock(&Helper::cacheMutex); + QMutexLocker lock(&Helper::cacheMutex()); QVector searchPaths; // search in the projects, as they're packages and likely to be installed or added to PYTHONPATH later // and also add custom include paths that are defined in the projects auto project = ICore::self()->projectController()->findProjectForUrl(workingOnDocument); { - QMutexLocker lock(&Helper::projectPathLock); - searchPaths << Helper::projectSearchPaths; - searchPaths << Helper::cachedCustomIncludes.value(project); + QMutexLocker lock(&Helper::projectPathLock()); + searchPaths << Helper::projectSearchPaths(); + searchPaths << Helper::cachedCustomIncludes().value(project); } foreach ( const QString& path, getDataDirs() ) { searchPaths.append(QUrl::fromLocalFile(path)); } - if ( !cachedSearchPaths.contains(project) ) { + if ( !cachedSearchPaths().contains(project) ) { QVector cachedForProject; qCDebug(KDEV_PYTHON_DUCHAIN) << "*** Collecting search paths..."; QStringList getpath; @@ -495,11 +517,11 @@ cachedForProject.append(QUrl::fromLocalFile(path)); } } - qCDebug(KDEV_PYTHON_DUCHAIN) << " *** Done. Got search paths: " << cachedSearchPaths; - cachedSearchPaths.insert(project, cachedForProject); + qCDebug(KDEV_PYTHON_DUCHAIN) << " *** Done. Got search paths: " << cachedSearchPaths(); + cachedSearchPaths().insert(project, cachedForProject); } - searchPaths.append(cachedSearchPaths.value(project)); + searchPaths.append(cachedSearchPaths().value(project)); auto dir = workingOnDocument.adjusted(QUrl::RemoveFilename); if ( ! dir.isEmpty() ) { Index: projectconfig/projectconfigpage.cpp =================================================================== --- projectconfig/projectconfigpage.cpp +++ projectconfig/projectconfigpage.cpp @@ -37,8 +37,8 @@ { m_configGroup.writeEntry("interpreter", m_ui->pythonInterpreter->text()); // remove cached paths, so they are updated on the next parse job run - QMutexLocker lock(&Helper::cacheMutex); - Helper::cachedSearchPaths.remove(m_project); + QMutexLocker lock(&Helper::cacheMutex()); + Helper::cachedSearchPaths().remove(m_project); } void Python::ProjectConfigPage::defaults() Index: pythonparsejob.cpp =================================================================== --- pythonparsejob.cpp +++ pythonparsejob.cpp @@ -80,8 +80,8 @@ foreach (Path path, iface->includes(project->projectItem(), IDefinesAndIncludesManager::UserDefined)) { m_cachedCustomIncludes.append(path.toUrl()); } - QMutexLocker lock(&Helper::cacheMutex); - Helper::cachedCustomIncludes[project] = m_cachedCustomIncludes; + QMutexLocker lock(&Helper::cacheMutex()); + Helper::cachedCustomIncludes()[project] = m_cachedCustomIncludes; } } @@ -105,10 +105,10 @@ qCDebug(KDEV_PYTHON) << " ====> PARSING ====> parsing file " << document().toUrl() << "; has priority" << parsePriority(); { - QMutexLocker l(&Helper::projectPathLock); - Helper::projectSearchPaths.clear(); + QMutexLocker l(&Helper::projectPathLock()); + Helper::projectSearchPaths().clear(); foreach (IProject* project, ICore::self()->projectController()->projects() ) { - Helper::projectSearchPaths.append(QUrl::fromLocalFile(project->path().path())); + Helper::projectSearchPaths().append(QUrl::fromLocalFile(project->path().path())); } }