cppmodelmanager.cpp 41.3 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 "cpptoolsreuse.h"
46
#include "editordocumenthandle.h"
con's avatar
con committed
47

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

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

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

73 74
Q_DECLARE_METATYPE(QSet<QString>)

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

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

81
#ifdef QTCREATOR_WITH_DUMP_AST
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 120

#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
121
#endif // QTCREATOR_WITH_DUMP_AST
122

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

static QMutex m_instanceMutex;
static CppModelManager *m_instance;

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

    // Project integration
    mutable QMutex m_projectMutex;
    QMap<ProjectExplorer::Project *, ProjectInfo> m_projectToProjectsInfo;
139
    QMap<Utils::FileName, QList<ProjectPart::Ptr> > m_fileToProjectParts;
140
    QMap<QString, ProjectPart::Ptr> m_projectPartIdToProjectProjectPart;
141 142 143 144 145 146 147
    // 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
148 149
    mutable QMutex m_cppEditorDocumentsMutex;
    QMap<QString, CppEditorDocumentHandle *> m_cppEditorDocuments;
150 151 152
    QSet<AbstractEditorSupport *> m_extraEditorSupports;

    // Completion & highlighting
153 154 155 156
    ModelManagerSupportProviderInternal m_modelManagerSupportInternalProvider;
    ModelManagerSupport::Ptr m_modelManagerSupportInternal;
    QHash<QString, ModelManagerSupportProvider *> m_availableModelManagerSupports;
    QHash<QString, ModelManagerSupport::Ptr> m_activeModelManagerSupports;
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171

    // 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
172
    "# 1 \"<configuration>\"\n"
173
    "#define Q_CREATOR_RUN 1\n"
con's avatar
con committed
174 175 176 177 178 179
    "#define __cplusplus 1\n"
    "#define __extension__\n"
    "#define __context__\n"
    "#define __range__\n"
    "#define   restrict\n"
    "#define __restrict\n"
180
    "#define __restrict__\n"
con's avatar
con committed
181

182 183 184 185
    "#define __complex__\n"
    "#define __imag__\n"
    "#define __real__\n"

186 187
    "#define __builtin_va_arg(a,b) ((b)0)\n"

188 189
    "#define _Pragma(x)\n" // C99 _Pragma operator

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

207
QSet<QString> CppModelManager::timeStampModifiedFiles(const QList<Document::Ptr> &documentsToCheck)
208
{
209
    QSet<QString> sourceFiles;
210

211
    foreach (const Document::Ptr doc, documentsToCheck) {
212 213
        const QDateTime lastModified = doc->lastModified();

214
        if (!lastModified.isNull()) {
215 216 217
            QFileInfo fileInfo(doc->fileName());

            if (fileInfo.exists() && fileInfo.lastModified() != lastModified)
218
                sourceFiles.insert(doc->fileName());
219 220 221
        }
    }

222 223 224
    return sourceFiles;
}

225 226 227 228 229 230 231 232 233 234 235 236
/*!
 * \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
237
    return new CppSourceProcessor(that->snapshot(), [that](const Document::Ptr &doc) {
238 239 240 241 242
        const Document::Ptr previousDocument = that->document(doc->fileName());
        const unsigned newRevision = previousDocument.isNull()
                ? 1U
                : previousDocument->revision() + 1;
        doc->setRevision(newRevision);
243 244 245 246 247
        that->emitDocumentUpdated(doc);
        doc->releaseSourceAndAST();
    });
}

248 249 250 251 252 253 254
QString CppModelManager::editorConfigurationFileName()
{
    return QLatin1String("<per-editor-defines>");
}

QString CppModelManager::configurationFileName()
{
255
    return Preprocessor::configurationFileName;
256 257
}

258 259 260 261 262 263 264
void CppModelManager::updateModifiedSourceFiles()
{
    const Snapshot snapshot = this->snapshot();
    QList<Document::Ptr> documentsToCheck;
    foreach (const Document::Ptr document, snapshot)
        documentsToCheck << document;

265
    updateSourceFiles(timeStampModifiedFiles(documentsToCheck));
266 267
}

con's avatar
con committed
268 269
/*!
    \class CppTools::CppModelManager
270 271 272
    \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
273

274 275 276 277
    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
278 279
*/

