cppmodelmanager.cpp 35.6 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
Eike Ziller's avatar
Eike Ziller committed
3 4
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8 9 10 11
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
Eike Ziller's avatar
Eike Ziller committed
12 13
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
Eike Ziller's avatar
Eike Ziller committed
14
** use the contact form at http://www.qt.io/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18 19 20 21 22 23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
24
**
Eike Ziller's avatar
Eike Ziller committed
25 26
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company LGPL Exception
con's avatar
con committed
27 28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
hjk's avatar
hjk committed
30

con's avatar
con committed
31
#include "cppmodelmanager.h"
32

33
#include "abstracteditorsupport.h"
34
#include "baseeditordocumentprocessor.h"
35
#include "builtinindexingsupport.h"
36
#include "cppcodemodelinspectordumper.h"
37
#include "cppcodemodelsettings.h"
38
#include "cppfindreferences.h"
39
#include "cppindexingsupport.h"
40
#include "cppmodelmanagersupportinternal.h"
41
#include "cpprefactoringchanges.h"
42
#include "cppsourceprocessor.h"
43
#include "cpptoolsconstants.h"
44
#include "cpptoolsplugin.h"
45
#include "editordocumenthandle.h"
con's avatar
con committed
46

47
#include <coreplugin/documentmanager.h>
48 49
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
50 51
#include <coreplugin/editormanager/editormanager.h>
#include <texteditor/textdocument.h>
52
#include <projectexplorer/project.h>
53 54
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/session.h>
55
#include <extensionsystem/pluginmanager.h>
56
#include <utils/fileutils.h>
hjk's avatar
hjk committed
57
#include <utils/qtcassert.h>
con's avatar
con committed
58

59 60
#include <QCoreApplication>
#include <QDebug>
61
#include <QDir>
62 63
#include <QMutexLocker>
#include <QTextBlock>
64
#include <QTimer>
65

66 67
#if defined(QTCREATOR_WITH_DUMP_AST) && defined(Q_CC_GNU)
#define WITH_AST_DUMP
68 69
#include <iostream>
#include <sstream>
70
#endif
hjk's avatar
hjk committed
71

72 73
Q_DECLARE_METATYPE(QSet<QString>)

74 75
static const bool DumpProjectInfo = qgetenv("QTC_DUMP_PROJECT_INFO") == "1";

76 77
using namespace CppTools;
using namespace CppTools::Internal;
con's avatar
con committed
78 79
using namespace CPlusPlus;

80
#ifdef QTCREATOR_WITH_DUMP_AST
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119

#include <cxxabi.h>

class DumpAST: protected ASTVisitor
{
public:
    int depth;

    DumpAST(Control *control)
        : ASTVisitor(control), depth(0)
    { }

    void operator()(AST *ast)
    { accept(ast); }

protected:
    virtual bool preVisit(AST *ast)
    {
        std::ostringstream s;
        PrettyPrinter pp(control(), s);
        pp(ast);
        QString code = QString::fromStdString(s.str());
        code.replace('\n', ' ');
        code.replace(QRegExp("\\s+"), " ");

        const char *name = abi::__cxa_demangle(typeid(*ast).name(), 0, 0, 0) + 11;

        QByteArray ind(depth, ' ');
        ind += name;

        printf("%-40s %s\n", ind.constData(), qPrintable(code));
        ++depth;
        return true;
    }

    virtual void postVisit(AST *)
    { --depth; }
};

Roberto Raggi's avatar
Roberto Raggi committed
120
#endif // QTCREATOR_WITH_DUMP_AST
121

122 123 124 125 126 127 128 129 130 131 132
namespace CppTools {
namespace Internal {

static QMutex m_instanceMutex;
static CppModelManager *m_instance;

class CppModelManagerPrivate
{
public:
    // Snapshot
    mutable QMutex m_snapshotMutex;
133
    Snapshot m_snapshot;
134 135 136 137

    // Project integration
    mutable QMutex m_projectMutex;
    QMap<ProjectExplorer::Project *, ProjectInfo> m_projectToProjectsInfo;
138 139
    QMap<Utils::FileName, QList<ProjectPart::Ptr> > m_fileToProjectParts;
    QMap<QString, ProjectPart::Ptr> m_projectFileToProjectPart;
140 141 142 143 144 145 146
    // The members below are cached/(re)calculated from the projects and/or their project parts
    bool m_dirty;
    QStringList m_projectFiles;
    ProjectPart::HeaderPaths m_headerPaths;
    QByteArray m_definedMacros;

