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