clangbackendipcintegration.cpp 17 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
**
** This file is part of Qt Creator.
**
** 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
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** 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.
**
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

31
#include "clangbackendipcintegration.h"
32

33
#include "clangcompletionassistprocessor.h"
Marco Bubke's avatar
Marco Bubke committed
34
#include "clangeditordocumentprocessor.h"
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
#include "clangmodelmanagersupport.h"
#include "clangutils.h"
#include "pchmanager.h"

#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>

#include <cpptools/abstracteditorsupport.h>
#include <cpptools/baseeditordocumentprocessor.h>
#include <cpptools/editordocumenthandle.h>

#include <texteditor/codeassist/functionhintproposal.h>
#include <texteditor/codeassist/iassistprocessor.h>
#include <texteditor/texteditor.h>

Marco Bubke's avatar
Marco Bubke committed
50 51
#include <clangbackendipc/diagnosticschangedmessage.h>

52 53 54
#include <utils/hostosinfo.h>
#include <utils/qtcassert.h>

Marco Bubke's avatar
Marco Bubke committed
55 56 57 58 59 60 61 62
#include <clangbackendipc/cmbcodecompletedmessage.h>
#include <clangbackendipc/cmbcompletecodemessage.h>
#include <clangbackendipc/cmbechomessage.h>
#include <clangbackendipc/cmbregistertranslationunitsforcodecompletionmessage.h>
#include <clangbackendipc/cmbregisterprojectsforcodecompletionmessage.h>
#include <clangbackendipc/cmbunregistertranslationunitsforcodecompletionmessage.h>
#include <clangbackendipc/cmbunregisterprojectsforcodecompletionmessage.h>
#include <clangbackendipc/cmbmessages.h>
Marco Bubke's avatar
Marco Bubke committed
63 64
#include <clangbackendipc/requestdiagnosticsmessage.h>
#include <clangbackendipc/filecontainer.h>
Marco Bubke's avatar
Marco Bubke committed
65 66
#include <clangbackendipc/projectpartsdonotexistmessage.h>
#include <clangbackendipc/translationunitdoesnotexistmessage.h>
67 68 69 70 71 72 73 74 75 76 77 78

#include <cplusplus/Icons.h>

#include <QElapsedTimer>
#include <QLoggingCategory>
#include <QProcess>

static Q_LOGGING_CATEGORY(log, "qtc.clangcodemodel.ipc")

using namespace CPlusPlus;
using namespace ClangCodeModel;
using namespace ClangCodeModel::Internal;
79
using namespace ClangBackEnd;
80 81 82 83 84 85
using namespace TextEditor;

namespace {

QString backendProcessPath()
{
86
    return Core::ICore::libexecPath()
87
            + QStringLiteral("/clangbackend")
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
            + QStringLiteral(QTC_HOST_EXE_SUFFIX);
}

} // anonymous namespace

IpcReceiver::IpcReceiver()
{
}

IpcReceiver::~IpcReceiver()
{
    deleteAndClearWaitingAssistProcessors();
}

void IpcReceiver::setAliveHandler(const IpcReceiver::AliveHandler &handler)
{
    m_aliveHandler = handler;
}

Marco Bubke's avatar
Marco Bubke committed
107
void IpcReceiver::addExpectedCodeCompletedMessage(
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
        quint64 ticket,
        ClangCompletionAssistProcessor *processor)
{
    QTC_ASSERT(processor, return);
    QTC_CHECK(!m_assistProcessorsTable.contains(ticket));
    m_assistProcessorsTable.insert(ticket, processor);
}

void IpcReceiver::deleteAndClearWaitingAssistProcessors()
{
    qDeleteAll(m_assistProcessorsTable.begin(), m_assistProcessorsTable.end());
    m_assistProcessorsTable.clear();
}

void IpcReceiver::deleteProcessorsOfEditorWidget(TextEditor::TextEditorWidget *textEditorWidget)
{
    QMutableHashIterator<quint64, ClangCompletionAssistProcessor *> it(m_assistProcessorsTable);
    while (it.hasNext()) {
        it.next();
        ClangCompletionAssistProcessor *assistProcessor = it.value();
        if (assistProcessor->textEditorWidget() == textEditorWidget) {
            delete assistProcessor;
            it.remove();
        }
    }
}