    // Editor integration
147 148
    mutable QMutex m_cppEditorDocumentsMutex;
    QMap<QString, CppEditorDocumentHandle *> m_cppEditorDocuments;
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
    QSet<AbstractEditorSupport *> m_extraEditorSupports;

    // Completion & highlighting
    QHash<QString, ModelManagerSupport *> m_idTocodeModelSupporter;
    QScopedPointer<ModelManagerSupport> m_modelManagerSupportFallback;

    // Indexing
    CppIndexingSupport *m_indexingSupporter;
    CppIndexingSupport *m_internalIndexingSupport;
    bool m_indexerEnabled;

    CppFindReferences *m_findReferences;

    bool m_enableGC;
    QTimer m_delayedGcTimer;
};

} // namespace Internal

const char pp_configuration[] =
con's avatar
con committed
169
    "# 1 \"<configuration>\"\n"
170
    "#define Q_CREATOR_RUN 1\n"
con's avatar
con committed
171 172 173 174 175 176
    "#define __cplusplus 1\n"
    "#define __extension__\n"
    "#define __context__\n"
    "#define __range__\n"
    "#define   restrict\n"
    "#define __restrict\n"
177
    "#define __restrict__\n"
con's avatar
con committed
178

179 180 181 182
    "#define __complex__\n"
    "#define __imag__\n"
    "#define __real__\n"

183 184
    "#define __builtin_va_arg(a,b) ((b)0)\n"

185 186
    "#define _Pragma(x)\n" // C99 _Pragma operator

con's avatar
con committed
187 188
    // ### add macros for win32
    "#define __cdecl\n"
189
    "#define __stdcall\n"
190
    "#define __thiscall\n"
con's avatar
con committed
191 192 193 194 195
    "#define QT_WA(x) x\n"
    "#define CALLBACK\n"
    "#define STDMETHODCALLTYPE\n"
    "#define __RPC_FAR\n"
    "#define __declspec(a)\n"
196 197 198
    "#define STDMETHOD(method) virtual HRESULT STDMETHODCALLTYPE method\n"
    "#define __try try\n"
    "#define __except catch\n"
199 200
    "#define __finally\n"
    "#define __inline inline\n"
201 202
    "#define __forceinline inline\n"
    "#define __pragma(x)\n";
con's avatar
con committed
203

204
QSet<QString> CppModelManager::timeStampModifiedFiles(const QList<Document::Ptr> &documentsToCheck)
205
{
206
    QSet<QString> sourceFiles;
207

208
    foreach (const Document::Ptr doc, documentsToCheck) {
209 210
        const QDateTime lastModified = doc->lastModified();

211
        if (!lastModified.isNull()) {
212 213 214
            QFileInfo fileInfo(doc->fileName());

            if (fileInfo.exists() && fileInfo.lastModified() != lastModified)
215
                sourceFiles.insert(doc->fileName());
216 217 218
        }
    }

219 220 221
    return sourceFiles;
}

222 223 224 225 226 227 228 229 230 231 232 233
/*!
 * \brief createSourceProcessor Create a new source processor, which will signal the
 * model manager when a document has been processed.
 *
 * Indexed file is truncated version of fully parsed document: copy of source
 * code and full AST will be dropped when indexing is done.
 *
 * \return a new source processor object, which the caller needs to delete when finished.
 */
CppSourceProcessor *CppModelManager::createSourceProcessor()
{
    CppModelManager *that = instance();
Orgad Shaneh's avatar
Orgad Shaneh committed
234
    return new CppSourceProcessor(that->snapshot(), [that](const Document::Ptr &doc) {
235 236 237 238 239
        that->emitDocumentUpdated(doc);
        doc->releaseSourceAndAST();
    });
}

240 241 242 243 244 245 246
QString CppModelManager::editorConfigurationFileName()
{
    return QLatin1String("<per-editor-defines>");
}

QString CppModelManager::configurationFileName()
{
247
    return Preprocessor::configurationFileName;
248 249
}

250 251 252 253 254 255 256
void CppModelManager::updateModifiedSourceFiles()
{
    const Snapshot snapshot = this->snapshot();
    QList<Document::Ptr> documentsToCheck;
    foreach (const Document::Ptr document, snapshot)
        documentsToCheck << document;

257
    updateSourceFiles(timeStampModifiedFiles(documentsToCheck));
258 259
}

con's avatar
con committed
260 261
/*!
    \class CppTools::CppModelManager
262 263 264
    \brief The CppModelManager keeps tracks of the source files the code model is aware of.

    The CppModelManager manages the source files in a Snapshot object.
con's avatar
con committed
265

266 267 268 269
    The snapshot is updated in case e.g.
        * New files are opened/edited (Editor integration)
        * A project manager pushes updated project information (Project integration)
        * Files are garbage collected
con's avatar
con committed
270 271
*/

272 273
CppModelManager *CppModelManager::instance()
{
274 275 276 277 278 279 280 281
    if (m_instance)
        return m_instance;

    QMutexLocker locker(&m_instanceMutex);
    if (!m_instance)
        m_instance = new CppModelManager;

    return m_instance;
282 283
}

284
CppModelManager::CppModelManager(QObject *parent)
285
    : CppModelManagerBase(parent), d(new CppModelManagerPrivate)
con's avatar
con committed
286
{
287 288 289
    d->m_indexingSupporter = 0;
    d->m_enableGC = true;

290
    qRegisterMetaType<QSet<QString> >();
291
    connect(this, SIGNAL(sourceFilesRefreshed(QSet<QString>)),
292
            this, SLOT(onSourceFilesRefreshed()));
Erik Verbruggen's avatar
Erik Verbruggen committed
293

294 295
    d->m_findReferences = new CppFindReferences(this);
    d->m_indexerEnabled = qgetenv("QTC_NO_CODE_INDEXER") != "1";
Roberto Raggi's avatar
Roberto Raggi committed
296

297
    d->m_dirty = true;
298

299 300 301
    d->m_delayedGcTimer.setObjectName(QLatin1String("CppModelManager::m_delayedGcTimer"));
    d->m_delayedGcTimer.setSingleShot(true);
    connect(&d->m_delayedGcTimer, SIGNAL(timeout()), this, SLOT(GC()));
302

hjk's avatar
hjk committed
303 304
    QObject *sessionManager = ProjectExplorer::SessionManager::instance();
    connect(sessionManager, SIGNAL(projectAdded(ProjectExplorer::Project*)),
305
            this, SLOT(onProjectAdded(ProjectExplorer::Project*)));
hjk's avatar
hjk committed
306
    connect(sessionManager, SIGNAL(aboutToRemoveProject(ProjectExplorer::Project*)),
Robert Loehning's avatar
Robert Loehning committed
307
            this, SLOT(onAboutToRemoveProject(ProjectExplorer::Project*)));
hjk's avatar
hjk committed
308
    connect(sessionManager, SIGNAL(aboutToLoadSession(QString)),
309
            this, SLOT(onAboutToLoadSession()));
hjk's avatar
hjk committed
310
    connect(sessionManager, SIGNAL(aboutToUnloadSession(QString)),
311
            this, SLOT(onAboutToUnloadSession()));
312 313 314 315

    connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged,
            this, &CppModelManager::onCurrentEditorChanged);

316 317
    connect(Core::DocumentManager::instance(), &Core::DocumentManager::allDocumentsRenamed,
            this, &CppModelManager::renameIncludes);
con's avatar
con committed
318

319 320 321
    connect(Core::ICore::instance(), SIGNAL(coreAboutToClose()),
            this, SLOT(onCoreAboutToClose()));

con's avatar
con committed
322
    qRegisterMetaType<CPlusPlus::Document::Ptr>("CPlusPlus::Document::Ptr");
323
    qRegisterMetaType<QList<Document::DiagnosticMessage>>(
324
                "QList<CPlusPlus::Document::DiagnosticMessage>");
con's avatar
con committed
325

326
    d->m_modelManagerSupportFallback.reset(new ModelManagerSupportInternal);
327
    CppToolsPlugin::instance()->codeModelSettings()->setDefaultId(
328 329
                d->m_modelManagerSupportFallback->id());
    addModelManagerSupport(d->m_modelManagerSupportFallback.data());
330

331
    d->m_internalIndexingSupport = new BuiltinIndexingSupport;
con's avatar
con committed
332 333 334
}