280 281
CppModelManager *CppModelManager::instance()
{
282 283 284 285 286 287 288 289
    if (m_instance)
        return m_instance;

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

    return m_instance;
290 291
}

292
CppModelManager::CppModelManager(QObject *parent)
293
    : CppModelManagerBase(parent), d(new CppModelManagerPrivate)
con's avatar
con committed
294
{
295 296 297
    d->m_indexingSupporter = 0;
    d->m_enableGC = true;

298
    qRegisterMetaType<QSet<QString> >();
299
    connect(this, SIGNAL(sourceFilesRefreshed(QSet<QString>)),
300
            this, SLOT(onSourceFilesRefreshed()));
Erik Verbruggen's avatar
Erik Verbruggen committed
301

302 303
    d->m_findReferences = new CppFindReferences(this);
    d->m_indexerEnabled = qgetenv("QTC_NO_CODE_INDEXER") != "1";
Roberto Raggi's avatar
Roberto Raggi committed
304

305
    d->m_dirty = true;
306

307 308 309
    d->m_delayedGcTimer.setObjectName(QLatin1String("CppModelManager::m_delayedGcTimer"));
    d->m_delayedGcTimer.setSingleShot(true);
    connect(&d->m_delayedGcTimer, SIGNAL(timeout()), this, SLOT(GC()));
310

hjk's avatar
hjk committed
311 312
    QObject *sessionManager = ProjectExplorer::SessionManager::instance();
    connect(sessionManager, SIGNAL(projectAdded(ProjectExplorer::Project*)),
313
            this, SLOT(onProjectAdded(ProjectExplorer::Project*)));
hjk's avatar
hjk committed
314
    connect(sessionManager, SIGNAL(aboutToRemoveProject(ProjectExplorer::Project*)),
Robert Loehning's avatar
Robert Loehning committed
315
            this, SLOT(onAboutToRemoveProject(ProjectExplorer::Project*)));
hjk's avatar
hjk committed
316
    connect(sessionManager, SIGNAL(aboutToLoadSession(QString)),
317
            this, SLOT(onAboutToLoadSession()));
hjk's avatar
hjk committed
318
    connect(sessionManager, SIGNAL(aboutToUnloadSession(QString)),
319
            this, SLOT(onAboutToUnloadSession()));
320 321 322 323

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

324 325
    connect(Core::DocumentManager::instance(), &Core::DocumentManager::allDocumentsRenamed,
            this, &CppModelManager::renameIncludes);
con's avatar
con committed
326

327 328 329
    connect(Core::ICore::instance(), SIGNAL(coreAboutToClose()),
            this, SLOT(onCoreAboutToClose()));

con's avatar
con committed
330
    qRegisterMetaType<CPlusPlus::Document::Ptr>("CPlusPlus::Document::Ptr");
331
    qRegisterMetaType<QList<Document::DiagnosticMessage>>(
332
                "QList<CPlusPlus::Document::DiagnosticMessage>");
con's avatar
con committed
333

334 335 336 337 338 339 340 341 342 343 344
    QSharedPointer<CppCodeModelSettings> codeModelSettings
            = CppToolsPlugin::instance()->codeModelSettings();
    codeModelSettings->setDefaultId(d->m_modelManagerSupportInternalProvider.id());
    connect(codeModelSettings.data(), &CppCodeModelSettings::changed,
            this, &CppModelManager::onCodeModelSettingsChanged);

    d->m_modelManagerSupportInternal
            = d->m_modelManagerSupportInternalProvider.createModelManagerSupport();
    d->m_activeModelManagerSupports.insert(d->m_modelManagerSupportInternalProvider.id(),
                                           d->m_modelManagerSupportInternal);
    addModelManagerSupportProvider(&d->m_modelManagerSupportInternalProvider);
345

346
    d->m_internalIndexingSupport = new BuiltinIndexingSupport;
con's avatar
con committed
347 348 349
}

