|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the Qt Mobility Components. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include <cplusplus/pp.h> |
|
43 #include <cplusplus/CppBindings.h> |
|
44 #include <cplusplus/Overview.h> |
|
45 #include <cplusplus/CheckUndefinedSymbols.h> |
|
46 |
|
47 #include "cppmodelmanager.h" |
|
48 #ifndef ICHECK_BUILD |
|
49 # include "cpptoolsconstants.h" |
|
50 # include "cpptoolseditorsupport.h" |
|
51 # include "cppfindreferences.h" |
|
52 #endif |
|
53 |
|
54 #include <functional> |
|
55 #include <QtConcurrentRun> |
|
56 #ifndef ICHECK_BUILD |
|
57 # include <QFutureSynchronizer> |
|
58 # include <qtconcurrent/runextensions.h> |
|
59 # include <texteditor/itexteditor.h> |
|
60 # include <texteditor/basetexteditor.h> |
|
61 # include <projectexplorer/project.h> |
|
62 # include <projectexplorer/projectexplorer.h> |
|
63 # include <projectexplorer/projectexplorerconstants.h> |
|
64 # include <projectexplorer/session.h> |
|
65 # include <coreplugin/icore.h> |
|
66 # include <coreplugin/uniqueidmanager.h> |
|
67 # include <coreplugin/mimedatabase.h> |
|
68 # include <coreplugin/editormanager/editormanager.h> |
|
69 # include <coreplugin/progressmanager/progressmanager.h> |
|
70 # include <extensionsystem/pluginmanager.h> |
|
71 #else |
|
72 # include <QDir> |
|
73 #endif |
|
74 |
|
75 #include <utils/qtcassert.h> |
|
76 |
|
77 #include <TranslationUnit.h> |
|
78 #include <Semantic.h> |
|
79 #include <AST.h> |
|
80 #include <Scope.h> |
|
81 #include <Literals.h> |
|
82 #include <Symbols.h> |
|
83 #include <Names.h> |
|
84 #include <NameVisitor.h> |
|
85 #include <TypeVisitor.h> |
|
86 #include <ASTVisitor.h> |
|
87 #include <Lexer.h> |
|
88 #include <Token.h> |
|
89 |
|
90 #include <cplusplus/LookupContext.h> |
|
91 |
|
92 #include <QtCore/QCoreApplication> |
|
93 #include <QtCore/QDebug> |
|
94 #include <QtCore/QMutexLocker> |
|
95 #include <QtCore/QTime> |
|
96 #include <QtCore/QTimer> |
|
97 #include <QtConcurrentMap> |
|
98 #include <iostream> |
|
99 #include <sstream> |
|
100 |
|
101 using namespace CppTools; |
|
102 using namespace CppTools::Internal; |
|
103 using namespace CPlusPlus; |
|
104 |
|
105 #if defined(QTCREATOR_WITH_DUMP_AST) && defined(Q_CC_GNU) |
|
106 |
|
107 #include <cxxabi.h> |
|
108 |
|
109 class DumpAST: protected ASTVisitor |
|
110 { |
|
111 public: |
|
112 int depth; |
|
113 |
|
114 DumpAST(Control *control) |
|
115 : ASTVisitor(control), depth(0) |
|
116 { } |
|
117 |
|
118 void operator()(AST *ast) |
|
119 { accept(ast); } |
|
120 |
|
121 protected: |
|
122 virtual bool preVisit(AST *ast) |
|
123 { |
|
124 std::ostringstream s; |
|
125 PrettyPrinter pp(control(), s); |
|
126 pp(ast); |
|
127 QString code = QString::fromStdString(s.str()); |
|
128 code.replace('\n', ' '); |
|
129 code.replace(QRegExp("\\s+"), " "); |
|
130 |
|
131 const char *name = abi::__cxa_demangle(typeid(*ast).name(), 0, 0, 0) + 11; |
|
132 |
|
133 QByteArray ind(depth, ' '); |
|
134 ind += name; |
|
135 |
|
136 printf("%-40s %s\n", ind.constData(), qPrintable(code)); |
|
137 ++depth; |
|
138 return true; |
|
139 } |
|
140 |
|
141 virtual void postVisit(AST *) |
|
142 { --depth; } |
|
143 }; |
|
144 |
|
145 #endif // QTCREATOR_WITH_DUMP_AST |
|
146 |
|
147 static const char pp_configuration_file[] = "<configuration>"; |
|
148 |
|
149 static const char pp_configuration[] = |
|
150 "# 1 \"<configuration>\"\n" |
|
151 "#define __cplusplus 1\n" |
|
152 "#define __extension__\n" |
|
153 "#define __context__\n" |
|
154 "#define __range__\n" |
|
155 "#define restrict\n" |
|
156 "#define __restrict\n" |
|
157 "#define __restrict__\n" |
|
158 |
|
159 "#define __complex__\n" |
|
160 "#define __imag__\n" |
|
161 "#define __real__\n" |
|
162 |
|
163 "#define __builtin_va_arg(a,b) ((b)0)\n" |
|
164 |
|
165 // ### add macros for win32 |
|
166 "#define __cdecl\n" |
|
167 "#define __stdcall\n" |
|
168 "#define QT_WA(x) x\n" |
|
169 "#define API\n" |
|
170 "#define WINAPI\n" |
|
171 "#define CALLBACK\n" |
|
172 "#define STDMETHODCALLTYPE\n" |
|
173 "#define __RPC_FAR\n" |
|
174 "#define APIENTRY\n" |
|
175 "#define __declspec(a)\n" |
|
176 "#define STDMETHOD(method) virtual HRESULT STDMETHODCALLTYPE method\n"; |
|
177 |
|
178 #ifndef ICHECK_BUILD |
|
179 CppPreprocessor::CppPreprocessor(QPointer<CppModelManager> modelManager) |
|
180 : snapshot(modelManager->snapshot()), |
|
181 m_modelManager(modelManager), |
|
182 preprocess(this, &env), |
|
183 m_revision(0) |
|
184 { } |
|
185 |
|
186 #else |
|
187 |
|
188 CppPreprocessor::CppPreprocessor(QPointer<CPlusPlus::ParseManager> modelManager) |
|
189 : preprocess(this, &env), |
|
190 m_revision(0) |
|
191 { |
|
192 } |
|
193 #endif |
|
194 |
|
195 CppPreprocessor::~CppPreprocessor() |
|
196 { } |
|
197 |
|
198 void CppPreprocessor::setRevision(unsigned revision) |
|
199 { m_revision = revision; } |
|
200 |
|
201 void CppPreprocessor::setWorkingCopy(const CppTools::CppModelManagerInterface::WorkingCopy &workingCopy) |
|
202 { m_workingCopy = workingCopy; } |
|
203 |
|
204 void CppPreprocessor::setIncludePaths(const QStringList &includePaths) |
|
205 { m_includePaths = includePaths; } |
|
206 |
|
207 void CppPreprocessor::setFrameworkPaths(const QStringList &frameworkPaths) |
|
208 { m_frameworkPaths = frameworkPaths; } |
|
209 |
|
210 void CppPreprocessor::setProjectFiles(const QStringList &files) |
|
211 { m_projectFiles = files; } |
|
212 |
|
213 void CppPreprocessor::setTodo(const QStringList &files) |
|
214 { m_todo = QSet<QString>::fromList(files); } |
|
215 |
|
216 #ifndef ICHECK_BUILD |
|
217 namespace { |
|
218 class Process: public std::unary_function<Document::Ptr, void> |
|
219 { |
|
220 QPointer<CppModelManager> _modelManager; |
|
221 Snapshot _snapshot; |
|
222 CppModelManager::WorkingCopy _workingCopy; |
|
223 Document::Ptr _doc; |
|
224 |
|
225 public: |
|
226 Process(QPointer<CppModelManager> modelManager, |
|
227 Snapshot snapshot, |
|
228 const CppModelManager::WorkingCopy &workingCopy) |
|
229 : _modelManager(modelManager), |
|
230 _snapshot(snapshot), |
|
231 _workingCopy(workingCopy) |
|
232 { } |
|
233 |
|
234 LookupContext lookupContext(unsigned line, unsigned column) const |
|
235 { return lookupContext(_doc->findSymbolAt(line, column)); } |
|
236 |
|
237 LookupContext lookupContext(Symbol *symbol) const |
|
238 { |
|
239 LookupContext context(symbol, Document::create(QLatin1String("<none>")), _doc, _snapshot); |
|
240 return context; |
|
241 } |
|
242 |
|
243 void operator()(Document::Ptr doc) |
|
244 { |
|
245 _doc = doc; |
|
246 |
|
247 Document::CheckMode mode = Document::FastCheck; |
|
248 |
|
249 if (_workingCopy.contains(doc->fileName())) |
|
250 mode = Document::FullCheck; |
|
251 |
|
252 doc->parse(); |
|
253 doc->check(mode); |
|
254 |
|
255 if (mode == Document::FullCheck) { |
|
256 // run the binding pass |
|
257 NamespaceBindingPtr ns = bind(doc, _snapshot); |
|
258 |
|
259 // check for undefined symbols. |
|
260 CheckUndefinedSymbols checkUndefinedSymbols(doc); |
|
261 checkUndefinedSymbols.setGlobalNamespaceBinding(ns); |
|
262 |
|
263 checkUndefinedSymbols(doc->translationUnit()->ast()); // ### FIXME |
|
264 } |
|
265 |
|
266 doc->releaseTranslationUnit(); |
|
267 |
|
268 if (_modelManager) |
|
269 _modelManager->emitDocumentUpdated(doc); // ### TODO: compress |
|
270 } |
|
271 }; |
|
272 } // end of anonymous namespace |
|
273 #endif |
|
274 |
|
275 void CppPreprocessor::run(const QString &fileName) |
|
276 { |
|
277 QString absoluteFilePath = fileName; |
|
278 sourceNeeded(absoluteFilePath, IncludeGlobal, /*line = */ 0); |
|
279 } |
|
280 |
|
281 void CppPreprocessor::resetEnvironment() |
|
282 { |
|
283 env.reset(); |
|
284 m_processed.clear(); |
|
285 } |
|
286 |
|
287 bool CppPreprocessor::includeFile(const QString &absoluteFilePath, QString *result, unsigned *revision) |
|
288 { |
|
289 if (absoluteFilePath.isEmpty() || m_included.contains(absoluteFilePath)) |
|
290 return true; |
|
291 |
|
292 if (m_workingCopy.contains(absoluteFilePath)) { |
|
293 m_included.insert(absoluteFilePath); |
|
294 const QPair<QString, unsigned> r = m_workingCopy.get(absoluteFilePath); |
|
295 *result = r.first; |
|
296 *revision = r.second; |
|
297 return true; |
|
298 } |
|
299 |
|
300 QFileInfo fileInfo(absoluteFilePath); |
|
301 if (! fileInfo.isFile()) |
|
302 return false; |
|
303 |
|
304 QFile file(absoluteFilePath); |
|
305 if (file.open(QFile::ReadOnly)) { |
|
306 m_included.insert(absoluteFilePath); |
|
307 QTextStream stream(&file); |
|
308 const QString contents = stream.readAll(); |
|
309 *result = contents.toUtf8(); |
|
310 file.close(); |
|
311 return true; |
|
312 } |
|
313 |
|
314 return false; |
|
315 } |
|
316 |
|
317 QString CppPreprocessor::tryIncludeFile(QString &fileName, IncludeType type, unsigned *revision) |
|
318 { |
|
319 QFileInfo fileInfo(fileName); |
|
320 if (fileName == QLatin1String(pp_configuration_file) || fileInfo.isAbsolute()) { |
|
321 QString contents; |
|
322 includeFile(fileName, &contents, revision); |
|
323 return contents; |
|
324 } |
|
325 |
|
326 if (type == IncludeLocal && m_currentDoc) { |
|
327 QFileInfo currentFileInfo(m_currentDoc->fileName()); |
|
328 QString path = currentFileInfo.absolutePath(); |
|
329 path += QLatin1Char('/'); |
|
330 path += fileName; |
|
331 path = QDir::cleanPath(path); |
|
332 QString contents; |
|
333 if (includeFile(path, &contents, revision)) { |
|
334 fileName = path; |
|
335 return contents; |
|
336 } |
|
337 } |
|
338 |
|
339 foreach (const QString &includePath, m_includePaths) { |
|
340 QString path = includePath; |
|
341 path += QLatin1Char('/'); |
|
342 path += fileName; |
|
343 path = QDir::cleanPath(path); |
|
344 QString contents; |
|
345 if (includeFile(path, &contents, revision)) { |
|
346 fileName = path; |
|
347 return contents; |
|
348 } |
|
349 } |
|
350 |
|
351 // look in the system include paths |
|
352 foreach (const QString &includePath, m_systemIncludePaths) { |
|
353 QString path = includePath; |
|
354 path += QLatin1Char('/'); |
|
355 path += fileName; |
|
356 path = QDir::cleanPath(path); |
|
357 QString contents; |
|
358 if (includeFile(path, &contents, revision)) { |
|
359 fileName = path; |
|
360 return contents; |
|
361 } |
|
362 } |
|
363 |
|
364 int index = fileName.indexOf(QLatin1Char('/')); |
|
365 if (index != -1) { |
|
366 QString frameworkName = fileName.left(index); |
|
367 QString name = fileName.mid(index + 1); |
|
368 |
|
369 foreach (const QString &frameworkPath, m_frameworkPaths) { |
|
370 QString path = frameworkPath; |
|
371 path += QLatin1Char('/'); |
|
372 path += frameworkName; |
|
373 path += QLatin1String(".framework/Headers/"); |
|
374 path += name; |
|
375 path = QDir::cleanPath(path); |
|
376 QString contents; |
|
377 if (includeFile(path, &contents, revision)) { |
|
378 fileName = path; |
|
379 return contents; |
|
380 } |
|
381 } |
|
382 } |
|
383 |
|
384 QString path = fileName; |
|
385 if (path.at(0) != QLatin1Char('/')) |
|
386 path.prepend(QLatin1Char('/')); |
|
387 |
|
388 foreach (const QString &projectFile, m_projectFiles) { |
|
389 if (projectFile.endsWith(path)) { |
|
390 fileName = projectFile; |
|
391 QString contents; |
|
392 includeFile(fileName, &contents, revision); |
|
393 return contents; |
|
394 } |
|
395 } |
|
396 |
|
397 //qDebug() << "**** file" << fileName << "not found!"; |
|
398 return QString(); |
|
399 } |
|
400 |
|
401 void CppPreprocessor::macroAdded(const Macro ¯o) |
|
402 { |
|
403 if (! m_currentDoc) |
|
404 return; |
|
405 |
|
406 m_currentDoc->appendMacro(macro); |
|
407 } |
|
408 |
|
409 void CppPreprocessor::passedMacroDefinitionCheck(unsigned offset, const Macro ¯o) |
|
410 { |
|
411 if (! m_currentDoc) |
|
412 return; |
|
413 |
|
414 m_currentDoc->addMacroUse(macro, offset, macro.name().length(), env.currentLine, |
|
415 QVector<MacroArgumentReference>(), true); |
|
416 } |
|
417 |
|
418 void CppPreprocessor::failedMacroDefinitionCheck(unsigned offset, const QByteArray &name) |
|
419 { |
|
420 if (! m_currentDoc) |
|
421 return; |
|
422 |
|
423 m_currentDoc->addUndefinedMacroUse(name, offset); |
|
424 } |
|
425 |
|
426 void CppPreprocessor::startExpandingMacro(unsigned offset, |
|
427 const Macro ¯o, |
|
428 const QByteArray &originalText, |
|
429 bool inCondition, |
|
430 const QVector<MacroArgumentReference> &actuals) |
|
431 { |
|
432 if (! m_currentDoc) |
|
433 return; |
|
434 |
|
435 //qDebug() << "start expanding:" << macro.name() << "text:" << originalText; |
|
436 m_currentDoc->addMacroUse(macro, offset, originalText.length(), env.currentLine, |
|
437 actuals, inCondition); |
|
438 } |
|
439 |
|
440 void CppPreprocessor::stopExpandingMacro(unsigned, const Macro &) |
|
441 { |
|
442 if (! m_currentDoc) |
|
443 return; |
|
444 |
|
445 //qDebug() << "stop expanding:" << macro.name; |
|
446 } |
|
447 |
|
448 void CppPreprocessor::mergeEnvironment(Document::Ptr doc) |
|
449 { |
|
450 if (! doc) |
|
451 return; |
|
452 |
|
453 const QString fn = doc->fileName(); |
|
454 |
|
455 if (m_processed.contains(fn)) |
|
456 return; |
|
457 |
|
458 m_processed.insert(fn); |
|
459 |
|
460 foreach (const Document::Include &incl, doc->includes()) { |
|
461 QString includedFile = incl.fileName(); |
|
462 |
|
463 if (Document::Ptr includedDoc = snapshot.document(includedFile)) |
|
464 mergeEnvironment(includedDoc); |
|
465 else |
|
466 run(includedFile); |
|
467 } |
|
468 |
|
469 env.addMacros(doc->definedMacros()); |
|
470 } |
|
471 |
|
472 void CppPreprocessor::startSkippingBlocks(unsigned offset) |
|
473 { |
|
474 //qDebug() << "start skipping blocks:" << offset; |
|
475 if (m_currentDoc) |
|
476 m_currentDoc->startSkippingBlocks(offset); |
|
477 } |
|
478 |
|
479 void CppPreprocessor::stopSkippingBlocks(unsigned offset) |
|
480 { |
|
481 //qDebug() << "stop skipping blocks:" << offset; |
|
482 if (m_currentDoc) |
|
483 m_currentDoc->stopSkippingBlocks(offset); |
|
484 } |
|
485 |
|
486 void CppPreprocessor::sourceNeeded(QString &fileName, IncludeType type, unsigned line) |
|
487 { |
|
488 if (fileName.isEmpty()) |
|
489 return; |
|
490 |
|
491 unsigned editorRevision = 0; |
|
492 QString contents = tryIncludeFile(fileName, type, &editorRevision); |
|
493 fileName = QDir::cleanPath(fileName); |
|
494 if (m_currentDoc) { |
|
495 m_currentDoc->addIncludeFile(fileName, line); |
|
496 |
|
497 if (contents.isEmpty() && ! QFileInfo(fileName).isAbsolute()) { |
|
498 QString msg = QCoreApplication::translate( |
|
499 "CppPreprocessor", "%1: No such file or directory").arg(fileName); |
|
500 |
|
501 Document::DiagnosticMessage d(Document::DiagnosticMessage::Warning, |
|
502 m_currentDoc->fileName(), |
|
503 env.currentLine, /*column = */ 0, |
|
504 msg); |
|
505 |
|
506 m_currentDoc->addDiagnosticMessage(d); |
|
507 |
|
508 //qWarning() << "file not found:" << fileName << m_currentDoc->fileName() << env.current_line; |
|
509 } |
|
510 } |
|
511 |
|
512 //qDebug() << "parse file:" << fileName << "contents:" << contents.size(); |
|
513 |
|
514 Document::Ptr doc = snapshot.document(fileName); |
|
515 if (doc) { |
|
516 mergeEnvironment(doc); |
|
517 return; |
|
518 } |
|
519 |
|
520 doc = Document::create(fileName); |
|
521 doc->setRevision(m_revision); |
|
522 doc->setEditorRevision(editorRevision); |
|
523 |
|
524 QFileInfo info(fileName); |
|
525 if (info.exists()) |
|
526 doc->setLastModified(info.lastModified()); |
|
527 |
|
528 Document::Ptr previousDoc = switchDocument(doc); |
|
529 |
|
530 const QByteArray preprocessedCode = preprocess(fileName, contents); |
|
531 |
|
532 doc->setSource(preprocessedCode); |
|
533 doc->tokenize(); |
|
534 doc->releaseSource(); |
|
535 |
|
536 snapshot.insert(doc); |
|
537 m_todo.remove(fileName); |
|
538 |
|
539 #ifndef ICHECK_BUILD |
|
540 Process process(m_modelManager, snapshot, m_workingCopy); |
|
541 |
|
542 process(doc); |
|
543 |
|
544 (void) switchDocument(previousDoc); |
|
545 #else |
|
546 (void) switchDocument(previousDoc); |
|
547 Document::CheckMode mode = Document::FastCheck; |
|
548 mode = Document::FullCheck; |
|
549 doc->parse(); |
|
550 doc->check(mode); |
|
551 #endif |
|
552 } |
|
553 |
|
554 Document::Ptr CppPreprocessor::switchDocument(Document::Ptr doc) |
|
555 { |
|
556 Document::Ptr previousDoc = m_currentDoc; |
|
557 m_currentDoc = doc; |
|
558 return previousDoc; |
|
559 } |
|
560 |
|
561 #ifndef ICHECK_BUILD |
|
562 void CppTools::CppModelManagerInterface::updateModifiedSourceFiles() |
|
563 { |
|
564 const Snapshot snapshot = this->snapshot(); |
|
565 QStringList sourceFiles; |
|
566 |
|
567 foreach (const Document::Ptr doc, snapshot) { |
|
568 const QDateTime lastModified = doc->lastModified(); |
|
569 |
|
570 if (! lastModified.isNull()) { |
|
571 QFileInfo fileInfo(doc->fileName()); |
|
572 |
|
573 if (fileInfo.exists() && fileInfo.lastModified() != lastModified) |
|
574 sourceFiles.append(doc->fileName()); |
|
575 } |
|
576 } |
|
577 |
|
578 updateSourceFiles(sourceFiles); |
|
579 } |
|
580 |
|
581 CppTools::CppModelManagerInterface *CppTools::CppModelManagerInterface::instance() |
|
582 { |
|
583 ExtensionSystem::PluginManager *pluginManager = ExtensionSystem::PluginManager::instance(); |
|
584 return pluginManager->getObject<CppTools::CppModelManagerInterface>(); |
|
585 |
|
586 } |
|
587 |
|
588 |
|
589 /*! |
|
590 \class CppTools::CppModelManager |
|
591 \brief The CppModelManager keeps track of one CppCodeModel instance |
|
592 for each project and all related CppCodeModelPart instances. |
|
593 |
|
594 It also takes care of updating the code models when C++ files are |
|
595 modified within Qt Creator. |
|
596 */ |
|
597 |
|
598 CppModelManager::CppModelManager(QObject *parent) |
|
599 : CppModelManagerInterface(parent) |
|
600 { |
|
601 m_findReferences = new CppFindReferences(this); |
|
602 m_indexerEnabled = qgetenv("QTCREATOR_NO_CODE_INDEXER").isNull(); |
|
603 |
|
604 m_revision = 0; |
|
605 m_synchronizer.setCancelOnWait(true); |
|
606 |
|
607 m_core = Core::ICore::instance(); // FIXME |
|
608 m_dirty = true; |
|
609 |
|
610 ProjectExplorer::ProjectExplorerPlugin *pe = |
|
611 ProjectExplorer::ProjectExplorerPlugin::instance(); |
|
612 |
|
613 QTC_ASSERT(pe, return); |
|
614 |
|
615 ProjectExplorer::SessionManager *session = pe->session(); |
|
616 QTC_ASSERT(session, return); |
|
617 |
|
618 m_updateEditorSelectionsTimer = new QTimer(this); |
|
619 m_updateEditorSelectionsTimer->setInterval(500); |
|
620 m_updateEditorSelectionsTimer->setSingleShot(true); |
|
621 connect(m_updateEditorSelectionsTimer, SIGNAL(timeout()), |
|
622 this, SLOT(updateEditorSelections())); |
|
623 |
|
624 connect(session, SIGNAL(projectAdded(ProjectExplorer::Project*)), |
|
625 this, SLOT(onProjectAdded(ProjectExplorer::Project*))); |
|
626 |
|
627 connect(session, SIGNAL(aboutToRemoveProject(ProjectExplorer::Project *)), |
|
628 this, SLOT(onAboutToRemoveProject(ProjectExplorer::Project *))); |
|
629 |
|
630 connect(session, SIGNAL(aboutToUnloadSession()), |
|
631 this, SLOT(onAboutToUnloadSession())); |
|
632 |
|
633 qRegisterMetaType<CPlusPlus::Document::Ptr>("CPlusPlus::Document::Ptr"); |
|
634 |
|
635 // thread connections |
|
636 connect(this, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)), |
|
637 this, SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr))); |
|
638 |
|
639 // Listen for editor closed and opened events so that we can keep track of changing files |
|
640 connect(m_core->editorManager(), SIGNAL(editorOpened(Core::IEditor *)), |
|
641 this, SLOT(editorOpened(Core::IEditor *))); |
|
642 |
|
643 connect(m_core->editorManager(), SIGNAL(editorAboutToClose(Core::IEditor *)), |
|
644 this, SLOT(editorAboutToClose(Core::IEditor *))); |
|
645 } |
|
646 |
|
647 CppModelManager::~CppModelManager() |
|
648 { } |
|
649 |
|
650 Snapshot CppModelManager::snapshot() const |
|
651 { |
|
652 QMutexLocker locker(&protectSnapshot); |
|
653 return m_snapshot; |
|
654 } |
|
655 |
|
656 void CppModelManager::ensureUpdated() |
|
657 { |
|
658 QMutexLocker locker(&mutex); |
|
659 if (! m_dirty) |
|
660 return; |
|
661 |
|
662 m_projectFiles = internalProjectFiles(); |
|
663 m_includePaths = internalIncludePaths(); |
|
664 m_frameworkPaths = internalFrameworkPaths(); |
|
665 m_definedMacros = internalDefinedMacros(); |
|
666 m_dirty = false; |
|
667 } |
|
668 |
|
669 QStringList CppModelManager::internalProjectFiles() const |
|
670 { |
|
671 QStringList files; |
|
672 QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects); |
|
673 while (it.hasNext()) { |
|
674 it.next(); |
|
675 ProjectInfo pinfo = it.value(); |
|
676 files += pinfo.sourceFiles; |
|
677 } |
|
678 files.removeDuplicates(); |
|
679 return files; |
|
680 } |
|
681 |
|
682 QStringList CppModelManager::internalIncludePaths() const |
|
683 { |
|
684 QStringList includePaths; |
|
685 QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects); |
|
686 while (it.hasNext()) { |
|
687 it.next(); |
|
688 ProjectInfo pinfo = it.value(); |
|
689 includePaths += pinfo.includePaths; |
|
690 } |
|
691 includePaths.removeDuplicates(); |
|
692 return includePaths; |
|
693 } |
|
694 |
|
695 QStringList CppModelManager::internalFrameworkPaths() const |
|
696 { |
|
697 QStringList frameworkPaths; |
|
698 QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects); |
|
699 while (it.hasNext()) { |
|
700 it.next(); |
|
701 ProjectInfo pinfo = it.value(); |
|
702 frameworkPaths += pinfo.frameworkPaths; |
|
703 } |
|
704 frameworkPaths.removeDuplicates(); |
|
705 return frameworkPaths; |
|
706 } |
|
707 |
|
708 QByteArray CppModelManager::internalDefinedMacros() const |
|
709 { |
|
710 QByteArray macros; |
|
711 QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects); |
|
712 while (it.hasNext()) { |
|
713 it.next(); |
|
714 ProjectInfo pinfo = it.value(); |
|
715 macros += pinfo.defines; |
|
716 } |
|
717 return macros; |
|
718 } |
|
719 |
|
720 void CppModelManager::setIncludesInPaths(const QMap<QString, QStringList> &includesInPaths) |
|
721 { |
|
722 QMutexLocker locker(&mutex); |
|
723 QMapIterator<QString, QStringList> i(includesInPaths); |
|
724 while (i.hasNext()) { |
|
725 i.next(); |
|
726 m_includesInPaths.insert(i.key(), i.value()); |
|
727 } |
|
728 } |
|
729 |
|
730 void CppModelManager::addEditorSupport(AbstractEditorSupport *editorSupport) |
|
731 { |
|
732 m_addtionalEditorSupport.insert(editorSupport); |
|
733 } |
|
734 |
|
735 void CppModelManager::removeEditorSupport(AbstractEditorSupport *editorSupport) |
|
736 { |
|
737 m_addtionalEditorSupport.remove(editorSupport); |
|
738 } |
|
739 |
|
740 QList<int> CppModelManager::references(CPlusPlus::Symbol *symbol, |
|
741 CPlusPlus::Document::Ptr doc, |
|
742 const CPlusPlus::Snapshot &snapshot) |
|
743 { |
|
744 NamespaceBindingPtr glo = bind(doc, snapshot); |
|
745 return m_findReferences->references(LookupContext::canonicalSymbol(symbol, glo.data()), doc, snapshot); |
|
746 } |
|
747 |
|
748 void CppModelManager::findUsages(CPlusPlus::Symbol *symbol) |
|
749 { |
|
750 if (symbol->identifier()) |
|
751 m_findReferences->findUsages(symbol); |
|
752 } |
|
753 |
|
754 void CppModelManager::renameUsages(CPlusPlus::Symbol *symbol) |
|
755 { |
|
756 if (symbol->identifier()) |
|
757 m_findReferences->renameUsages(symbol); |
|
758 } |
|
759 |
|
760 void CppModelManager::findMacroUsages(const CPlusPlus::Macro ¯o) |
|
761 { |
|
762 m_findReferences->findMacroUses(macro); |
|
763 } |
|
764 |
|
765 CppModelManager::WorkingCopy CppModelManager::buildWorkingCopyList() |
|
766 { |
|
767 WorkingCopy workingCopy; |
|
768 QMapIterator<TextEditor::ITextEditor *, CppEditorSupport *> it(m_editorSupport); |
|
769 while (it.hasNext()) { |
|
770 it.next(); |
|
771 TextEditor::ITextEditor *textEditor = it.key(); |
|
772 CppEditorSupport *editorSupport = it.value(); |
|
773 QString fileName = textEditor->file()->fileName(); |
|
774 workingCopy.insert(fileName, editorSupport->contents(), editorSupport->editorRevision()); |
|
775 } |
|
776 |
|
777 QSetIterator<AbstractEditorSupport *> jt(m_addtionalEditorSupport); |
|
778 while (jt.hasNext()) { |
|
779 AbstractEditorSupport *es = jt.next(); |
|
780 workingCopy.insert(es->fileName(), es->contents()); |
|
781 } |
|
782 |
|
783 // add the project configuration file |
|
784 QByteArray conf(pp_configuration); |
|
785 conf += definedMacros(); |
|
786 workingCopy.insert(pp_configuration_file, conf); |
|
787 |
|
788 return workingCopy; |
|
789 } |
|
790 |
|
791 CppModelManager::WorkingCopy CppModelManager::workingCopy() const |
|
792 { |
|
793 return const_cast<CppModelManager *>(this)->buildWorkingCopyList(); |
|
794 } |
|
795 |
|
796 void CppModelManager::updateSourceFiles(const QStringList &sourceFiles) |
|
797 { (void) refreshSourceFiles(sourceFiles); } |
|
798 |
|
799 QList<CppModelManager::ProjectInfo> CppModelManager::projectInfos() const |
|
800 { |
|
801 QMutexLocker locker(&mutex); |
|
802 |
|
803 return m_projects.values(); |
|
804 } |
|
805 |
|
806 CppModelManager::ProjectInfo CppModelManager::projectInfo(ProjectExplorer::Project *project) const |
|
807 { |
|
808 QMutexLocker locker(&mutex); |
|
809 |
|
810 return m_projects.value(project, ProjectInfo(project)); |
|
811 } |
|
812 |
|
813 void CppModelManager::updateProjectInfo(const ProjectInfo &pinfo) |
|
814 { |
|
815 QMutexLocker locker(&mutex); |
|
816 |
|
817 if (! pinfo.isValid()) |
|
818 return; |
|
819 |
|
820 m_projects.insert(pinfo.project, pinfo); |
|
821 m_dirty = true; |
|
822 |
|
823 if (m_indexerEnabled) { |
|
824 QFuture<void> result = QtConcurrent::run(&CppModelManager::updateIncludesInPaths, |
|
825 this, |
|
826 pinfo.includePaths, |
|
827 pinfo.frameworkPaths, |
|
828 m_headerSuffixes); |
|
829 |
|
830 if (pinfo.includePaths.size() > 1) { |
|
831 m_core->progressManager()->addTask(result, tr("Scanning"), |
|
832 CppTools::Constants::TASK_INDEX); |
|
833 } |
|
834 } |
|
835 } |
|
836 |
|
837 QStringList CppModelManager::includesInPath(const QString &path) const |
|
838 { |
|
839 QMutexLocker locker(&mutex); |
|
840 return m_includesInPaths.value(path); |
|
841 } |
|
842 |
|
843 QFuture<void> CppModelManager::refreshSourceFiles(const QStringList &sourceFiles) |
|
844 { |
|
845 if (! sourceFiles.isEmpty() && m_indexerEnabled) { |
|
846 const WorkingCopy workingCopy = buildWorkingCopyList(); |
|
847 |
|
848 CppPreprocessor *preproc = new CppPreprocessor(this); |
|
849 preproc->setRevision(++m_revision); |
|
850 preproc->setProjectFiles(projectFiles()); |
|
851 preproc->setIncludePaths(includePaths()); |
|
852 preproc->setFrameworkPaths(frameworkPaths()); |
|
853 preproc->setWorkingCopy(workingCopy); |
|
854 |
|
855 QFuture<void> result = QtConcurrent::run(&CppModelManager::parse, |
|
856 preproc, sourceFiles); |
|
857 |
|
858 if (m_synchronizer.futures().size() > 10) { |
|
859 QList<QFuture<void> > futures = m_synchronizer.futures(); |
|
860 |
|
861 m_synchronizer.clearFutures(); |
|
862 |
|
863 foreach (const QFuture<void> &future, futures) { |
|
864 if (! (future.isFinished() || future.isCanceled())) |
|
865 m_synchronizer.addFuture(future); |
|
866 } |
|
867 } |
|
868 |
|
869 m_synchronizer.addFuture(result); |
|
870 |
|
871 if (sourceFiles.count() > 1) { |
|
872 m_core->progressManager()->addTask(result, tr("Indexing"), |
|
873 CppTools::Constants::TASK_INDEX); |
|
874 } |
|
875 |
|
876 return result; |
|
877 } |
|
878 return QFuture<void>(); |
|
879 } |
|
880 |
|
881 /*! |
|
882 \fn void CppModelManager::editorOpened(Core::IEditor *editor) |
|
883 \brief If a C++ editor is opened, the model manager listens to content changes |
|
884 in order to update the CppCodeModel accordingly. It also updates the |
|
885 CppCodeModel for the first time with this editor. |
|
886 |
|
887 \sa void CppModelManager::editorContentsChanged() |
|
888 */ |
|
889 void CppModelManager::editorOpened(Core::IEditor *editor) |
|
890 { |
|
891 if (isCppEditor(editor)) { |
|
892 TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor); |
|
893 QTC_ASSERT(textEditor, return); |
|
894 |
|
895 CppEditorSupport *editorSupport = new CppEditorSupport(this); |
|
896 editorSupport->setTextEditor(textEditor); |
|
897 m_editorSupport[textEditor] = editorSupport; |
|
898 } |
|
899 } |
|
900 |
|
901 void CppModelManager::editorAboutToClose(Core::IEditor *editor) |
|
902 { |
|
903 if (isCppEditor(editor)) { |
|
904 TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor); |
|
905 QTC_ASSERT(textEditor, return); |
|
906 |
|
907 CppEditorSupport *editorSupport = m_editorSupport.value(textEditor); |
|
908 m_editorSupport.remove(textEditor); |
|
909 delete editorSupport; |
|
910 } |
|
911 } |
|
912 |
|
913 bool CppModelManager::isCppEditor(Core::IEditor *editor) const |
|
914 { |
|
915 Core::UniqueIDManager *uidm = m_core->uniqueIDManager(); |
|
916 const int uid = uidm->uniqueIdentifier(ProjectExplorer::Constants::LANG_CXX); |
|
917 return editor->context().contains(uid); |
|
918 } |
|
919 |
|
920 void CppModelManager::emitDocumentUpdated(Document::Ptr doc) |
|
921 { emit documentUpdated(doc); } |
|
922 |
|
923 void CppModelManager::onDocumentUpdated(Document::Ptr doc) |
|
924 { |
|
925 const QString fileName = doc->fileName(); |
|
926 |
|
927 bool outdated = false; |
|
928 |
|
929 protectSnapshot.lock(); |
|
930 |
|
931 Document::Ptr previous = m_snapshot.document(fileName); |
|
932 |
|
933 if (previous && (doc->revision() != 0 && doc->revision() < previous->revision())) |
|
934 outdated = true; |
|
935 else |
|
936 m_snapshot.insert(doc); |
|
937 |
|
938 protectSnapshot.unlock(); |
|
939 |
|
940 if (outdated) |
|
941 return; |
|
942 |
|
943 QList<Core::IEditor *> openedEditors = m_core->editorManager()->openedEditors(); |
|
944 foreach (Core::IEditor *editor, openedEditors) { |
|
945 if (editor->file()->fileName() == fileName) { |
|
946 TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor); |
|
947 if (! textEditor) |
|
948 continue; |
|
949 |
|
950 TextEditor::BaseTextEditor *ed = qobject_cast<TextEditor::BaseTextEditor *>(textEditor->widget()); |
|
951 if (! ed) |
|
952 continue; |
|
953 |
|
954 QList<TextEditor::BaseTextEditor::BlockRange> blockRanges; |
|
955 |
|
956 foreach (const Document::Block &block, doc->skippedBlocks()) { |
|
957 blockRanges.append(TextEditor::BaseTextEditor::BlockRange(block.begin(), block.end())); |
|
958 } |
|
959 |
|
960 QList<QTextEdit::ExtraSelection> selections; |
|
961 |
|
962 #ifdef QTCREATOR_WITH_MACRO_HIGHLIGHTING |
|
963 // set up the format for the macros |
|
964 QTextCharFormat macroFormat; |
|
965 macroFormat.setUnderlineStyle(QTextCharFormat::SingleUnderline); |
|
966 |
|
967 QTextCursor c = ed->textCursor(); |
|
968 foreach (const Document::MacroUse &block, doc->macroUses()) { |
|
969 QTextEdit::ExtraSelection sel; |
|
970 sel.cursor = c; |
|
971 sel.cursor.setPosition(block.begin()); |
|
972 sel.cursor.setPosition(block.end(), QTextCursor::KeepAnchor); |
|
973 sel.format = macroFormat; |
|
974 selections.append(sel); |
|
975 } |
|
976 #endif // QTCREATOR_WITH_MACRO_HIGHLIGHTING |
|
977 |
|
978 // set up the format for the errors |
|
979 QTextCharFormat errorFormat; |
|
980 errorFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline); |
|
981 errorFormat.setUnderlineColor(Qt::red); |
|
982 |
|
983 // set up the format for the warnings. |
|
984 QTextCharFormat warningFormat; |
|
985 warningFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline); |
|
986 warningFormat.setUnderlineColor(Qt::darkYellow); |
|
987 |
|
988 #ifdef QTCREATOR_WITH_ADVANCED_HIGHLIGHTER |
|
989 QSet<QPair<unsigned, unsigned> > lines; |
|
990 foreach (const Document::DiagnosticMessage &m, doc->diagnosticMessages()) { |
|
991 if (m.fileName() != fileName) |
|
992 continue; |
|
993 |
|
994 const QPair<unsigned, unsigned> coordinates = qMakePair(m.line(), m.column()); |
|
995 |
|
996 if (lines.contains(coordinates)) |
|
997 continue; |
|
998 |
|
999 lines.insert(coordinates); |
|
1000 |
|
1001 QTextEdit::ExtraSelection sel; |
|
1002 if (m.isWarning()) |
|
1003 sel.format = warningFormat; |
|
1004 else |
|
1005 sel.format = errorFormat; |
|
1006 |
|
1007 QTextCursor c(ed->document()->findBlockByNumber(m.line() - 1)); |
|
1008 |
|
1009 // ### check for generated tokens. |
|
1010 |
|
1011 int column = m.column(); |
|
1012 |
|
1013 if (column > c.block().length()) { |
|
1014 column = 0; |
|
1015 |
|
1016 const QString text = c.block().text(); |
|
1017 for (int i = 0; i < text.size(); ++i) { |
|
1018 if (! text.at(i).isSpace()) { |
|
1019 ++column; |
|
1020 break; |
|
1021 } |
|
1022 } |
|
1023 } |
|
1024 |
|
1025 if (column != 0) |
|
1026 --column; |
|
1027 |
|
1028 c.setPosition(c.position() + column); |
|
1029 c.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); |
|
1030 sel.cursor = c; |
|
1031 selections.append(sel); |
|
1032 } |
|
1033 #else |
|
1034 QSet<int> lines; |
|
1035 foreach (const Document::DiagnosticMessage &m, doc->diagnosticMessages()) { |
|
1036 if (m.fileName() != fileName) |
|
1037 continue; |
|
1038 else if (lines.contains(m.line())) |
|
1039 continue; |
|
1040 |
|
1041 lines.insert(m.line()); |
|
1042 |
|
1043 QTextEdit::ExtraSelection sel; |
|
1044 if (m.isWarning()) |
|
1045 sel.format = warningFormat; |
|
1046 else |
|
1047 sel.format = errorFormat; |
|
1048 |
|
1049 QTextCursor c(ed->document()->findBlockByNumber(m.line() - 1)); |
|
1050 const QString text = c.block().text(); |
|
1051 for (int i = 0; i < text.size(); ++i) { |
|
1052 if (! text.at(i).isSpace()) { |
|
1053 c.setPosition(c.position() + i); |
|
1054 break; |
|
1055 } |
|
1056 } |
|
1057 c.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); |
|
1058 sel.cursor = c; |
|
1059 selections.append(sel); |
|
1060 } |
|
1061 #endif |
|
1062 QList<Editor> todo; |
|
1063 foreach (const Editor &e, todo) { |
|
1064 if (e.textEditor != textEditor) |
|
1065 todo.append(e); |
|
1066 } |
|
1067 |
|
1068 Editor e; |
|
1069 e.revision = ed->document()->revision(); |
|
1070 e.textEditor = textEditor; |
|
1071 e.selections = selections; |
|
1072 e.ifdefedOutBlocks = blockRanges; |
|
1073 todo.append(e); |
|
1074 m_todo = todo; |
|
1075 postEditorUpdate(); |
|
1076 break; |
|
1077 } |
|
1078 } |
|
1079 } |
|
1080 |
|
1081 void CppModelManager::postEditorUpdate() |
|
1082 { |
|
1083 m_updateEditorSelectionsTimer->start(500); |
|
1084 } |
|
1085 |
|
1086 void CppModelManager::updateEditorSelections() |
|
1087 { |
|
1088 foreach (const Editor &ed, m_todo) { |
|
1089 if (! ed.textEditor) |
|
1090 continue; |
|
1091 |
|
1092 TextEditor::ITextEditor *textEditor = ed.textEditor; |
|
1093 TextEditor::BaseTextEditor *editor = qobject_cast<TextEditor::BaseTextEditor *>(textEditor->widget()); |
|
1094 |
|
1095 if (! editor) |
|
1096 continue; |
|
1097 else if (editor->document()->revision() != ed.revision) |
|
1098 continue; // outdated |
|
1099 |
|
1100 editor->setExtraSelections(TextEditor::BaseTextEditor::CodeWarningsSelection, |
|
1101 ed.selections); |
|
1102 |
|
1103 editor->setIfdefedOutBlocks(ed.ifdefedOutBlocks); |
|
1104 } |
|
1105 |
|
1106 m_todo.clear(); |
|
1107 |
|
1108 } |
|
1109 |
|
1110 void CppModelManager::onProjectAdded(ProjectExplorer::Project *) |
|
1111 { |
|
1112 QMutexLocker locker(&mutex); |
|
1113 m_dirty = true; |
|
1114 } |
|
1115 |
|
1116 void CppModelManager::onAboutToRemoveProject(ProjectExplorer::Project *project) |
|
1117 { |
|
1118 do { |
|
1119 QMutexLocker locker(&mutex); |
|
1120 m_dirty = true; |
|
1121 m_projects.remove(project); |
|
1122 } while (0); |
|
1123 |
|
1124 GC(); |
|
1125 } |
|
1126 |
|
1127 void CppModelManager::onAboutToUnloadSession() |
|
1128 { |
|
1129 if (m_core->progressManager()) { |
|
1130 m_core->progressManager()->cancelTasks(CppTools::Constants::TASK_INDEX); |
|
1131 } |
|
1132 |
|
1133 do { |
|
1134 QMutexLocker locker(&mutex); |
|
1135 m_projects.clear(); |
|
1136 m_dirty = true; |
|
1137 } while (0); |
|
1138 |
|
1139 GC(); |
|
1140 } |
|
1141 |
|
1142 void CppModelManager::updateIncludesInPaths(QFutureInterface<void> &future, |
|
1143 CppModelManager *manager, |
|
1144 QStringList paths, |
|
1145 QStringList frameworkPaths, |
|
1146 QStringList suffixes) |
|
1147 { |
|
1148 QMap<QString, QStringList> entriesInPaths; |
|
1149 typedef QPair<QString, QString> SymLink; |
|
1150 typedef QList<SymLink> SymLinks; |
|
1151 SymLinks symlinks; |
|
1152 int processed = 0; |
|
1153 |
|
1154 future.setProgressRange(0, paths.size()); |
|
1155 |
|
1156 // Add framework header directories to path list |
|
1157 QStringList frameworkFilter; |
|
1158 frameworkFilter << QLatin1String("*.framework"); |
|
1159 QStringListIterator fwPathIt(frameworkPaths); |
|
1160 while (fwPathIt.hasNext()) { |
|
1161 const QString &fwPath = fwPathIt.next(); |
|
1162 QStringList entriesInFrameworkPath; |
|
1163 const QStringList &frameworks = QDir(fwPath).entryList(frameworkFilter, QDir::Dirs | QDir::NoDotAndDotDot); |
|
1164 QStringListIterator fwIt(frameworks); |
|
1165 while (fwIt.hasNext()) { |
|
1166 QString framework = fwIt.next(); |
|
1167 paths.append(fwPath + QLatin1Char('/') + framework + QLatin1String("/Headers")); |
|
1168 framework.chop(10); // remove the ".framework" |
|
1169 entriesInFrameworkPath.append(framework + QLatin1Char('/')); |
|
1170 } |
|
1171 entriesInPaths.insert(fwPath, entriesInFrameworkPath); |
|
1172 } |
|
1173 |
|
1174 while (!paths.isEmpty()) { |
|
1175 if (future.isPaused()) |
|
1176 future.waitForResume(); |
|
1177 |
|
1178 if (future.isCanceled()) |
|
1179 return; |
|
1180 |
|
1181 const QString path = paths.takeFirst(); |
|
1182 |
|
1183 // Skip already scanned paths |
|
1184 if (entriesInPaths.contains(path)) |
|
1185 continue; |
|
1186 |
|
1187 QStringList entries; |
|
1188 |
|
1189 QDirIterator i(path, QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); |
|
1190 while (i.hasNext()) { |
|
1191 const QString fileName = i.next(); |
|
1192 const QFileInfo fileInfo = i.fileInfo(); |
|
1193 const QString suffix = fileInfo.suffix(); |
|
1194 if (suffix.isEmpty() || suffixes.contains(suffix)) { |
|
1195 QString text = fileInfo.fileName(); |
|
1196 if (fileInfo.isDir()) { |
|
1197 text += QLatin1Char('/'); |
|
1198 |
|
1199 // Also scan subdirectory, but avoid endless recursion with symbolic links |
|
1200 if (fileInfo.isSymLink()) { |
|
1201 QString target = fileInfo.symLinkTarget(); |
|
1202 |
|
1203 // Don't add broken symlinks |
|
1204 if (!QFileInfo(target).exists()) |
|
1205 continue; |
|
1206 |
|
1207 QMap<QString, QStringList>::const_iterator result = entriesInPaths.find(target); |
|
1208 if (result != entriesInPaths.constEnd()) { |
|
1209 entriesInPaths.insert(fileName, result.value()); |
|
1210 } else { |
|
1211 paths.append(target); |
|
1212 symlinks.append(SymLink(fileName, target)); |
|
1213 } |
|
1214 } else { |
|
1215 paths.append(fileName); |
|
1216 } |
|
1217 } |
|
1218 entries.append(text); |
|
1219 } |
|
1220 } |
|
1221 |
|
1222 entriesInPaths.insert(path, entries); |
|
1223 |
|
1224 ++processed; |
|
1225 future.setProgressRange(0, processed + paths.size()); |
|
1226 future.setProgressValue(processed); |
|
1227 } |
|
1228 // link symlinks |
|
1229 QListIterator<SymLink> it(symlinks); |
|
1230 it.toBack(); |
|
1231 while (it.hasPrevious()) { |
|
1232 SymLink v = it.previous(); |
|
1233 QMap<QString, QStringList>::const_iterator result = entriesInPaths.find(v.second); |
|
1234 entriesInPaths.insert(v.first, result.value()); |
|
1235 } |
|
1236 |
|
1237 manager->setIncludesInPaths(entriesInPaths); |
|
1238 |
|
1239 future.reportFinished(); |
|
1240 } |
|
1241 |
|
1242 void CppModelManager::parse(QFutureInterface<void> &future, |
|
1243 CppPreprocessor *preproc, |
|
1244 QStringList files) |
|
1245 { |
|
1246 if (files.isEmpty()) |
|
1247 return; |
|
1248 |
|
1249 Core::MimeDatabase *db = Core::ICore::instance()->mimeDatabase(); |
|
1250 QStringList headers, sources; |
|
1251 Core::MimeType cSourceTy = db->findByType(QLatin1String("text/x-csrc")); |
|
1252 Core::MimeType cppSourceTy = db->findByType(QLatin1String("text/x-c++src")); |
|
1253 Core::MimeType mSourceTy = db->findByType(QLatin1String("text/x-objcsrc")); |
|
1254 |
|
1255 Core::MimeType cHeaderTy = db->findByType(QLatin1String("text/x-hdr")); |
|
1256 Core::MimeType cppHeaderTy = db->findByType(QLatin1String("text/x-c++hdr")); |
|
1257 |
|
1258 foreach (const QString &file, files) { |
|
1259 const QFileInfo fileInfo(file); |
|
1260 |
|
1261 if (cSourceTy.matchesFile(fileInfo) || cppSourceTy.matchesFile(fileInfo) || mSourceTy.matchesFile(fileInfo)) |
|
1262 sources.append(file); |
|
1263 |
|
1264 else if (cHeaderTy.matchesFile(fileInfo) || cppHeaderTy.matchesFile(fileInfo)) |
|
1265 headers.append(file); |
|
1266 } |
|
1267 |
|
1268 foreach (const QString &file, files) { |
|
1269 preproc->snapshot.remove(file); |
|
1270 } |
|
1271 |
|
1272 files = sources; |
|
1273 files += headers; |
|
1274 |
|
1275 preproc->setTodo(files); |
|
1276 |
|
1277 future.setProgressRange(0, files.size()); |
|
1278 |
|
1279 QString conf = QLatin1String(pp_configuration_file); |
|
1280 |
|
1281 bool processingHeaders = false; |
|
1282 |
|
1283 for (int i = 0; i < files.size(); ++i) { |
|
1284 if (future.isPaused()) |
|
1285 future.waitForResume(); |
|
1286 |
|
1287 if (future.isCanceled()) |
|
1288 break; |
|
1289 |
|
1290 // Change the priority of the background parser thread to idle. |
|
1291 QThread::currentThread()->setPriority(QThread::IdlePriority); |
|
1292 |
|
1293 QString fileName = files.at(i); |
|
1294 |
|
1295 bool isSourceFile = false; |
|
1296 if (cppSourceTy.matchesFile(fileName) || cSourceTy.matchesFile(fileName)) |
|
1297 isSourceFile = true; |
|
1298 |
|
1299 if (isSourceFile) |
|
1300 (void) preproc->run(conf); |
|
1301 |
|
1302 else if (! processingHeaders) { |
|
1303 (void) preproc->run(conf); |
|
1304 |
|
1305 processingHeaders = true; |
|
1306 } |
|
1307 |
|
1308 preproc->run(fileName); |
|
1309 |
|
1310 future.setProgressValue(files.size() - preproc->todo().size()); |
|
1311 |
|
1312 if (isSourceFile) |
|
1313 preproc->resetEnvironment(); |
|
1314 |
|
1315 // Restore the previous thread priority. |
|
1316 QThread::currentThread()->setPriority(QThread::NormalPriority); |
|
1317 } |
|
1318 |
|
1319 future.setProgressValue(files.size()); |
|
1320 |
|
1321 delete preproc; |
|
1322 } |
|
1323 |
|
1324 void CppModelManager::GC() |
|
1325 { |
|
1326 protectSnapshot.lock(); |
|
1327 Snapshot currentSnapshot = m_snapshot; |
|
1328 protectSnapshot.unlock(); |
|
1329 |
|
1330 QSet<QString> processed; |
|
1331 QStringList todo = projectFiles(); |
|
1332 |
|
1333 while (! todo.isEmpty()) { |
|
1334 QString fn = todo.last(); |
|
1335 todo.removeLast(); |
|
1336 |
|
1337 if (processed.contains(fn)) |
|
1338 continue; |
|
1339 |
|
1340 processed.insert(fn); |
|
1341 |
|
1342 if (Document::Ptr doc = currentSnapshot.document(fn)) { |
|
1343 todo += doc->includedFiles(); |
|
1344 } |
|
1345 } |
|
1346 |
|
1347 QStringList removedFiles; |
|
1348 |
|
1349 Snapshot newSnapshot; |
|
1350 for (Snapshot::const_iterator it = currentSnapshot.begin(); it != currentSnapshot.end(); ++it) { |
|
1351 const QString fileName = it.key(); |
|
1352 |
|
1353 if (processed.contains(fileName)) |
|
1354 newSnapshot.insert(it.value()); |
|
1355 else |
|
1356 removedFiles.append(fileName); |
|
1357 } |
|
1358 |
|
1359 emit aboutToRemoveFiles(removedFiles); |
|
1360 |
|
1361 protectSnapshot.lock(); |
|
1362 m_snapshot = newSnapshot; |
|
1363 protectSnapshot.unlock(); |
|
1364 } |
|
1365 #endif |
|
1366 |