CppModelManager::~CppModelManager()
335
{
336
    delete d->m_internalIndexingSupport;
337
    delete d;
338
}
con's avatar
con committed
339

340
Snapshot CppModelManager::snapshot() const
341
{
342 343
    QMutexLocker locker(&d->m_snapshotMutex);
    return d->m_snapshot;
344
}
con's avatar
con committed
345

346 347
Document::Ptr CppModelManager::document(const QString &fileName) const
{
348 349
    QMutexLocker locker(&d->m_snapshotMutex);
    return d->m_snapshot.document(fileName);
350 351 352 353 354 355 356
}

/// Replace the document in the snapshot.
///
/// \returns true if successful, false if the new document is out-dated.
bool CppModelManager::replaceDocument(Document::Ptr newDoc)
{
357
    QMutexLocker locker(&d->m_snapshotMutex);
358

359
    Document::Ptr previous = d->m_snapshot.document(newDoc->fileName());
360 361 362 363
    if (previous && (newDoc->revision() != 0 && newDoc->revision() < previous->revision()))
        // the new document is outdated
        return false;

364
    d->m_snapshot.insert(newDoc);
365 366 367
    return true;
}

368 369
void CppModelManager::ensureUpdated()
{
370 371
    QMutexLocker locker(&d->m_projectMutex);
    if (!d->m_dirty)
372 373
        return;

374 375 376 377
    d->m_projectFiles = internalProjectFiles();
    d->m_headerPaths = internalHeaderPaths();
    d->m_definedMacros = internalDefinedMacros();
    d->m_dirty = false;
378 379
}

