1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
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 "symbolsfindfilter.h"
27 #include "cppmodelmanager.h"
28 #include "cpptoolsconstants.h"
29 
30 #include <coreplugin/icore.h>
31 #include <coreplugin/progressmanager/futureprogress.h>
32 #include <coreplugin/progressmanager/progressmanager.h>
33 #include <coreplugin/editormanager/editormanager.h>
34 #include <coreplugin/find/searchresultwindow.h>
35 #include <projectexplorer/project.h>
36 #include <projectexplorer/projectexplorer.h>
37 #include <projectexplorer/session.h>
38 
39 #include <utils/algorithm.h>
40 #include <utils/runextensions.h>
41 #include <utils/qtcassert.h>
42 
43 #include <QSet>
44 #include <QGridLayout>
45 #include <QLabel>
46 #include <QButtonGroup>
47 
48 using namespace Core;
49 using namespace Utils;
50 
51 namespace CppTools {
52 namespace Internal {
53 
54 const char SETTINGS_GROUP[] = "CppSymbols";
55 const char SETTINGS_SYMBOLTYPES[] = "SymbolsToSearchFor";
56 const char SETTINGS_SEARCHSCOPE[] = "SearchScope";
57 
SymbolsFindFilter(CppModelManager * manager)58 SymbolsFindFilter::SymbolsFindFilter(CppModelManager *manager)
59     : m_manager(manager),
60       m_enabled(true),
61       m_symbolsToSearch(SearchSymbols::AllTypes),
62       m_scope(SymbolSearcher::SearchProjectsOnly)
63 {
64     // for disabling while parser is running
65     connect(ProgressManager::instance(), &ProgressManager::taskStarted,
66             this, &SymbolsFindFilter::onTaskStarted);
67     connect(ProgressManager::instance(), &ProgressManager::allTasksFinished,
68             this, &SymbolsFindFilter::onAllTasksFinished);
69 }
70 
id() const71 QString SymbolsFindFilter::id() const
72 {
73     return QLatin1String(Constants::SYMBOLS_FIND_FILTER_ID);
74 }
75 
displayName() const76 QString SymbolsFindFilter::displayName() const
77 {
78     return QString(Constants::SYMBOLS_FIND_FILTER_DISPLAY_NAME);
79 }
80 
isEnabled() const81 bool SymbolsFindFilter::isEnabled() const
82 {
83     return m_enabled;
84 }
85 
cancel()86 void SymbolsFindFilter::cancel()
87 {
88     auto search = qobject_cast<SearchResult *>(sender());
89     QTC_ASSERT(search, return);
90     QFutureWatcher<SearchResultItem> *watcher = m_watchers.key(search);
91     QTC_ASSERT(watcher, return);
92     watcher->cancel();
93 }
94 
setPaused(bool paused)95 void SymbolsFindFilter::setPaused(bool paused)
96 {
97     auto search = qobject_cast<SearchResult *>(sender());
98     QTC_ASSERT(search, return);
99     QFutureWatcher<SearchResultItem> *watcher = m_watchers.key(search);
100     QTC_ASSERT(watcher, return);
101     if (!paused || watcher->isRunning()) // guard against pausing when the search is finished
102         watcher->setPaused(paused);
103 }
104 
findAll(const QString & txt,FindFlags findFlags)105 void SymbolsFindFilter::findAll(const QString &txt, FindFlags findFlags)
106 {
107     SearchResultWindow *window = SearchResultWindow::instance();
108     SearchResult *search = window->startNewSearch(label(), toolTip(findFlags), txt);
109     search->setSearchAgainSupported(true);
110     connect(search, &SearchResult::activated,
111             this, &SymbolsFindFilter::openEditor);
112     connect(search, &SearchResult::cancelled, this, &SymbolsFindFilter::cancel);
113     connect(search, &SearchResult::paused, this, &SymbolsFindFilter::setPaused);
114     connect(search, &SearchResult::searchAgainRequested, this, &SymbolsFindFilter::searchAgain);
115     connect(this, &IFindFilter::enabledChanged, search, &SearchResult::setSearchAgainEnabled);
116     window->popup(IOutputPane::ModeSwitch | IOutputPane::WithFocus);
117 
118     SymbolSearcher::Parameters parameters;
119     parameters.text = txt;
120     parameters.flags = findFlags;
121     parameters.types = m_symbolsToSearch;
122     parameters.scope = m_scope;
123     search->setUserData(QVariant::fromValue(parameters));
124     startSearch(search);
125 }
126 
startSearch(SearchResult * search)127 void SymbolsFindFilter::startSearch(SearchResult *search)
128 {
129     SymbolSearcher::Parameters parameters = search->userData().value<SymbolSearcher::Parameters>();
130     QSet<QString> projectFileNames;
131     if (parameters.scope == SymbolSearcher::SearchProjectsOnly) {
132         for (ProjectExplorer::Project *project : ProjectExplorer::SessionManager::projects())
133             projectFileNames += Utils::transform<QSet>(project->files(ProjectExplorer::Project::AllFiles), &Utils::FilePath::toString);
134     }
135 
136     auto watcher = new QFutureWatcher<SearchResultItem>;
137     m_watchers.insert(watcher, search);
138     connect(watcher, &QFutureWatcherBase::finished,
139             this, &SymbolsFindFilter::finish);
140     connect(watcher, &QFutureWatcherBase::resultsReadyAt,
141             this, &SymbolsFindFilter::addResults);
142     SymbolSearcher *symbolSearcher = m_manager->indexingSupport()->createSymbolSearcher(parameters, projectFileNames);
143     connect(watcher, &QFutureWatcherBase::finished,
144             symbolSearcher, &QObject::deleteLater);
145     watcher->setFuture(Utils::runAsync(m_manager->sharedThreadPool(),
146                                        &SymbolSearcher::runSearch, symbolSearcher));
147     FutureProgress *progress = ProgressManager::addTask(watcher->future(), tr("Searching for Symbol"),
148                                                         Core::Constants::TASK_SEARCH);
149     connect(progress, &FutureProgress::clicked, search, &SearchResult::popup);
150 }
151 
addResults(int begin,int end)152 void SymbolsFindFilter::addResults(int begin, int end)
153 {
154     auto watcher = static_cast<QFutureWatcher<SearchResultItem> *>(sender());
155     SearchResult *search = m_watchers.value(watcher);
156     if (!search) {
157         // search was removed from search history while the search is running
158         watcher->cancel();
159         return;
160     }
161     QList<SearchResultItem> items;
162     for (int i = begin; i < end; ++i)
163         items << watcher->resultAt(i);
164     search->addResults(items, SearchResult::AddSorted);
165 }
166 
finish()167 void SymbolsFindFilter::finish()
168 {
169     auto watcher = static_cast<QFutureWatcher<SearchResultItem> *>(sender());
170     SearchResult *search = m_watchers.value(watcher);
171     if (search)
172         search->finishSearch(watcher->isCanceled());
173     m_watchers.remove(watcher);
174     watcher->deleteLater();
175 }
176 
openEditor(const SearchResultItem & item)177 void SymbolsFindFilter::openEditor(const SearchResultItem &item)
178 {
179     if (!item.userData().canConvert<IndexItem::Ptr>())
180         return;
181     IndexItem::Ptr info = item.userData().value<IndexItem::Ptr>();
182     EditorManager::openEditorAt(info->fileName(), info->line(), info->column());
183 }
184 
createConfigWidget()185 QWidget *SymbolsFindFilter::createConfigWidget()
186 {
187     return new SymbolsFindFilterConfigWidget(this);
188 }
189 
writeSettings(QSettings * settings)190 void SymbolsFindFilter::writeSettings(QSettings *settings)
191 {
192     settings->beginGroup(QLatin1String(SETTINGS_GROUP));
193     settings->setValue(QLatin1String(SETTINGS_SYMBOLTYPES), int(m_symbolsToSearch));
194     settings->setValue(QLatin1String(SETTINGS_SEARCHSCOPE), int(m_scope));
195     settings->endGroup();
196 }
197 
readSettings(QSettings * settings)198 void SymbolsFindFilter::readSettings(QSettings *settings)
199 {
200     settings->beginGroup(QLatin1String(SETTINGS_GROUP));
201     m_symbolsToSearch = static_cast<SearchSymbols::SymbolTypes>(
202                 settings->value(QLatin1String(SETTINGS_SYMBOLTYPES),
203                                 int(SearchSymbols::AllTypes)).toInt());
204     m_scope = static_cast<SearchScope>(
205                 settings->value(QLatin1String(SETTINGS_SEARCHSCOPE),
206                                 int(SymbolSearcher::SearchProjectsOnly)).toInt());
207     settings->endGroup();
208     emit symbolsToSearchChanged();
209 }
210 
onTaskStarted(Id type)211 void SymbolsFindFilter::onTaskStarted(Id type)
212 {
213     if (type == CppTools::Constants::TASK_INDEX) {
214         m_enabled = false;
215         emit enabledChanged(m_enabled);
216     }
217 }
218 
onAllTasksFinished(Id type)219 void SymbolsFindFilter::onAllTasksFinished(Id type)
220 {
221     if (type == CppTools::Constants::TASK_INDEX) {
222         m_enabled = true;
223         emit enabledChanged(m_enabled);
224     }
225 }
226 
searchAgain()227 void SymbolsFindFilter::searchAgain()
228 {
229     auto search = qobject_cast<SearchResult *>(sender());
230     QTC_ASSERT(search, return);
231     search->restart();
232     startSearch(search);
233 }
234 
label() const235 QString SymbolsFindFilter::label() const
236 {
237     return tr("C++ Symbols:");
238 }
239 
toolTip(FindFlags findFlags) const240 QString SymbolsFindFilter::toolTip(FindFlags findFlags) const
241 {
242     QStringList types;
243     if (m_symbolsToSearch & SymbolSearcher::Classes)
244         types.append(tr("Classes"));
245     if (m_symbolsToSearch & SymbolSearcher::Functions)
246         types.append(tr("Functions"));
247     if (m_symbolsToSearch & SymbolSearcher::Enums)
248         types.append(tr("Enums"));
249     if (m_symbolsToSearch & SymbolSearcher::Declarations)
250         types.append(tr("Declarations"));
251     return tr("Scope: %1\nTypes: %2\nFlags: %3")
252         .arg(searchScope() == SymbolSearcher::SearchGlobal ? tr("All") : tr("Projects"),
253              types.join(", "),
254              IFindFilter::descriptionForFindFlags(findFlags));
255 }
256 
257 // #pragma mark -- SymbolsFindFilterConfigWidget
258 
SymbolsFindFilterConfigWidget(SymbolsFindFilter * filter)259 SymbolsFindFilterConfigWidget::SymbolsFindFilterConfigWidget(SymbolsFindFilter *filter)
260     : m_filter(filter)
261 {
262     connect(m_filter, &SymbolsFindFilter::symbolsToSearchChanged,
263             this, &SymbolsFindFilterConfigWidget::getState);
264 
265     auto layout = new QGridLayout(this);
266     setLayout(layout);
267     layout->setContentsMargins(0, 0, 0, 0);
268 
269     auto typeLabel = new QLabel(tr("Types:"));
270     layout->addWidget(typeLabel, 0, 0);
271 
272     m_typeClasses = new QCheckBox(tr("Classes"));
273     layout->addWidget(m_typeClasses, 0, 1);
274 
275     m_typeMethods = new QCheckBox(tr("Functions"));
276     layout->addWidget(m_typeMethods, 0, 2);
277 
278     m_typeEnums = new QCheckBox(tr("Enums"));
279     layout->addWidget(m_typeEnums, 1, 1);
280 
281     m_typeDeclarations = new QCheckBox(tr("Declarations"));
282     layout->addWidget(m_typeDeclarations, 1, 2);
283 
284     // hacks to fix layouting:
285     typeLabel->setMinimumWidth(80);
286     typeLabel->setAlignment(Qt::AlignRight);
287     m_typeClasses->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
288     m_typeMethods->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
289 
290     connect(m_typeClasses, &QAbstractButton::clicked,
291             this, &SymbolsFindFilterConfigWidget::setState);
292     connect(m_typeMethods, &QAbstractButton::clicked,
293             this, &SymbolsFindFilterConfigWidget::setState);
294     connect(m_typeEnums, &QAbstractButton::clicked,
295             this, &SymbolsFindFilterConfigWidget::setState);
296     connect(m_typeDeclarations, &QAbstractButton::clicked,
297             this, &SymbolsFindFilterConfigWidget::setState);
298 
299     m_searchProjectsOnly = new QRadioButton(tr("Projects only"));
300     layout->addWidget(m_searchProjectsOnly, 2, 1);
301 
302     m_searchGlobal = new QRadioButton(tr("All files"));
303     layout->addWidget(m_searchGlobal, 2, 2);
304 
305     m_searchGroup = new QButtonGroup(this);
306     m_searchGroup->addButton(m_searchProjectsOnly);
307     m_searchGroup->addButton(m_searchGlobal);
308 
309     connect(m_searchProjectsOnly, &QAbstractButton::clicked,
310             this, &SymbolsFindFilterConfigWidget::setState);
311     connect(m_searchGlobal, &QAbstractButton::clicked,
312             this, &SymbolsFindFilterConfigWidget::setState);
313 }
314 
getState()315 void SymbolsFindFilterConfigWidget::getState()
316 {
317     SearchSymbols::SymbolTypes symbols = m_filter->symbolsToSearch();
318     m_typeClasses->setChecked(symbols & SymbolSearcher::Classes);
319     m_typeMethods->setChecked(symbols & SymbolSearcher::Functions);
320     m_typeEnums->setChecked(symbols & SymbolSearcher::Enums);
321     m_typeDeclarations->setChecked(symbols & SymbolSearcher::Declarations);
322 
323     SymbolsFindFilter::SearchScope scope = m_filter->searchScope();
324     m_searchProjectsOnly->setChecked(scope == SymbolSearcher::SearchProjectsOnly);
325     m_searchGlobal->setChecked(scope == SymbolSearcher::SearchGlobal);
326 }
327 
setState() const328 void SymbolsFindFilterConfigWidget::setState() const
329 {
330     SearchSymbols::SymbolTypes symbols;
331     if (m_typeClasses->isChecked())
332         symbols |= SymbolSearcher::Classes;
333     if (m_typeMethods->isChecked())
334         symbols |= SymbolSearcher::Functions;
335     if (m_typeEnums->isChecked())
336         symbols |= SymbolSearcher::Enums;
337     if (m_typeDeclarations->isChecked())
338         symbols |= SymbolSearcher::Declarations;
339     m_filter->setSymbolsToSearch(symbols);
340 
341     if (m_searchProjectsOnly->isChecked())
342         m_filter->setSearchScope(SymbolSearcher::SearchProjectsOnly);
343     else
344         m_filter->setSearchScope(SymbolSearcher::SearchGlobal);
345 }
346 
347 } // namespace Internal
348 } // namespace CppTools
349