1 /****************************************************************************
2 **
3 ** Copyright (C) 2017 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 "clangautomationutils.h"
27
28 #include "../clangcompletionassistinterface.h"
29 #include "../clangcompletionassistprovider.h"
30
31 #include <texteditor/codeassist/assistinterface.h>
32 #include <texteditor/codeassist/assistproposalitem.h>
33 #include <texteditor/codeassist/completionassistprovider.h>
34 #include <texteditor/codeassist/genericproposalmodel.h>
35 #include <texteditor/codeassist/iassistprocessor.h>
36 #include <texteditor/codeassist/iassistproposal.h>
37 #include <texteditor/textdocument.h>
38 #include <texteditor/texteditor.h>
39
40 #include <utils/qtcassert.h>
41
42 #include <QCoreApplication>
43 #include <QElapsedTimer>
44 #include <QScopedPointer>
45
46 namespace ClangCodeModel {
47 namespace Internal {
48
49 class WaitForAsyncCompletions
50 {
51 public:
52 enum WaitResult { GotResults, GotInvalidResults, Timeout };
53
wait(TextEditor::IAssistProcessor * processor,TextEditor::AssistInterface * assistInterface,int timeoutInMs)54 WaitResult wait(TextEditor::IAssistProcessor *processor,
55 TextEditor::AssistInterface *assistInterface,
56 int timeoutInMs)
57 {
58 QTC_ASSERT(processor, return Timeout);
59 QTC_ASSERT(assistInterface, return Timeout);
60
61 bool gotResults = false;
62
63 processor->setAsyncCompletionAvailableHandler(
64 [this, processor, &gotResults] (TextEditor::IAssistProposal *proposal) {
65 QTC_ASSERT(proposal, return);
66 QTC_CHECK(!processor->running());
67 proposalModel = proposal->model();
68 delete proposal;
69 gotResults = true;
70 });
71
72 // Are there any immediate results?
73 if (TextEditor::IAssistProposal *proposal = processor->perform(assistInterface)) {
74 proposalModel = proposal->model();
75 delete proposal;
76 QTC_ASSERT(proposalModel, return GotInvalidResults);
77 return GotResults;
78 }
79
80 // There are not any, so wait for async results.
81 QElapsedTimer timer;
82 timer.start();
83 while (!gotResults) {
84 if (timer.elapsed() >= timeoutInMs) {
85 processor->cancel();
86 return Timeout;
87 }
88 QCoreApplication::processEvents();
89 }
90
91 return proposalModel ? GotResults : GotInvalidResults;
92 }
93
94 public:
95 TextEditor::ProposalModelPtr proposalModel;
96 };
97
toHeaderPaths(const QStringList & paths)98 static const ProjectExplorer::HeaderPaths toHeaderPaths(const QStringList &paths)
99 {
100 ProjectExplorer::HeaderPaths result;
101 foreach (const QString &path, paths)
102 result.push_back({path, ProjectExplorer::HeaderPathType::User});
103 return result;
104 }
105
completionResults(TextEditor::BaseTextEditor * textEditor,const QStringList & includePaths,int timeOutInMs)106 TextEditor::ProposalModelPtr completionResults(TextEditor::BaseTextEditor *textEditor,
107 const QStringList &includePaths,
108 int timeOutInMs)
109 {
110 using namespace TextEditor;
111
112 auto textEditorWidget = TextEditorWidget::fromEditor(textEditor);
113 QTC_ASSERT(textEditorWidget, return TextEditor::ProposalModelPtr());
114 AssistInterface *assistInterface = textEditorWidget->createAssistInterface(
115 TextEditor::Completion, TextEditor::ExplicitlyInvoked);
116 QTC_ASSERT(assistInterface, return TextEditor::ProposalModelPtr());
117 if (!includePaths.isEmpty()) {
118 auto clangAssistInterface = static_cast<ClangCompletionAssistInterface *>(assistInterface);
119 clangAssistInterface->setHeaderPaths(toHeaderPaths(includePaths));
120 }
121
122 CompletionAssistProvider *assistProvider
123 = textEditor->textDocument()->completionAssistProvider();
124 QTC_ASSERT(qobject_cast<ClangCompletionAssistProvider *>(assistProvider),
125 return TextEditor::ProposalModelPtr());
126 QTC_ASSERT(assistProvider, return TextEditor::ProposalModelPtr());
127 QTC_ASSERT(assistProvider->runType() == IAssistProvider::Asynchronous,
128 return TextEditor::ProposalModelPtr());
129
130 QScopedPointer<IAssistProcessor> processor(assistProvider->createProcessor());
131 QTC_ASSERT(processor, return TextEditor::ProposalModelPtr());
132
133 WaitForAsyncCompletions waitForCompletions;
134 const WaitForAsyncCompletions::WaitResult result = waitForCompletions.wait(processor.data(),
135 assistInterface,
136 timeOutInMs);
137 QTC_ASSERT(result == WaitForAsyncCompletions::GotResults,
138 return TextEditor::ProposalModelPtr());
139 return waitForCompletions.proposalModel;
140 }
141
qrcPath(const QByteArray & relativeFilePath)142 QString qrcPath(const QByteArray &relativeFilePath)
143 {
144 return QLatin1String(":/unittests/ClangCodeModel/") + QString::fromUtf8(relativeFilePath);
145 }
146
147 } // namespace Internal
148 } // namespace ClangCodeModel
149