380
QStringList CppModelManager::internalProjectFiles() const
con's avatar
con committed
381 382
{
    QStringList files;
383
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(d->m_projectToProjectsInfo);
con's avatar
con committed
384 385
    while (it.hasNext()) {
        it.next();
386
        const ProjectInfo pinfo = it.value();
387
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
388 389
            foreach (const ProjectFile &file, part->files)
                files += file.path;
390
        }
con's avatar
con committed
391
    }
392
    files.removeDuplicates();
con's avatar
con committed
393 394 395
    return files;
}

396
ProjectPart::HeaderPaths CppModelManager::internalHeaderPaths() const
con's avatar
con committed
397
{
398
    ProjectPart::HeaderPaths headerPaths;
399
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(d->m_projectToProjectsInfo);
con's avatar
con committed
400 401
    while (it.hasNext()) {
        it.next();
402
        const ProjectInfo pinfo = it.value();
403 404
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
            foreach (const ProjectPart::HeaderPath &path, part->headerPaths) {
405
                const ProjectPart::HeaderPath hp(QDir::cleanPath(path.path), path.type);
406 407 408 409
                if (!headerPaths.contains(hp))
                    headerPaths += hp;
            }
        }
con's avatar
con committed
410
    }
411
    return headerPaths;
con's avatar
con committed
412 413
}

414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429
static void addUnique(const QList<QByteArray> &defs, QByteArray *macros, QSet<QByteArray> *alreadyIn)
{
    Q_ASSERT(macros);
    Q_ASSERT(alreadyIn);

    foreach (const QByteArray &def, defs) {
        if (def.trimmed().isEmpty())
            continue;
        if (!alreadyIn->contains(def)) {
            macros->append(def);
            macros->append('\n');
            alreadyIn->insert(def);
        }
    }
}

430
QByteArray CppModelManager::internalDefinedMacros() const
con's avatar
con committed
431 432
{
    QByteArray macros;
433
    QSet<QByteArray> alreadyIn;
434
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(d->m_projectToProjectsInfo);
con's avatar
con committed
435 436
    while (it.hasNext()) {
        it.next();
437
        const ProjectInfo pinfo = it.value();
438
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
439 440
            addUnique(part->toolchainDefines.split('\n'), &macros, &alreadyIn);
            addUnique(part->projectDefines.split('\n'), &macros, &alreadyIn);
441
            if (!part->projectConfigFile.isEmpty())
442
                macros += ProjectPart::readProjectConfigFile(part);
443
        }
con's avatar
con committed
444 445 446 447
    }
    return macros;
}