void IpcReceiver::alive()
{
Marco Bubke's avatar
Marco Bubke committed
137
    qCDebug(log) << "<<< AliveMessage";
138 139 140 141
    QTC_ASSERT(m_aliveHandler, return);
    m_aliveHandler();
}

Marco Bubke's avatar
Marco Bubke committed
142
void IpcReceiver::echo(const EchoMessage &message)
143
{
Marco Bubke's avatar
Marco Bubke committed
144
    qCDebug(log) << "<<<" << message;
145 146
}

Marco Bubke's avatar
Marco Bubke committed
147
void IpcReceiver::codeCompleted(const CodeCompletedMessage &message)
148
{
Marco Bubke's avatar
Marco Bubke committed
149
    qCDebug(log) << "<<< CodeCompletedMessage with" << message.codeCompletions().size() << "items";
150

Marco Bubke's avatar
Marco Bubke committed
151
    const quint64 ticket = message.ticketNumber();
152
    QScopedPointer<ClangCompletionAssistProcessor> processor(m_assistProcessorsTable.take(ticket));
153
    if (processor) {
Marco Bubke's avatar
Marco Bubke committed
154
        const bool finished = processor->handleAvailableAsyncCompletions(message.codeCompletions());
155 156 157
        if (!finished)
            processor.take();
    }
158 159
}

Marco Bubke's avatar
Marco Bubke committed
160 161 162 163 164 165 166 167 168 169 170 171 172 173
void IpcReceiver::diagnosticsChanged(const DiagnosticsChangedMessage &message)
{
    qCDebug(log) << "<<< DiagnosticsChangedMessage with" << message.diagnostics().size() << "items";

    auto processor = ClangEditorDocumentProcessor::get(message.file().filePath());

    if (processor && processor->projectPart()) {
        const QString diagnosticsProjectPartId = message.file().projectPartId();
        const QString documentProjectPartId = processor->projectPart()->id();
        if (diagnosticsProjectPartId == documentProjectPartId)
            processor->updateCodeWarnings(message.diagnostics(), message.documentRevision());
    }
}

Marco Bubke's avatar
Marco Bubke committed
174
void IpcReceiver::translationUnitDoesNotExist(const TranslationUnitDoesNotExistMessage &message)
175
{
Marco Bubke's avatar
Marco Bubke committed
176 177
    QTC_CHECK(!"Got TranslationUnitDoesNotExistMessage");
    qCDebug(log) << "<<< ERROR:" << message;
178 179
}

Marco Bubke's avatar
Marco Bubke committed
180
void IpcReceiver::projectPartsDoNotExist(const ProjectPartsDoNotExistMessage &message)
181
{
Marco Bubke's avatar
Marco Bubke committed
182 183
    QTC_CHECK(!"Got ProjectPartsDoNotExistMessage");
    qCDebug(log) << "<<< ERROR:" << message;
184 185 186 187 188
}

class IpcSender : public IpcSenderInterface
{
public:
189
    IpcSender(ClangBackEnd::ConnectionClient &connectionClient)
190 191 192 193
        : m_connection(connectionClient)
    {}

    void end() override;
Marco Bubke's avatar
Marco Bubke committed
194 195 196 197 198
    void registerTranslationUnitsForCodeCompletion(const ClangBackEnd::RegisterTranslationUnitForCodeCompletionMessage &message) override;
    void unregisterTranslationUnitsForCodeCompletion(const ClangBackEnd::UnregisterTranslationUnitsForCodeCompletionMessage &message) override;
    void registerProjectPartsForCodeCompletion(const ClangBackEnd::RegisterProjectPartsForCodeCompletionMessage &message) override;
    void unregisterProjectPartsForCodeCompletion(const ClangBackEnd::UnregisterProjectPartsForCodeCompletionMessage &message) override;
    void completeCode(const ClangBackEnd::CompleteCodeMessage &message) override;
Marco Bubke's avatar
Marco Bubke committed
199
    void requestDiagnostics(const ClangBackEnd::RequestDiagnosticsMessage &message) override;
200 201

private:
202
    ClangBackEnd::ConnectionClient &m_connection;
203 204 205 206 207
};

void IpcSender::end()
{
     QTC_CHECK(m_connection.isConnected());
Marco Bubke's avatar
Marco Bubke committed
208
     m_connection.sendEndMessage();
209 210
}