CppModelManager::~CppModelManager()
350
{
351
    delete d->m_internalIndexingSupport;
352
    delete d;
353
}
con's avatar
con committed
354

355
Snapshot CppModelManager::snapshot() const
356
{
357 358
    QMutexLocker locker(&d->m_snapshotMutex);
    return d->m_snapshot;
359
}
con's avatar
con committed
360

361 362
Document::Ptr CppModelManager::document(const QString &fileName) const
{
363 364
    QMutexLocker locker(&d->m_snapshotMutex);
    return d->m_snapshot.document(fileName);
365 366 367 368 369 370 371
}

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

374
    Document::Ptr previous = d->m_snapshot.document(newDoc->fileName());
375 376 377 378
    if (previous && (newDoc->revision() != 0 && newDoc->revision() < previous->revision()))
        // the new document is outdated
        return false;

379
    d->m_snapshot.insert(newDoc);
380 381 382
    return true;
}

383 384
void CppModelManager::ensureUpdated()
{
385 386
    QMutexLocker locker(&d->m_projectMutex);
    if (!d->m_dirty)
387 388
        return;

389 390 391 392
    d->m_projectFiles = internalProjectFiles();
    d->m_headerPaths = internalHeaderPaths();
    d->m_definedMacros = internalDefinedMacros();
    d->m_dirty = false;
393 394
}

395
QStringList CppModelManager::internalProjectFiles() const
con's avatar
con committed
396 397
{
    QStringList files;
398
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(d->m_projectToProjectsInfo);
con's avatar
con committed
399 400
    while (it.hasNext()) {
        it.next();
401
        const ProjectInfo pinfo = it.value();
402
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
403 404
            foreach (const ProjectFile &file, part->files)
                files += file.path;
405
        }
con's avatar
con committed
406
    }
407
    files.removeDuplicates();
con's avatar
con committed
408 409 410
    return files;
}

411
ProjectPart::HeaderPaths CppModelManager::internalHeaderPaths() const
con's avatar
con committed
412
{
413
    ProjectPart::HeaderPaths headerPaths;
414
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(d->m_projectToProjectsInfo);
con's avatar
con committed
415 416
    while (it.hasNext()) {
        it.next();
417
        const ProjectInfo pinfo = it.value();
418 419
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
            foreach (const ProjectPart::HeaderPath &path, part->headerPaths) {
420
                const ProjectPart::HeaderPath hp(QDir::cleanPath(path.path), path.type);
421 422 423 424
                if (!headerPaths.contains(hp))
                    headerPaths += hp;
            }
        }
con's avatar
con committed
425
    }
426
    return headerPaths;
con's avatar
con committed
427 428
}

429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
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);
        }
    }
}

445
QByteArray CppModelManager::internalDefinedMacros() const
con's avatar
con committed
446 447
{
    QByteArray macros;
448
    QSet<QByteArray> alreadyIn;
449
    QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(d->m_projectToProjectsInfo);
con's avatar
con committed
450 451
    while (it.hasNext()) {
        it.next();
452
        const ProjectInfo pinfo = it.value();
453
        foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
454 455
            addUnique(part->toolchainDefines.split('\n'), &macros, &alreadyIn);
            addUnique(part->projectDefines.split('\n'), &macros, &alreadyIn);
456
            if (!part->projectConfigFile.isEmpty())
457
                macros += ProjectPart::readProjectConfigFile(part);
458
        }
con's avatar
con committed
459 460 461 462
    }
    return macros;
}