448
/// This function will acquire mutexes!
449
void CppModelManager::dumpModelManagerConfiguration(const QString &logFileId)
450
{
451 452 453 454
    const Snapshot globalSnapshot = snapshot();
    const QString globalSnapshotTitle
        = QString::fromLatin1("Global/Indexing Snapshot (%1 Documents)").arg(globalSnapshot.size());

455
    CppCodeModelInspector::Dumper dumper(globalSnapshot, logFileId);
456 457 458
    dumper.dumpProjectInfos(projectInfos());
    dumper.dumpSnapshot(globalSnapshot, globalSnapshotTitle, /*isGlobalSnapshot=*/ true);
    dumper.dumpWorkingCopy(workingCopy());
459
    ensureUpdated();
460
    dumper.dumpMergedEntities(d->m_headerPaths, d->m_definedMacros);
461 462
}

463
void CppModelManager::addExtraEditorSupport(AbstractEditorSupport *editorSupport)
464
{
465
    d->m_extraEditorSupports.insert(editorSupport);
466 467
}

468
void CppModelManager::removeExtraEditorSupport(AbstractEditorSupport *editorSupport)
469
{
470
    d->m_extraEditorSupports.remove(editorSupport);
471 472
}

473
CppEditorDocumentHandle *CppModelManager::cppEditorDocument(const QString &filePath) const
474
{
475 476
    if (filePath.isEmpty())
        return 0;
477

478 479
    QMutexLocker locker(&d->m_cppEditorDocumentsMutex);
    return d->m_cppEditorDocuments.value(filePath, 0);
480 481
}

482
void CppModelManager::registerCppEditorDocument(CppEditorDocumentHandle *editorDocument)
483
{
484 485 486
    QTC_ASSERT(editorDocument, return);
    const QString filePath = editorDocument->filePath();
    QTC_ASSERT(!filePath.isEmpty(), return);
487

488 489 490
    QMutexLocker locker(&d->m_cppEditorDocumentsMutex);
    QTC_ASSERT(d->m_cppEditorDocuments.value(filePath, 0) == 0, return);
    d->m_cppEditorDocuments.insert(filePath, editorDocument);
491
}
492

493
void CppModelManager::unregisterCppEditorDocument(const QString &filePath)
494 495
{
    QTC_ASSERT(!filePath.isEmpty(), return);
496

497 498
    static short closedCppDocuments = 0;
    int openCppDocuments = 0;
499

500
    {
501 502 503 504
        QMutexLocker locker(&d->m_cppEditorDocumentsMutex);
        QTC_ASSERT(d->m_cppEditorDocuments.value(filePath, 0), return);
        QTC_CHECK(d->m_cppEditorDocuments.remove(filePath) == 1);
        openCppDocuments = d->m_cppEditorDocuments.size();
505 506
    }

507 508 509
    ++closedCppDocuments;
    if (openCppDocuments == 0 || closedCppDocuments == 5) {
        closedCppDocuments = 0;
510
        delayedGC();
511 512 513
    }
}

514
QList<int> CppModelManager::references(Symbol *symbol, const LookupContext &context)
515
{
516
    return d->m_findReferences->references(symbol, context);
517 518
}

519
void CppModelManager::findUsages(Symbol *symbol, const LookupContext &context)
Roberto Raggi's avatar
Roberto Raggi committed
520
{
521
    if (symbol->identifier())
522
        d->m_findReferences->findUsages(symbol, context);
523 524
}

525 526
void CppModelManager::renameUsages(Symbol *symbol,
                                   const LookupContext &context,
527
                                   const QString &replacement)
528 529
{
    if (symbol->identifier())
530
        d->m_findReferences->renameUsages(symbol, context, replacement);
Roberto Raggi's avatar
Roberto Raggi committed
531 532
}

533
void CppModelManager::findMacroUsages(const Macro &macro)
Christian Kamm's avatar
Christian Kamm committed
534
{
535
    d->m_findReferences->findMacroUses(macro);
Christian Kamm's avatar
Christian Kamm committed
536 537
}

538
void CppModelManager::renameMacroUsages(const Macro &macro, const QString &replacement)
539
{
540
    d->m_findReferences->renameMacroUses(macro, replacement);
541 542
}

543
void CppModelManager::replaceSnapshot(const Snapshot &newSnapshot)
544
{
545 546
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
    d->m_snapshot = newSnapshot;
547 548
}