Marco Bubke's avatar
Marco Bubke committed
211
void IpcSender::registerTranslationUnitsForCodeCompletion(const RegisterTranslationUnitForCodeCompletionMessage &message)
212 213
{
     QTC_CHECK(m_connection.isConnected());
Marco Bubke's avatar
Marco Bubke committed
214
     m_connection.serverProxy().registerTranslationUnitsForCodeCompletion(message);
215 216
}

Marco Bubke's avatar
Marco Bubke committed
217
void IpcSender::unregisterTranslationUnitsForCodeCompletion(const UnregisterTranslationUnitsForCodeCompletionMessage &message)
218 219
{
     QTC_CHECK(m_connection.isConnected());
Marco Bubke's avatar
Marco Bubke committed
220
     m_connection.serverProxy().unregisterTranslationUnitsForCodeCompletion(message);
221 222
}

Marco Bubke's avatar
Marco Bubke committed
223
void IpcSender::registerProjectPartsForCodeCompletion(const RegisterProjectPartsForCodeCompletionMessage &message)
224 225
{
     QTC_CHECK(m_connection.isConnected());
Marco Bubke's avatar
Marco Bubke committed
226
     m_connection.serverProxy().registerProjectPartsForCodeCompletion(message);
227 228
}

Marco Bubke's avatar
Marco Bubke committed
229
void IpcSender::unregisterProjectPartsForCodeCompletion(const UnregisterProjectPartsForCodeCompletionMessage &message)
230 231
{
     QTC_CHECK(m_connection.isConnected());
Marco Bubke's avatar
Marco Bubke committed
232
     m_connection.serverProxy().unregisterProjectPartsForCodeCompletion(message);
233 234
}

Marco Bubke's avatar
Marco Bubke committed
235
void IpcSender::completeCode(const CompleteCodeMessage &message)
236 237
{
     QTC_CHECK(m_connection.isConnected());
Marco Bubke's avatar
Marco Bubke committed
238
     m_connection.serverProxy().completeCode(message);
239 240
}

Marco Bubke's avatar
Marco Bubke committed
241 242 243 244 245 246
void IpcSender::requestDiagnostics(const RequestDiagnosticsMessage &message)
{
    QTC_CHECK(m_connection.isConnected());
    m_connection.serverProxy().requestDiagnostics(message);
}

247 248 249 250 251 252 253 254
IpcCommunicator::IpcCommunicator()
    : m_connection(&m_ipcReceiver)
    , m_ipcSender(new IpcSender(m_connection))
{
    m_ipcReceiver.setAliveHandler([this]() { m_connection.resetProcessAliveTimer(); });

    connect(Core::EditorManager::instance(), &Core::EditorManager::editorAboutToClose,
            this, &IpcCommunicator::onEditorAboutToClose);
255 256
    connect(Core::ICore::instance(), &Core::ICore::coreAboutToClose,
            this, &IpcCommunicator::onCoreAboutToClose);
257 258 259 260

    initializeBackend();
}

Marco Bubke's avatar
Marco Bubke committed
261
static bool areMessagesRegistered = false;
262 263 264

void IpcCommunicator::initializeBackend()
{
Marco Bubke's avatar
Marco Bubke committed
265 266 267
    if (!areMessagesRegistered) {
        areMessagesRegistered = true;
        Messages::registerMessages();
268 269
    }

270 271 272
    const QString clangBackEndProcessPath = backendProcessPath();
    qCDebug(log) << "Starting" << clangBackEndProcessPath;
    QTC_ASSERT(QFileInfo(clangBackEndProcessPath).exists(), return);
273

274
    m_connection.setProcessAliveTimerInterval(30 * 1000);
275
    m_connection.setProcessPath(clangBackEndProcessPath);
276 277 278 279

    connect(&m_connection, &ConnectionClient::processRestarted,
            this, &IpcCommunicator::onBackendRestarted);

280 281
    // TODO: Add a asynchron API to ConnectionClient, otherwise we might hang here
    if (m_connection.connectToServer())
282 283 284 285 286 287
        initializeBackendWithCurrentData();
}

void IpcCommunicator::registerEmptyProjectForProjectLessFiles()
{
    QTC_CHECK(m_connection.isConnected());
288
    registerProjectPartsForCodeCompletion({ClangBackEnd::ProjectPartContainer(
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
                                           Utf8String(),
                                           Utf8StringVector())});
}