463
/// This function will acquire mutexes!
464
void CppModelManager::dumpModelManagerConfiguration(const QString &logFileId)
465
{
466 467 468 469
    const Snapshot globalSnapshot = snapshot();
    const QString globalSnapshotTitle
        = QString::fromLatin1("Global/Indexing Snapshot (%1 Documents)").arg(globalSnapshot.size());

470
    CppCodeModelInspector::Dumper dumper(globalSnapshot, logFileId);
471 472 473
    dumper.dumpProjectInfos(projectInfos());
    dumper.dumpSnapshot(globalSnapshot, globalSnapshotTitle, /*isGlobalSnapshot=*/ true);
    dumper.dumpWorkingCopy(workingCopy());
474
    ensureUpdated();
475
    dumper.dumpMergedEntities(d->m_headerPaths, d->m_definedMacros);
476 477
}

478 479 480 481 482
QSet<AbstractEditorSupport *> CppModelManager::abstractEditorSupports() const
{
    return d->m_extraEditorSupports;
}

483
void CppModelManager::addExtraEditorSupport(AbstractEditorSupport *editorSupport)
484
{
485
    d->m_extraEditorSupports.insert(editorSupport);
486 487
}

488
void CppModelManager::removeExtraEditorSupport(AbstractEditorSupport *editorSupport)
489
{
490
    d->m_extraEditorSupports.remove(editorSupport);
491 492
}

493
CppEditorDocumentHandle *CppModelManager::cppEditorDocument(const QString &filePath) const
494
{
495 496
    if (filePath.isEmpty())
        return 0;
497

498 499
    QMutexLocker locker(&d->m_cppEditorDocumentsMutex);
    return d->m_cppEditorDocuments.value(filePath, 0);
500 501
}

502
void CppModelManager::registerCppEditorDocument(CppEditorDocumentHandle *editorDocument)
503
{
504 505 506
    QTC_ASSERT(editorDocument, return);
    const QString filePath = editorDocument->filePath();
    QTC_ASSERT(!filePath.isEmpty(), return);
507

508 509 510
    QMutexLocker locker(&d->m_cppEditorDocumentsMutex);
    QTC_ASSERT(d->m_cppEditorDocuments.value(filePath, 0) == 0, return);
    d->m_cppEditorDocuments.insert(filePath, editorDocument);
511
}
512

513
void CppModelManager::unregisterCppEditorDocument(const QString &filePath)
514 515
{
    QTC_ASSERT(!filePath.isEmpty(), return);
516

517 518
    static short closedCppDocuments = 0;
    int openCppDocuments = 0;
519

520
    {
521 522 523 524
        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();
525 526
    }

527 528 529
    ++closedCppDocuments;
    if (openCppDocuments == 0 || closedCppDocuments == 5) {
        closedCppDocuments = 0;
530
        delayedGC();
531 532 533
    }
}

534
QList<int> CppModelManager::references(Symbol *symbol, const LookupContext &context)
535
{
536
    return d->m_findReferences->references(symbol, context);
537 538
}

539
void CppModelManager::findUsages(Symbol *symbol, const LookupContext &context)
Roberto Raggi's avatar
Roberto Raggi committed
540
{
541
    if (symbol->identifier())
542
        d->m_findReferences->findUsages(symbol, context);
543 544
}

545 546
void CppModelManager::renameUsages(Symbol *symbol,
                                   const LookupContext &context,
547
                                   const QString &replacement)
548 549
{
    if (symbol->identifier())
550
        d->m_findReferences->renameUsages(symbol, context, replacement);
Roberto Raggi's avatar
Roberto Raggi committed
551 552
}

553
void CppModelManager::findMacroUsages(const Macro &macro)
Christian Kamm's avatar
Christian Kamm committed
554
{
555
    d->m_findReferences->findMacroUses(macro);
Christian Kamm's avatar
Christian Kamm committed
556 557
}