549
WorkingCopy CppModelManager::buildWorkingCopyList()
con's avatar
con committed
550
{
551
    WorkingCopy workingCopy;
552

553 554 555 556 557
    foreach (const CppEditorDocumentHandle *cppEditorDocument, cppEditorDocuments()) {
        workingCopy.insert(cppEditorDocument->filePath(),
                           cppEditorDocument->contents(),
                           cppEditorDocument->revision());
    }
con's avatar
con committed
558

559
    QSetIterator<AbstractEditorSupport *> it(d->m_extraEditorSupports);
560 561
    while (it.hasNext()) {
        AbstractEditorSupport *es = it.next();
562
        workingCopy.insert(es->fileName(), es->contents(), es->revision());
563 564
    }

565
    // Add the project configuration file
566
    QByteArray conf = codeModelConfiguration();
con's avatar
con committed
567
    conf += definedMacros();
568
    workingCopy.insert(configurationFileName(), conf);
con's avatar
con committed
569 570 571 572

    return workingCopy;
}

573
WorkingCopy CppModelManager::workingCopy() const
574 575 576 577
{
    return const_cast<CppModelManager *>(this)->buildWorkingCopyList();
}

578 579 580 581 582
QByteArray CppModelManager::codeModelConfiguration() const
{
    return QByteArray::fromRawData(pp_configuration, qstrlen(pp_configuration));
}

583
QFuture<void> CppModelManager::updateSourceFiles(const QSet<QString> &sourceFiles,
584
                                                 ProgressNotificationMode mode)
585
{
586
    if (sourceFiles.isEmpty() || !d->m_indexerEnabled)
587 588
        return QFuture<void>();

589 590 591
    if (d->m_indexingSupporter)
        d->m_indexingSupporter->refreshSourceFiles(sourceFiles, mode);
    return d->m_internalIndexingSupport->refreshSourceFiles(sourceFiles, mode);
592
}
con's avatar
con committed
593

594
QList<ProjectInfo> CppModelManager::projectInfos() const
595
{
596 597
    QMutexLocker locker(&d->m_projectMutex);
    return d->m_projectToProjectsInfo.values();
598 599
}

600
ProjectInfo CppModelManager::projectInfo(ProjectExplorer::Project *project) const
601
{
602
    QMutexLocker locker(&d->m_projectMutex);
603
    return d->m_projectToProjectsInfo.value(project, ProjectInfo());
604 605
}

606 607 608 609 610 611
/// \brief Remove all files and their includes (recursively) of given ProjectInfo from the snapshot.
void CppModelManager::removeProjectInfoFilesAndIncludesFromSnapshot(const ProjectInfo &projectInfo)
{
    if (!projectInfo.isValid())
        return;

612
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
613 614
    foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
        foreach (const ProjectFile &cxxFile, projectPart->files) {
615 616 617
            foreach (const QString &fileName, d->m_snapshot.allIncludesForDocument(cxxFile.path))
                d->m_snapshot.remove(fileName);
            d->m_snapshot.remove(cxxFile.path);
618 619 620 621
        }
    }
}

622
QList<CppEditorDocumentHandle *> CppModelManager::cppEditorDocuments() const
623
{
624 625
    QMutexLocker locker(&d->m_cppEditorDocumentsMutex);
    return d->m_cppEditorDocuments.values();
626 627
}

628 629 630
/// \brief Remove all given files from the snapshot.
void CppModelManager::removeFilesFromSnapshot(const QSet<QString> &filesToRemove)
{
631
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
632 633
    QSetIterator<QString> i(filesToRemove);
    while (i.hasNext())
634
        d->m_snapshot.remove(i.next());
635 636 637
}

