/**************************************************************************** ** ** 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. ** ****************************************************************************/ #include "clangeditordocumentprocessor.h" #include "clangfixitoperation.h" #include "clangfixitoperationsextractor.h" #include "clanghighlightingmarksreporter.h" #include "clangmodelmanagersupport.h" #include "clangutils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace ClangCodeModel { namespace Internal { ClangEditorDocumentProcessor::ClangEditorDocumentProcessor( ModelManagerSupportClang *modelManagerSupport, TextEditor::TextDocument *document) : BaseEditorDocumentProcessor(document) , m_diagnosticManager(document) , m_modelManagerSupport(modelManagerSupport) , m_parser(new ClangEditorDocumentParser(document->filePath().toString())) , m_parserRevision(0) , m_semanticHighlighter(document) , m_builtinProcessor(document, /*enableSemanticHighlighter=*/ false) { // Forwarding the semantic info from the builtin processor enables us to provide all // editor (widget) related features that are not yet implemented by the clang plugin. connect(&m_builtinProcessor, &CppTools::BuiltinEditorDocumentProcessor::cppDocumentUpdated, this, &ClangEditorDocumentProcessor::cppDocumentUpdated); connect(&m_builtinProcessor, &CppTools::BuiltinEditorDocumentProcessor::semanticInfoUpdated, this, &ClangEditorDocumentProcessor::semanticInfoUpdated); } ClangEditorDocumentProcessor::~ClangEditorDocumentProcessor() { m_parserWatcher.cancel(); m_parserWatcher.waitForFinished(); if (m_projectPart) { QTC_ASSERT(m_modelManagerSupport, return); m_modelManagerSupport->ipcCommunicator().unregisterTranslationUnitsForEditor( {ClangBackEnd::FileContainer(filePath(), m_projectPart->id())}); } } void ClangEditorDocumentProcessor::run() { updateTranslationUnitIfProjectPartExists(); // Run clang parser disconnect(&m_parserWatcher, &QFutureWatcher::finished, this, &ClangEditorDocumentProcessor::onParserFinished); m_parserWatcher.cancel(); m_parserWatcher.setFuture(QFuture()); m_parserRevision = revision(); connect(&m_parserWatcher, &QFutureWatcher::finished, this, &ClangEditorDocumentProcessor::onParserFinished); const QFuture future = QtConcurrent::run(&runParser, parser(), ClangEditorDocumentParser::InMemoryInfo(true)); m_parserWatcher.setFuture(future); // Run builtin processor m_builtinProcessor.run(); } void ClangEditorDocumentProcessor::recalculateSemanticInfoDetached(bool force) { m_builtinProcessor.recalculateSemanticInfoDetached(force); } void ClangEditorDocumentProcessor::semanticRehighlight() { m_semanticHighlighter.updateFormatMapFromFontSettings(); if (m_projectPart) requestDocumentAnnotations(m_projectPart->id()); } CppTools::SemanticInfo ClangEditorDocumentProcessor::recalculateSemanticInfo() { return m_builtinProcessor.recalculateSemanticInfo(); } CppTools::BaseEditorDocumentParser::Ptr ClangEditorDocumentProcessor::parser() { return m_parser; } CPlusPlus::Snapshot ClangEditorDocumentProcessor::snapshot() { return m_builtinProcessor.snapshot(); } bool ClangEditorDocumentProcessor::isParserRunning() const { return m_parserWatcher.isRunning(); } bool ClangEditorDocumentProcessor::hasProjectPart() const { return m_projectPart; } CppTools::ProjectPart::Ptr ClangEditorDocumentProcessor::projectPart() const { return m_projectPart; } void ClangEditorDocumentProcessor::clearProjectPart() { m_projectPart.clear(); } void ClangEditorDocumentProcessor::updateCodeWarnings(const QVector &diagnostics, uint documentRevision) { if (documentRevision == revision()) { m_diagnosticManager.processNewDiagnostics(diagnostics); const auto codeWarnings = m_diagnosticManager.takeExtraSelections(); emit codeWarningsUpdated(revision(), codeWarnings); } } namespace { int positionInText(QTextDocument *textDocument, const ClangBackEnd::SourceLocationContainer &sourceLocationContainer) { auto textBlock = textDocument->findBlockByNumber(int(sourceLocationContainer.line()) - 1); return textBlock.position() + int(sourceLocationContainer.column()) - 1; } TextEditor::BlockRange toTextEditorBlock(QTextDocument *textDocument, const ClangBackEnd::SourceRangeContainer &sourceRangeContainer) { return TextEditor::BlockRange(positionInText(textDocument, sourceRangeContainer.start()), positionInText(textDocument, sourceRangeContainer.end())); } QList toTextEditorBlocks(QTextDocument *textDocument, const QVector &ifdefedOutRanges) { QList blockRanges; blockRanges.reserve(ifdefedOutRanges.size()); for (const auto &range : ifdefedOutRanges) blockRanges.append(toTextEditorBlock(textDocument, range)); return blockRanges; } } void ClangEditorDocumentProcessor::updateHighlighting( const QVector &highlightingMarks, const QVector &skippedPreprocessorRanges, uint documentRevision) { if (documentRevision == revision()) { const auto skippedPreprocessorBlocks = toTextEditorBlocks(textDocument(), skippedPreprocessorRanges); emit ifdefedOutBlocksUpdated(documentRevision, skippedPreprocessorBlocks); m_semanticHighlighter.setHighlightingRunner( [highlightingMarks]() { auto *reporter = new HighlightingMarksReporter(highlightingMarks); return reporter->start(); }); m_semanticHighlighter.run(); } } static int currentLine(const TextEditor::AssistInterface &assistInterface) { int line, column; TextEditor::Convenience::convertPosition(assistInterface.textDocument(), assistInterface.position(), &line, &column); return line; } TextEditor::QuickFixOperations ClangEditorDocumentProcessor::extraRefactoringOperations( const TextEditor::AssistInterface &assistInterface) { ClangFixItOperationsExtractor extractor(m_diagnosticManager.diagnosticsWithFixIts()); return extractor.extract(assistInterface.fileName(), currentLine(assistInterface)); } ClangBackEnd::FileContainer ClangEditorDocumentProcessor::fileContainerWithArguments() const { return fileContainerWithArguments(m_projectPart.data()); } void ClangEditorDocumentProcessor::clearDiagnosticsWithFixIts() { m_diagnosticManager.clearDiagnosticsWithFixIts(); } ClangEditorDocumentProcessor *ClangEditorDocumentProcessor::get(const QString &filePath) { return qobject_cast(BaseEditorDocumentProcessor::get(filePath)); } static bool isProjectPartLoadedOrIsFallback(CppTools::ProjectPart::Ptr projectPart) { return projectPart && (projectPart->id().isEmpty() || ClangCodeModel::Utils::isProjectPartLoaded(projectPart)); } void ClangEditorDocumentProcessor::updateProjectPartAndTranslationUnitForEditor() { const CppTools::ProjectPart::Ptr projectPart = m_parser->projectPart(); if (isProjectPartLoadedOrIsFallback(projectPart)) { registerTranslationUnitForEditor(projectPart.data()); m_projectPart = projectPart; } } void ClangEditorDocumentProcessor::onParserFinished() { if (revision() != m_parserRevision) return; updateProjectPartAndTranslationUnitForEditor(); } void ClangEditorDocumentProcessor::registerTranslationUnitForEditor(CppTools::ProjectPart *projectPart) { QTC_ASSERT(m_modelManagerSupport, return); IpcCommunicator &ipcCommunicator = m_modelManagerSupport->ipcCommunicator(); if (m_projectPart) { if (projectPart->id() != m_projectPart->id()) { ipcCommunicator.unregisterTranslationUnitsForEditor({fileContainerWithArguments()}); ipcCommunicator.registerTranslationUnitsForEditor({fileContainerWithArguments(projectPart)}); } } else { ipcCommunicator.registerTranslationUnitsForEditor({{fileContainerWithArguments(projectPart)}}); } } void ClangEditorDocumentProcessor::updateTranslationUnitIfProjectPartExists() { if (m_projectPart) { const ClangBackEnd::FileContainer fileContainer = fileContainerWithDocumentContent(m_projectPart->id()); m_modelManagerSupport->ipcCommunicator().updateTranslationUnitWithRevisionCheck(fileContainer); } } void ClangEditorDocumentProcessor::requestDocumentAnnotations(const QString &projectpartId) { const auto fileContainer = fileContainerWithDocumentContent(projectpartId); auto &ipcCommunicator = m_modelManagerSupport->ipcCommunicator(); ipcCommunicator.requestDiagnostics(fileContainer); ipcCommunicator.requestHighlighting(fileContainer); } static CppTools::ProjectPart projectPartForLanguageOption(CppTools::ProjectPart *projectPart) { if (projectPart) return *projectPart; return *CppTools::CppModelManager::instance()->fallbackProjectPart().data(); } static QStringList languageOptions(const QString &filePath, CppTools::ProjectPart *projectPart) { const auto theProjectPart = projectPartForLanguageOption(projectPart); CppTools::CompilerOptionsBuilder builder(theProjectPart); builder.addLanguageOption(CppTools::ProjectFile::classify(filePath)); return builder.options(); } static QStringList fileArguments(const QString &filePath, CppTools::ProjectPart *projectPart) { return QStringList(languageOptions(filePath, projectPart)) + CppTools::codeModelSettings()->extraClangOptions(); } ClangBackEnd::FileContainer ClangEditorDocumentProcessor::fileContainerWithArguments(CppTools::ProjectPart *projectPart) const { const auto projectPartId = projectPart ? Utf8String::fromString(projectPart->id()) : Utf8String(); const QStringList theFileArguments = fileArguments(filePath(), projectPart); return {filePath(), projectPartId, Utf8StringVector(theFileArguments), revision()}; } ClangBackEnd::FileContainer ClangEditorDocumentProcessor::fileContainerWithDocumentContent(const QString &projectpartId) const { return ClangBackEnd::FileContainer(filePath(), projectpartId, baseTextDocument()->plainText(), true, revision()); } } // namespace Internal } // namespace ClangCodeModel