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 "clangjobrequest.h"
27 
28 #include "clangcompletecodejob.h"
29 #include "clangfollowsymboljob.h"
30 #include "clangparsesupportivetranslationunitjob.h"
31 #include "clangrequestannotationsjob.h"
32 #include "clangrequestreferencesjob.h"
33 #include "clangrequesttooltipjob.h"
34 #include "clangresumedocumentjob.h"
35 #include "clangsuspenddocumentjob.h"
36 #include "clangupdateannotationsjob.h"
37 #include "clangupdateextraannotationsjob.h"
38 
39 #include <clangsupport/clangcodemodelclientinterface.h>
40 #include <clangsupport/completionsmessage.h>
41 #include <clangsupport/followsymbolmessage.h>
42 #include <clangsupport/referencesmessage.h>
43 #include <clangsupport/tooltipmessage.h>
44 
45 #include <utils/qtcassert.h>
46 
47 #include <QFileInfo>
48 
49 #include <ostream>
50 
51 namespace ClangBackEnd {
52 
53 #define RETURN_TEXT_FOR_CASE(enumValue) case JobRequest::Type::enumValue: return #enumValue
JobRequestTypeToText(JobRequest::Type type)54 static const char *JobRequestTypeToText(JobRequest::Type type)
55 {
56     switch (type) {
57         RETURN_TEXT_FOR_CASE(Invalid);
58         RETURN_TEXT_FOR_CASE(UpdateAnnotations);
59         RETURN_TEXT_FOR_CASE(UpdateExtraAnnotations);
60         RETURN_TEXT_FOR_CASE(ParseSupportiveTranslationUnit);
61         RETURN_TEXT_FOR_CASE(RequestCompletions);
62         RETURN_TEXT_FOR_CASE(RequestAnnotations);
63         RETURN_TEXT_FOR_CASE(RequestReferences);
64         RETURN_TEXT_FOR_CASE(RequestFollowSymbol);
65         RETURN_TEXT_FOR_CASE(RequestToolTip);
66         RETURN_TEXT_FOR_CASE(SuspendDocument);
67         RETURN_TEXT_FOR_CASE(ResumeDocument);
68     }
69 
70     return "UnhandledJobRequestType";
71 }
72 #undef RETURN_TEXT_FOR_CASE
73 
74 #define RETURN_TEXT_FOR_CASE(enumValue) case PreferredTranslationUnit::enumValue: return #enumValue
preferredTranslationUnitToText(PreferredTranslationUnit type)75 const char *preferredTranslationUnitToText(PreferredTranslationUnit type)
76 {
77     switch (type) {
78         RETURN_TEXT_FOR_CASE(RecentlyParsed);
79         RETURN_TEXT_FOR_CASE(PreviouslyParsed);
80         RETURN_TEXT_FOR_CASE(LastUninitialized);
81     }
82 
83     return "UnhandledPreferredTranslationUnitType";
84 }
85 #undef RETURN_TEXT_FOR_CASE
86 
operator <<(QDebug debug,JobRequest::Type type)87 QDebug operator<<(QDebug debug, JobRequest::Type type)
88 {
89     debug << JobRequestTypeToText(type);
90 
91     return debug;
92 }
93 
operator <<(std::ostream & os,JobRequest::Type type)94 std::ostream &operator<<(std::ostream &os, JobRequest::Type type)
95 {
96     return os << JobRequestTypeToText(type);
97 }
98 
operator <<(std::ostream & os,PreferredTranslationUnit preferredTranslationUnit)99 std::ostream &operator<<(std::ostream &os, PreferredTranslationUnit preferredTranslationUnit)
100 {
101     return os << preferredTranslationUnitToText(preferredTranslationUnit);
102 }
103 
operator <<(QDebug debug,const JobRequest & jobRequest)104 QDebug operator<<(QDebug debug, const JobRequest &jobRequest)
105 {
106     debug.nospace() << "Job<"
107                     << jobRequest.id
108                     << ","
109                     << QFileInfo(jobRequest.filePath).fileName()
110                     << ","
111                     << JobRequestTypeToText(jobRequest.type)
112                     << ","
113                     << preferredTranslationUnitToText(jobRequest.preferredTranslationUnit)
114                     << ">";
115 
116     return debug.space();
117 }
118 
expirationConditionsForType(JobRequest::Type type)119 static JobRequest::ExpirationConditions expirationConditionsForType(JobRequest::Type type)
120 {
121     using Type = JobRequest::Type;
122     using Condition = JobRequest::ExpirationCondition;
123     using Conditions = JobRequest::ExpirationConditions;
124 
125     switch (type) {
126     case Type::UpdateAnnotations:
127     case Type::UpdateExtraAnnotations:
128         return Conditions(Condition::AnythingChanged);
129     case Type::RequestReferences:
130     case Type::RequestAnnotations:
131     case Type::RequestToolTip:
132     case Type::RequestFollowSymbol:
133         return Conditions(Condition::DocumentClosed)
134              | Conditions(Condition::DocumentRevisionChanged);
135     default:
136         return Condition::DocumentClosed;
137     }
138 }
139 
conditionsForType(JobRequest::Type type)140 static JobRequest::RunConditions conditionsForType(JobRequest::Type type)
141 {
142     using Type = JobRequest::Type;
143     using Condition = JobRequest::RunCondition;
144     using Conditions = JobRequest::RunConditions;
145 
146     if (type == Type::SuspendDocument) {
147         return Conditions(Condition::DocumentUnsuspended)
148              | Conditions(Condition::DocumentNotVisible);
149     }
150 
151     if (type == Type::ResumeDocument) {
152         return Conditions(Condition::DocumentSuspended)
153              | Conditions(Condition::DocumentVisible);
154     }
155 
156     Conditions conditions = Conditions(Condition::DocumentUnsuspended)
157                           | Conditions(Condition::DocumentVisible);
158 
159     if (type == Type::RequestReferences || type == Type::RequestFollowSymbol
160         || type == Type::RequestToolTip || type == Type::UpdateExtraAnnotations) {
161         conditions |= Condition::CurrentDocumentRevision;
162     }
163 
164     if (type != Type::UpdateAnnotations && type != Type::ParseSupportiveTranslationUnit)
165         conditions |= Condition::DocumentParsed;
166 
167     return conditions;
168 }
169 
isTakeOverable() const170 bool JobRequest::isTakeOverable() const
171 {
172     // When new project information comes in and there are unprocessed jobs
173     // in the queue, we need to decide what to do with them.
174 
175     switch (type) {
176     // Never discard these as the client side might wait for a response.
177     case Type::RequestCompletions:
178     case Type::RequestReferences:
179     case Type::RequestFollowSymbol:
180     case Type::RequestToolTip:
181         return true;
182 
183     // Discard this one as UpdateAnnotations will have the same effect.
184     case Type::RequestAnnotations:
185 
186     // Discard Suspend because the document will be cleared anyway.
187     // Discard Resume because a (re)parse will happen on demand.
188     case Type::SuspendDocument:
189     case Type::ResumeDocument:
190 
191     // Discard these as they are initial jobs that will be recreated on demand
192     // anyway.
193     case Type::UpdateAnnotations:
194     case Type::UpdateExtraAnnotations:
195 
196     // Discard these as they only make sense in a row. Avoid splitting them up.
197     case Type::ParseSupportiveTranslationUnit:
198 
199     case Type::Invalid:
200         return false;
201     }
202 
203     return false;
204 }
205 
JobRequest(Type type)206 JobRequest::JobRequest(Type type)
207 {
208     static quint64 idCounter = 0;
209 
210     id = ++idCounter;
211     this->type = type;
212     runConditions = conditionsForType(type);
213     expirationConditions = expirationConditionsForType(type);
214 }
215 
createJob() const216 IAsyncJob *JobRequest::createJob() const
217 {
218     switch (type) {
219     case JobRequest::Type::Invalid:
220         QTC_CHECK(false && "Cannot create job for invalid job request.");
221         break;
222     case JobRequest::Type::UpdateAnnotations:
223         return new UpdateAnnotationsJob();
224     case JobRequest::Type::UpdateExtraAnnotations:
225         return new UpdateExtraAnnotationsJob();
226     case JobRequest::Type::ParseSupportiveTranslationUnit:
227         return new ParseSupportiveTranslationUnitJob();
228     case JobRequest::Type::RequestCompletions:
229         return new CompleteCodeJob();
230     case JobRequest::Type::RequestAnnotations:
231         return new RequestAnnotationsJob();
232     case JobRequest::Type::RequestReferences:
233         return new RequestReferencesJob();
234     case JobRequest::Type::RequestToolTip:
235          return new RequestToolTipJob();
236     case JobRequest::Type::RequestFollowSymbol:
237         return new FollowSymbolJob();
238     case JobRequest::Type::SuspendDocument:
239         return new SuspendDocumentJob();
240     case JobRequest::Type::ResumeDocument:
241         return new ResumeDocumentJob();
242     }
243 
244     return nullptr;
245 }
246 
cancelJob(ClangCodeModelClientInterface & client) const247 void JobRequest::cancelJob(ClangCodeModelClientInterface &client) const
248 {
249     // If a job request with a ticket number is cancelled, the plugin side
250     // must get back some results in order to clean up the state there.
251 
252     switch (type) {
253     case JobRequest::Type::Invalid:
254     case JobRequest::Type::UpdateAnnotations:
255     case JobRequest::Type::UpdateExtraAnnotations:
256     case JobRequest::Type::ParseSupportiveTranslationUnit:
257     case JobRequest::Type::RequestAnnotations:
258     case JobRequest::Type::SuspendDocument:
259     case JobRequest::Type::ResumeDocument:
260         break;
261     case JobRequest::Type::RequestReferences:
262         client.references(ReferencesMessage(FileContainer(),
263                                             QVector<SourceRangeContainer>(),
264                                             false,
265                                             ticketNumber));
266         break;
267     case JobRequest::Type::RequestToolTip:
268         client.tooltip(ToolTipMessage(FileContainer(), ToolTipInfo(), ticketNumber));
269         break;
270     case JobRequest::Type::RequestCompletions:
271         client.completions(CompletionsMessage(CodeCompletions(), ticketNumber));
272         break;
273     case JobRequest::Type::RequestFollowSymbol:
274         client.followSymbol(
275             FollowSymbolMessage(FileContainer(), SourceRangeContainer(), ticketNumber));
276         break;
277     }
278 }
279 
operator ==(const JobRequest & other) const280 bool JobRequest::operator==(const JobRequest &other) const
281 {
282     return type == other.type
283         && expirationConditions == other.expirationConditions
284         && runConditions == other.runConditions
285 
286         && filePath == other.filePath
287         && unsavedFilesChangeTimePoint == other.unsavedFilesChangeTimePoint
288         && documentRevision == other.documentRevision
289         && preferredTranslationUnit == other.preferredTranslationUnit
290 
291         && line == other.line
292         && column == other.column
293         && ticketNumber == other.ticketNumber;
294 
295         // Additional members that are not compared here explicitly are
296         // supposed to depend on the already compared ones.
297 }
298 
299 } // namespace ClangBackEnd
300