558
void CppModelManager::renameMacroUsages(const Macro &macro, const QString &replacement)
559
{
560
    d->m_findReferences->renameMacroUses(macro, replacement);
561 562
}

563
void CppModelManager::replaceSnapshot(const Snapshot &newSnapshot)
564
{
565 566
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
    d->m_snapshot = newSnapshot;
567 568
}

569
WorkingCopy CppModelManager::buildWorkingCopyList()
con's avatar
con committed
570
{
571
    WorkingCopy workingCopy;
572

573 574 575 576 577
    foreach (const CppEditorDocumentHandle *cppEditorDocument, cppEditorDocuments()) {
        workingCopy.insert(cppEditorDocument->filePath(),
                           cppEditorDocument->contents(),
                           cppEditorDocument->revision());
    }
con's avatar
con committed
578

579
    QSetIterator<AbstractEditorSupport *> it(d->m_extraEditorSupports);
580 581
    while (it.hasNext()) {
        AbstractEditorSupport *es = it.next();
582
        workingCopy.insert(es->fileName(), es->contents(), es->revision());
583 584
    }

585
    // Add the project configuration file
586
    QByteArray conf = codeModelConfiguration();
con's avatar
con committed
587
    conf += definedMacros();
588
    workingCopy.insert(configurationFileName(), conf);
con's avatar
con committed
589 590 591 592

    return workingCopy;
}

593
WorkingCopy CppModelManager::workingCopy() const
594 595 596 597
{
    return const_cast<CppModelManager *>(this)->buildWorkingCopyList();
}

598 599 600 601 602
QByteArray CppModelManager::codeModelConfiguration() const
{
    return QByteArray::fromRawData(pp_configuration, qstrlen(pp_configuration));
}

603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
static QSet<QString> tooBigFilesRemoved(const QSet<QString> &files, int fileSizeLimit)
{
    if (fileSizeLimit == 0)
        return files;

    QSet<QString> result;
    QFileInfo fileInfo;

    QSetIterator<QString> i(files);
    while (i.hasNext()) {
        const QString filePath = i.next();
        fileInfo.setFile(filePath);
        if (skipFileDueToSizeLimit(fileInfo), fileSizeLimit)
            continue;

        result << filePath;
    }

    return result;
}

624
QFuture<void> CppModelManager::updateSourceFiles(const QSet<QString> &sourceFiles,
625
                                                 ProgressNotificationMode mode)
626
{
627
    if (sourceFiles.isEmpty() || !d->m_indexerEnabled)
628 629
        return QFuture<void>();

630 631
    const auto filteredFiles = tooBigFilesRemoved(sourceFiles, fileSizeLimit());

632
    if (d->m_indexingSupporter)
633 634
        d->m_indexingSupporter->refreshSourceFiles(filteredFiles, mode);
    return d->m_internalIndexingSupport->refreshSourceFiles(filteredFiles, mode);
635
}
con's avatar
con committed
636

637
QList<ProjectInfo> CppModelManager::projectInfos() const
638
{
639 640
    QMutexLocker locker(&d->m_projectMutex);
    return d->m_projectToProjectsInfo.values();
641 642
}

643
ProjectInfo CppModelManager::projectInfo(ProjectExplorer::Project *project) const
644
{
645
    QMutexLocker locker(&d->m_projectMutex);
646
    return d->m_projectToProjectsInfo.value(project, ProjectInfo());
647 648
}

649 650 651 652 653 654
/// \brief Remove all files and their includes (recursively) of given ProjectInfo from the snapshot.
void CppModelManager::removeProjectInfoFilesAndIncludesFromSnapshot(const ProjectInfo &projectInfo)
{
    if (!projectInfo.isValid())
        return;

655
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
656 657
    foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
        foreach (const ProjectFile &cxxFile, projectPart->files) {
658 659 660
            foreach (const QString &fileName, d->m_snapshot.allIncludesForDocument(cxxFile.path))
                d->m_snapshot.remove(fileName);
            d->m_snapshot.remove(cxxFile.path);
661 662 663 664
        }
    }
}

