1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 Alexander Drozdov.
4 ** Contact: Alexander Drozdov (adrozdoff@gmail.com)
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include "treescanner.h"
27 
28 #include "projectexplorerconstants.h"
29 #include "projectnodeshelper.h"
30 #include "projecttree.h"
31 
32 #include <coreplugin/iversioncontrol.h>
33 #include <coreplugin/vcsmanager.h>
34 
35 #include <cpptools/cpptoolsconstants.h>
36 
37 #include <utils/qtcassert.h>
38 #include <utils/algorithm.h>
39 #include <utils/runextensions.h>
40 
41 #include <memory>
42 
43 namespace ProjectExplorer {
44 
TreeScanner(QObject * parent)45 TreeScanner::TreeScanner(QObject *parent) : QObject(parent)
46 {
47     m_factory = TreeScanner::genericFileType;
48     m_filter = [](const Utils::MimeType &mimeType, const Utils::FilePath &fn) {
49         return isWellKnownBinary(mimeType, fn) && isMimeBinary(mimeType, fn);
50     };
51 
52     connect(&m_futureWatcher, &FutureWatcher::finished, this, &TreeScanner::finished);
53 }
54 
~TreeScanner()55 TreeScanner::~TreeScanner()
56 {
57     disconnect(&m_futureWatcher, nullptr, nullptr, nullptr); // Do not trigger signals anymore!
58 
59     if (!m_futureWatcher.isFinished()) {
60         m_futureWatcher.cancel();
61         m_futureWatcher.waitForFinished();
62     }
63 }
64 
asyncScanForFiles(const Utils::FilePath & directory)65 bool TreeScanner::asyncScanForFiles(const Utils::FilePath &directory)
66 {
67     if (!m_futureWatcher.isFinished())
68         return false;
69 
70     m_scanFuture = Utils::runAsync([this, directory](FutureInterface &fi) {
71         TreeScanner::scanForFiles(fi, directory, m_filter, m_factory);
72     });
73     m_futureWatcher.setFuture(m_scanFuture);
74 
75     return true;
76 }
77 
setFilter(TreeScanner::FileFilter filter)78 void TreeScanner::setFilter(TreeScanner::FileFilter filter)
79 {
80     if (isFinished())
81         m_filter = filter;
82 }
83 
setTypeFactory(TreeScanner::FileTypeFactory factory)84 void TreeScanner::setTypeFactory(TreeScanner::FileTypeFactory factory)
85 {
86     if (isFinished())
87         m_factory = factory;
88 }
89 
future() const90 TreeScanner::Future TreeScanner::future() const
91 {
92     return m_scanFuture;
93 }
94 
isFinished() const95 bool TreeScanner::isFinished() const
96 {
97     return m_futureWatcher.isFinished();
98 }
99 
result() const100 TreeScanner::Result TreeScanner::result() const
101 {
102     if (isFinished())
103         return m_scanFuture.result();
104     return Result();
105 }
106 
release()107 TreeScanner::Result TreeScanner::release()
108 {
109     if (isFinished() && m_scanFuture.resultCount() > 0) {
110         auto result = m_scanFuture.result();
111         m_scanFuture = Future();
112         return result;
113     }
114     m_scanFuture = Future();
115     return Result();
116 }
117 
reset()118 void TreeScanner::reset()
119 {
120     if (isFinished())
121         m_scanFuture = Future();
122 }
123 
isWellKnownBinary(const Utils::MimeType &,const Utils::FilePath & fn)124 bool TreeScanner::isWellKnownBinary(const Utils::MimeType & /*mdb*/, const Utils::FilePath &fn)
125 {
126     return fn.endsWith(QLatin1String(".a")) ||
127             fn.endsWith(QLatin1String(".o")) ||
128             fn.endsWith(QLatin1String(".d")) ||
129             fn.endsWith(QLatin1String(".exe")) ||
130             fn.endsWith(QLatin1String(".dll")) ||
131             fn.endsWith(QLatin1String(".obj")) ||
132             fn.endsWith(QLatin1String(".elf"));
133 }
134 
isMimeBinary(const Utils::MimeType & mimeType,const Utils::FilePath &)135 bool TreeScanner::isMimeBinary(const Utils::MimeType &mimeType, const Utils::FilePath &/*fn*/)
136 {
137     bool isBinary = false;
138     if (mimeType.isValid()) {
139         QStringList mimes;
140         mimes << mimeType.name() << mimeType.allAncestors();
141         isBinary = !mimes.contains(QLatin1String("text/plain"));
142     }
143     return isBinary;
144 }
145 
genericFileType(const Utils::MimeType & mimeType,const Utils::FilePath &)146 FileType TreeScanner::genericFileType(const Utils::MimeType &mimeType, const Utils::FilePath &/*fn*/)
147 {
148     return Node::fileTypeForMimeType(mimeType);
149 }
150 
createFolderNode(const Utils::FilePath & directory,const QList<FileNode * > & allFiles)151 static std::unique_ptr<FolderNode> createFolderNode(const Utils::FilePath &directory,
152                                                     const QList<FileNode *> &allFiles)
153 {
154     auto fileSystemNode = std::make_unique<FolderNode>(directory);
155     for (const FileNode *fn : allFiles) {
156         if (!fn->filePath().isChildOf(directory))
157             continue;
158 
159         std::unique_ptr<FileNode> node(fn->clone());
160         fileSystemNode->addNestedNode(std::move(node));
161     }
162     ProjectTree::applyTreeManager(fileSystemNode.get()); // QRC nodes
163     return fileSystemNode;
164 }
165 
scanForFiles(FutureInterface & fi,const Utils::FilePath & directory,const FileFilter & filter,const FileTypeFactory & factory)166 void TreeScanner::scanForFiles(FutureInterface &fi, const Utils::FilePath& directory,
167                                const FileFilter &filter, const FileTypeFactory &factory)
168 {
169     QList<FileNode *> nodes = ProjectExplorer::scanForFiles(fi, directory,
170                            [&filter, &factory](const Utils::FilePath &fn) -> FileNode * {
171         const Utils::MimeType mimeType = Utils::mimeTypeForFile(fn);
172 
173         // Skip some files during scan.
174         if (filter && filter(mimeType, fn))
175             return nullptr;
176 
177         // Type detection
178         FileType type = FileType::Unknown;
179         if (factory)
180             type = factory(mimeType, fn);
181 
182         return new FileNode(fn, type);
183     });
184 
185     Utils::sort(nodes, ProjectExplorer::Node::sortByPath);
186 
187     fi.setProgressValue(fi.progressMaximum());
188     Result result{createFolderNode(directory, nodes), nodes};
189 
190     fi.reportResult(result);
191 }
192 
193 } // namespace ProjectExplorer
194