void IpcCommunicator::registerCurrentProjectParts()
{
    using namespace CppTools;

    const QList<ProjectInfo> projectInfos = CppModelManager::instance()->projectInfos();
    foreach (const ProjectInfo &projectInfo, projectInfos)
        registerProjectsParts(projectInfo.projectParts());
}

void IpcCommunicator::registerCurrentUnsavedFiles()
{
    using namespace CppTools;

    const auto cppEditorDocuments = CppModelManager::instance()->cppEditorDocuments();
    foreach (const CppEditorDocumentHandle *cppEditorDocument, cppEditorDocuments) {
        if (cppEditorDocument->processor()->baseTextDocument()->isModified())
            updateUnsavedFileFromCppEditorDocument(cppEditorDocument->filePath());
    }

}

void IpcCommunicator::registerCurrrentCodeModelUiHeaders()
{
    using namespace CppTools;

    const auto editorSupports = CppModelManager::instance()->abstractEditorSupports();
    foreach (const AbstractEditorSupport *es, editorSupports)
        updateUnsavedFile(es->fileName(), es->contents());
}

Marco Bubke's avatar
Marco Bubke committed
323
static QStringList projectPartMessageLine(const CppTools::ProjectPart::Ptr &projectPart)
324 325 326 327 328
{
    QStringList options = ClangCodeModel::Utils::createClangOptions(projectPart,
        CppTools::ProjectFile::Unclassified); // No language option
    if (PchInfo::Ptr pchInfo = PchManager::instance()->pchInfo(projectPart))
        options += ClangCodeModel::Utils::createPCHInclusionOptions(pchInfo->fileName());
Marco Bubke's avatar
Marco Bubke committed
329

330 331 332
    return options;
}

333
static ClangBackEnd::ProjectPartContainer toProjectPartContainer(
334 335
        const CppTools::ProjectPart::Ptr &projectPart)
{
Marco Bubke's avatar
Marco Bubke committed
336
    const QStringList arguments = projectPartMessageLine(projectPart);
337
    return ClangBackEnd::ProjectPartContainer(projectPart->id(), Utf8StringVector(arguments));
338 339
}

340
static QVector<ClangBackEnd::ProjectPartContainer> toProjectPartContainers(
341 342
        const QList<CppTools::ProjectPart::Ptr> projectParts)
{
343
    QVector<ClangBackEnd::ProjectPartContainer> projectPartContainers;
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
    projectPartContainers.reserve(projectParts.size());
    foreach (const CppTools::ProjectPart::Ptr &projectPart, projectParts)
        projectPartContainers << toProjectPartContainer(projectPart);
    return projectPartContainers;
}

void IpcCommunicator::registerProjectsParts(const QList<CppTools::ProjectPart::Ptr> projectParts)
{
    const auto projectPartContainers = toProjectPartContainers(projectParts);
    registerProjectPartsForCodeCompletion(projectPartContainers);
}

void IpcCommunicator::updateUnsavedFileFromCppEditorDocument(const QString &filePath)
{
    const QByteArray unsavedContent = CppTools::CppModelManager::instance()
            ->cppEditorDocument(filePath)->contents();
    updateUnsavedFile(filePath, unsavedContent);
}

void IpcCommunicator::updateUnsavedFile(const QString &filePath, const QByteArray &contents)
{
365
    const QString projectPartId = Utils::projectPartIdForFile(filePath);
366 367 368
    const bool hasUnsavedContent = true;

    // TODO: Send new only if changed
Marco Bubke's avatar
Marco Bubke committed
369 370 371 372 373 374 375 376 377 378
    registerFilesForCodeCompletion({{filePath,
                                    projectPartId,
                                    Utf8String::fromByteArray(contents),
                                    hasUnsavedContent}});
}

void IpcCommunicator::requestDiagnostics(const FileContainer &fileContainer, uint documentRevision)
{
    registerFilesForCodeCompletion({fileContainer});
    m_ipcSender->requestDiagnostics({fileContainer, documentRevision});
379 380 381 382 383 384 385 386 387 388 389 390
}

void IpcCommunicator::updateUnsavedFileIfNotCurrentDocument(Core::IDocument *document)
{
    QTC_ASSERT(document, return);

    if (Core::EditorManager::currentDocument() != document)
        updateUnsavedFileFromCppEditorDocument(document->filePath().toString());
}