665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697
void CppModelManager::handleAddedModelManagerSupports(const QSet<QString> &supportIds)
{
    foreach (const QString &id, supportIds) {
        ModelManagerSupportProvider * const provider = d->m_availableModelManagerSupports.value(id);
        if (provider) {
            QTC_CHECK(!d->m_activeModelManagerSupports.contains(id));
            d->m_activeModelManagerSupports.insert(id, provider->createModelManagerSupport());
        }
    }
}

QList<ModelManagerSupport::Ptr> CppModelManager::handleRemovedModelManagerSupports(
        const QSet<QString> &supportIds)
{
    QList<ModelManagerSupport::Ptr> removed;

    foreach (const QString &id, supportIds) {
        const ModelManagerSupport::Ptr support = d->m_activeModelManagerSupports.value(id);
        d->m_activeModelManagerSupports.remove(id);
        removed << support;
    }

    return removed;
}

void CppModelManager::closeCppEditorDocuments()
{
    QList<Core::IDocument *> cppDocumentsToClose;
    foreach (CppEditorDocumentHandle *cppDocument, cppEditorDocuments())
        cppDocumentsToClose << cppDocument->processor()->baseTextDocument();
    QTC_CHECK(Core::EditorManager::closeDocuments(cppDocumentsToClose));
}

698
QList<CppEditorDocumentHandle *> CppModelManager::cppEditorDocuments() const
699
{
700 701
    QMutexLocker locker(&d->m_cppEditorDocumentsMutex);
    return d->m_cppEditorDocuments.values();
702 703
}

704 705 706
/// \brief Remove all given files from the snapshot.
void CppModelManager::removeFilesFromSnapshot(const QSet<QString> &filesToRemove)
{
707
    QMutexLocker snapshotLocker(&d->m_snapshotMutex);
708 709
    QSetIterator<QString> i(filesToRemove);
    while (i.hasNext())
710
        d->m_snapshot.remove(i.next());
711 712
}

713
static QStringList projectPartIds(const QSet<ProjectPart::Ptr> &projectParts)
714 715 716 717
{
    QStringList result;
    QSetIterator<ProjectPart::Ptr> it(projectParts);
    while (it.hasNext())
718
        result << it.next()->id();
719 720 721
    return result;
}

722
class ProjectInfoComparer
723
{
724
public:
725 726
    ProjectInfoComparer(const ProjectInfo &oldProjectInfo,
                        const ProjectInfo &newProjectInfo)
727
        : m_old(oldProjectInfo)
728
        , m_oldSourceFiles(oldProjectInfo.sourceFiles())
729
        , m_new(newProjectInfo)
730
        , m_newSourceFiles(newProjectInfo.sourceFiles())
731 732
    {}

733 734 735
    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); }
736 737 738 739 740 741 742 743 744 745 746 747 748 749 750

    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;
    }

751 752 753 754
    QStringList removedProjectParts()
    {
        QSet<ProjectPart::Ptr> removed = m_old.projectParts().toSet();
        removed.subtract(m_new.projectParts().toSet());
755
        return projectPartIds(removed);
756 757
    }

758 759 760 761 762 763 764 765 766 767 768 769 770 771
    /// 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;
        }

772
        return CppModelManager::timeStampModifiedFiles(documentsToCheck);
773 774 775
    }

private:
776
    const ProjectInfo &m_old;
777 778
    const QSet<QString> m_oldSourceFiles;

779
    const ProjectInfo &m_new;
780 781 782
    const QSet<QString> m_newSourceFiles;
};