class ProjectInfoComparer
638
{
639
public:
640 641
    ProjectInfoComparer(const ProjectInfo &oldProjectInfo,
                        const ProjectInfo &newProjectInfo)
642
        : m_old(oldProjectInfo)
643
        , m_oldSourceFiles(oldProjectInfo.sourceFiles())
644
        , m_new(newProjectInfo)
645
        , m_newSourceFiles(newProjectInfo.sourceFiles())
646 647
    {}

648 649 650
    bool definesChanged() const { return m_new.definesChanged(m_old); }
    bool configurationChanged() const { return m_new.configurationChanged(m_old); }
    bool configurationOrFilesChanged() const { return m_new.configurationOrFilesChanged(m_old); }
651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679

    QSet<QString> addedFiles() const
    {
        QSet<QString> addedFilesSet = m_newSourceFiles;
        addedFilesSet.subtract(m_oldSourceFiles);
        return addedFilesSet;
    }

    QSet<QString> removedFiles() const
    {
        QSet<QString> removedFilesSet = m_oldSourceFiles;
        removedFilesSet.subtract(m_newSourceFiles);
        return removedFilesSet;
    }

    /// Returns a list of common files that have a changed timestamp.
    QSet<QString> timeStampModifiedFiles(const Snapshot &snapshot) const
    {
        QSet<QString> commonSourceFiles = m_newSourceFiles;
        commonSourceFiles.intersect(m_oldSourceFiles);

        QList<Document::Ptr> documentsToCheck;
        QSetIterator<QString> i(commonSourceFiles);
        while (i.hasNext()) {
            const QString file = i.next();
            if (Document::Ptr document = snapshot.document(file))
                documentsToCheck << document;
        }

680
        return CppModelManager::timeStampModifiedFiles(documentsToCheck);
681 682 683
    }

private:
684
    const ProjectInfo &m_old;
685 686
    const QSet<QString> m_oldSourceFiles;

687
    const ProjectInfo &m_new;
688 689 690
    const QSet<QString> m_newSourceFiles;
};

691 692 693
/// Make sure that m_projectMutex is locked when calling this.
void CppModelManager::recalculateFileToProjectParts()
{
694 695 696
    d->m_projectFileToProjectPart.clear();
    d->m_fileToProjectParts.clear();
    foreach (const ProjectInfo &projectInfo, d->m_projectToProjectsInfo) {
697
        foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
698
            d->m_projectFileToProjectPart[projectPart->projectFile] = projectPart;
699
            foreach (const ProjectFile &cxxFile, projectPart->files)
700 701
                d->m_fileToProjectParts[Utils::FileName::fromString(cxxFile.path)].append(
                            projectPart);
702

703 704 705 706
        }
    }
}

707
void CppModelManager::updateCppEditorDocuments() const
708
{
709 710
    // Refresh visible documents
    QSet<Core::IDocument *> visibleCppEditorDocuments;
711
    foreach (Core::IEditor *editor, Core::EditorManager::visibleEditors()) {
712
        if (Core::IDocument *document = editor->document()) {
713 714
            const QString filePath = document->filePath().toString();
            if (CppEditorDocumentHandle *theCppEditorDocument = cppEditorDocument(filePath)) {
715
                visibleCppEditorDocuments.insert(document);
716
                theCppEditorDocument->processor()->run();
717
            }
718 719 720
        }
    }

721 722 723 724 725
    // Mark invisible documents dirty
    QSet<Core::IDocument *> invisibleCppEditorDocuments
        = Core::DocumentModel::openedDocuments().toSet();
    invisibleCppEditorDocuments.subtract(visibleCppEditorDocuments);
    foreach (Core::IDocument *document, invisibleCppEditorDocuments) {
726 727 728
        const QString filePath = document->filePath().toString();
        if (CppEditorDocumentHandle *theCppEditorDocument = cppEditorDocument(filePath))
            theCppEditorDocument->setNeedsRefresh(true);
729 730 731
    }
}