void IpcCommunicator::onBackendRestarted()
{
391
    qWarning("Clang back end finished unexpectedly, restarted.");
392 393 394 395 396 397 398 399 400 401 402 403
    qCDebug(log) << "Backend restarted, re-initializing with project data and unsaved files.";

    m_ipcReceiver.deleteAndClearWaitingAssistProcessors();
    initializeBackendWithCurrentData();
}

void IpcCommunicator::onEditorAboutToClose(Core::IEditor *editor)
{
    if (auto *textEditor = qobject_cast<TextEditor::BaseTextEditor *>(editor))
        m_ipcReceiver.deleteProcessorsOfEditorWidget(textEditor->editorWidget());
}

404 405 406 407 408
void IpcCommunicator::onCoreAboutToClose()
{
    m_sendMode = IgnoreSendRequests;
}

409 410 411 412 413 414 415 416 417 418 419 420
void IpcCommunicator::initializeBackendWithCurrentData()
{
    registerEmptyProjectForProjectLessFiles();
    registerCurrentProjectParts();
    registerCurrentUnsavedFiles();
    registerCurrrentCodeModelUiHeaders();

    emit backendReinitialized();
}

IpcSenderInterface *IpcCommunicator::setIpcSender(IpcSenderInterface *ipcSender)
{
Marco Bubke's avatar
Marco Bubke committed
421
    IpcSenderInterface *previousMessageSender = m_ipcSender.take();
422
    m_ipcSender.reset(ipcSender);
Marco Bubke's avatar
Marco Bubke committed
423
    return previousMessageSender;
424 425 426 427 428 429 430 431 432 433 434 435
}

void IpcCommunicator::killBackendProcess()
{
    m_connection.processForTestOnly()->kill();
}

void IpcCommunicator::registerFilesForCodeCompletion(const FileContainers &fileContainers)
{
    if (m_sendMode == IgnoreSendRequests)
        return;

Marco Bubke's avatar
Marco Bubke committed
436 437 438
    const RegisterTranslationUnitForCodeCompletionMessage message(fileContainers);
    qCDebug(log) << ">>>" << message;
    m_ipcSender->registerTranslationUnitsForCodeCompletion(message);
439 440 441 442 443 444 445
}

void IpcCommunicator::unregisterFilesForCodeCompletion(const FileContainers &fileContainers)
{
    if (m_sendMode == IgnoreSendRequests)
        return;

Marco Bubke's avatar
Marco Bubke committed
446 447 448
    const UnregisterTranslationUnitsForCodeCompletionMessage message(fileContainers);
    qCDebug(log) << ">>>" << message;
    m_ipcSender->unregisterTranslationUnitsForCodeCompletion(message);
449 450 451 452 453 454 455 456
}

void IpcCommunicator::registerProjectPartsForCodeCompletion(
        const ProjectPartContainers &projectPartContainers)
{
    if (m_sendMode == IgnoreSendRequests)
        return;

Marco Bubke's avatar
Marco Bubke committed
457 458 459
    const RegisterProjectPartsForCodeCompletionMessage message(projectPartContainers);
    qCDebug(log) << ">>>" << message;
    m_ipcSender->registerProjectPartsForCodeCompletion(message);
460 461
}

462
void IpcCommunicator::unregisterProjectPartsForCodeCompletion(const QStringList &projectPartIds)
463 464 465 466
{
    if (m_sendMode == IgnoreSendRequests)
        return;

Marco Bubke's avatar
Marco Bubke committed
467 468 469
    const UnregisterProjectPartsForCodeCompletionMessage message((Utf8StringVector(projectPartIds)));
    qCDebug(log) << ">>>" << message;
    m_ipcSender->unregisterProjectPartsForCodeCompletion(message);
470 471 472 473 474 475 476 477 478 479 480
}

void IpcCommunicator::completeCode(ClangCompletionAssistProcessor *assistProcessor,
                              const QString &filePath,
                              quint32 line,
                              quint32 column,
                              const QString &projectFilePath)
{
    if (m_sendMode == IgnoreSendRequests)
        return;

Marco Bubke's avatar
Marco Bubke committed
481 482 483 484
    const CompleteCodeMessage message(filePath, line, column, projectFilePath);
    qCDebug(log) << ">>>" << message;
    m_ipcSender->completeCode(message);
    m_ipcReceiver.addExpectedCodeCompletedMessage(message.ticketNumber(), assistProcessor);
485
}