783
/// Make sure that m_projectMutex is locked when calling this.
784
void CppModelManager::recalculateProjectPartMappings()
785
{
786
    d->m_projectPartIdToProjectProjectPart.clear();
787 788
    d->m_fileToProjectParts.clear();
    foreach (const ProjectInfo &projectInfo, d->m_projectToProjectsInfo) {
789
        foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
790
            d->m_projectPartIdToProjectProjectPart[projectPart->id()] = projectPart;
791
            foreach (const ProjectFile &cxxFile, projectPart->files)
792 793
                d->m_fileToProjectParts[Utils::FileName::fromString(cxxFile.path)].append(
                            projectPart);
794

795 796 797 798
        }
    }
}

799
void CppModelManager::updateCppEditorDocuments() const
800
{
801 802
    // Refresh visible documents
    QSet<Core::IDocument *> visibleCppEditorDocuments;
803
    foreach (Core::IEditor *editor, Core::EditorManager::visibleEditors()) {
804
        if (Core::IDocument *document = editor->document()) {
805 806
            const QString filePath = document->filePath().toString();
            if (CppEditorDocumentHandle *theCppEditorDocument = cppEditorDocument(filePath)) {
807
                visibleCppEditorDocuments.insert(document);
808
                theCppEditorDocument->processor()->run();
809
            }
810 811 812
        }
    }

813 814 815 816 817
    // Mark invisible documents dirty
    QSet<Core::IDocument *> invisibleCppEditorDocuments
        = Core::DocumentModel::openedDocuments().toSet();
    invisibleCppEditorDocuments.subtract(visibleCppEditorDocuments);
    foreach (Core::IDocument *document, invisibleCppEditorDocuments) {
818 819 820
        const QString filePath = document->filePath().toString();
        if (CppEditorDocumentHandle *theCppEditorDocument = cppEditorDocument(filePath))
            theCppEditorDocument->setNeedsRefresh(true);
821 822 823
    }
}

824 825 826
QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo &newProjectInfo)
{
    if (!newProjectInfo.isValid())
827
        return QFuture<void>();
828

829
    QSet<QString> filesToReindex;
830 831
    bool filesRemoved = false;

832
    { // Only hold the mutex for a limited scope, so the dumping afterwards does not deadlock.
833
        QMutexLocker projectLocker(&d->m_projectMutex);
834 835

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

838
        // Check if we can avoid a full reindexing
839
        ProjectInfo oldProjectInfo = d->m_projectToProjectsInfo.value(project);
840
        if (oldProjectInfo.isValid()) {
841
            ProjectInfoComparer comparer(oldProjectInfo, newProjectInfo);
842

843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863
            if (comparer.configurationOrFilesChanged()) {
                d->m_dirty = true;

                // If the project configuration changed, do a full reindexing
                if (comparer.configurationChanged()) {
                    removeProjectInfoFilesAndIncludesFromSnapshot(oldProjectInfo);
                    filesToReindex.unite(newSourceFiles);

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

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

                    const QSet<QString> modifiedFiles = comparer.timeStampModifiedFiles(snapshot());
                    filesToReindex.unite(modifiedFiles);
864
                }
865

866 867 868 869 870 871 872
                // 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);
                }
873 874
            }

875 876 877
            // Announce removed project parts
            emit projectPartsRemoved(comparer.removedProjectParts());

878 879
        // A new project was opened/created, do a full indexing
        } else {
880
            d->m_dirty = true;
881
            filesToReindex.unite(newSourceFiles);
882 883
        }

884
        // Update Project/ProjectInfo and File/ProjectPart table
885
        d->m_projectToProjectsInfo.insert(project, newProjectInfo);
886
        recalculateProjectPartMappings();
887

888 889 890
    } // Mutex scope

    // If requested, dump everything we got
891
    if (DumpProjectInfo)
892
        dumpModelManagerConfiguration(QLatin1String("updateProjectInfo"));
893

894 895 896 897
    // Remove files from snapshot that are not reachable any more
    if (filesRemoved)
        GC();

898
    // Announce added project parts
899
    emit projectPartsUpdated(newProjectInfo.project().data());
900

901 902 903 904
    // 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.
905
    updateCppEditorDocuments();