732 733 734
QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo &newProjectInfo)
{
    if (!newProjectInfo.isValid())
735
        return QFuture<void>();
736

737
    QSet<QString> filesToReindex;
738 739
    bool filesRemoved = false;

740
    { // Only hold the mutex for a limited scope, so the dumping afterwards does not deadlock.
741
        QMutexLocker projectLocker(&d->m_projectMutex);
742 743

        ProjectExplorer::Project *project = newProjectInfo.project().data();
744
        const QSet<QString> newSourceFiles = newProjectInfo.sourceFiles();
745

746
        // Check if we can avoid a full reindexing
747
        ProjectInfo oldProjectInfo = d->m_projectToProjectsInfo.value(project);
748
        if (oldProjectInfo.isValid()) {
749
            ProjectInfoComparer comparer(oldProjectInfo, newProjectInfo);
750 751 752 753

            if (!comparer.configurationOrFilesChanged()) {
                // Some other attached data might have changed
                d->m_projectToProjectsInfo.insert(project, newProjectInfo);
754
                return QFuture<void>();
755
            }
756

757 758 759
            // If the project configuration changed, do a full reindexing
            if (comparer.configurationChanged()) {
                removeProjectInfoFilesAndIncludesFromSnapshot(oldProjectInfo);
760
                filesToReindex.unite(newSourceFiles);
761 762 763

                // The "configuration file" includes all defines and therefore should be updated
                if (comparer.definesChanged()) {
764 765
                    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
                    d->m_snapshot.remove(configurationFileName());
766
                }
767 768 769 770

            // Otherwise check for added and modified files
            } else {
                const QSet<QString> addedFiles = comparer.addedFiles();
771
                filesToReindex.unite(addedFiles);
772 773

                const QSet<QString> modifiedFiles = comparer.timeStampModifiedFiles(snapshot());
774
                filesToReindex.unite(modifiedFiles);
775
            }
776 777 778 779 780 781 782 783 784 785 786

            // Announce and purge the removed files from the snapshot
            const QSet<QString> removedFiles = comparer.removedFiles();
            if (!removedFiles.isEmpty()) {
                filesRemoved = true;
                emit aboutToRemoveFiles(removedFiles.toList());
                removeFilesFromSnapshot(removedFiles);
            }

        // A new project was opened/created, do a full indexing
        } else {
787
            filesToReindex.unite(newSourceFiles);
788 789
        }

790
        // Update Project/ProjectInfo and File/ProjectPart table
791 792
        d->m_dirty = true;
        d->m_projectToProjectsInfo.insert(project, newProjectInfo);
793
        recalculateFileToProjectParts();
794

795 796 797
    } // Mutex scope

    // If requested, dump everything we got
798
    if (DumpProjectInfo)
799
        dumpModelManagerConfiguration(QLatin1String("updateProjectInfo"));
800

801 802 803 804 805
    // Remove files from snapshot that are not reachable any more
    if (filesRemoved)
        GC();

    emit projectPartsUpdated(newProjectInfo.project().data());
806

807 808 809 810
    // Ideally, we would update all the editor documents that depend on the 'filesToReindex'.
    // However, on e.g. a session restore first the editor documents are created and then the
    // project updates come in. That is, there are no reasonable dependency tables based on
    // resolved includes that we could rely on.
811
    updateCppEditorDocuments();
812

813 814
    // Trigger reindexing
    return updateSourceFiles(filesToReindex, ForcedProgressNotification);
815 816
}

817 818
ProjectPart::Ptr CppModelManager::projectPartForProjectFile(const QString &projectFile) const
{
819
    return d->m_projectFileToProjectPart.value(projectFile);
820 821
}

822
QList<ProjectPart::Ptr> CppModelManager::projectPart(const Utils::FileName &fileName) const
823
{
824 825
    QMutexLocker locker(&d->m_projectMutex);
    return d->m_fileToProjectParts.value(fileName);
826
}
827

828 829
QList<ProjectPart::Ptr> CppModelManager::projectPartFromDependencies(
        const Utils::FileName &fileName) const
830 831
{
    QSet<ProjectPart::Ptr> parts;
832
    const Utils::FileNameList deps = snapshot().filesDependingOn(fileName);
833

834 835
    QMutexLocker locker(&d->m_projectMutex);
    foreach (const Utils::FileName &dep, deps) {
836
        parts.unite(QSet<ProjectPart::Ptr>::fromList(d->m_fileToProjectParts.value(dep)));
837
    }
838

839
    return parts.values();
840
}
con's avatar
con committed
841

842 843 844 845
ProjectPart::Ptr CppModelManager::fallbackProjectPart() const
{
    ProjectPart::Ptr part(new ProjectPart);

846 847
    part->projectDefines = d->m_definedMacros;
    part->headerPaths = d->m_headerPaths;
848 849
    part->languageVersion = ProjectPart::CXX14;
    part->languageExtensions = ProjectPart::AllExtensions;
850
    part->qtVersion = ProjectPart::Qt5;
851
    part->updateLanguageFeatures();
852 853 854 855

    return part;
}

con's avatar
con committed
856 857
bool CppModelManager::isCppEditor(Core::IEditor *editor) const
{
858
    return editor->context().contains(ProjectExplorer::Constants::LANG_CXX);
con's avatar
con committed
859 860 861 862
}

void CppModelManager::emitDocumentUpdated(Document::Ptr doc)
{
863
    if (replaceDocument(doc))
864
        emit documentUpdated(doc);