Files
qt-creator/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp
Ivan Donchevskii 32bae7ef6c Clang: use local renaming based on ClangCodeModel
Provide refactoring engine for ClangCodeModel and
implement missing methods.

Change-Id: If5c913e0c5a7941cd2ced54d0fcfa4d625eadc93
Reviewed-by: Nikolai Kosjar <nikolai.kosjar@qt.io>
2017-11-20 10:24:47 +00:00

415 lines
15 KiB
C++

/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://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 https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "clangmodelmanagersupport.h"
#include "clangconstants.h"
#include "clangeditordocumentprocessor.h"
#include "clangutils.h"
#include "clangfollowsymbol.h"
#include "clangrefactoringengine.h"
#include <coreplugin/editormanager/editormanager.h>
#include <cpptools/cppfollowsymbolundercursor.h>
#include <cpptools/cppmodelmanager.h>
#include <cpptools/editordocumenthandle.h>
#include <cpptools/projectinfo.h>
#include <texteditor/quickfix.h>
#include <projectexplorer/project.h>
#include <clangsupport/cmbregisterprojectsforeditormessage.h>
#include <clangsupport/filecontainer.h>
#include <clangsupport/projectpartcontainer.h>
#include <utils/qtcassert.h>
#include <QCoreApplication>
#include <QMenu>
#include <QTextBlock>
using namespace ClangCodeModel;
using namespace ClangCodeModel::Internal;
static ModelManagerSupportClang *m_instance = 0;
static bool useClangFollowSymbol()
{
static bool use = qEnvironmentVariableIntValue("QTC_CLANG_FOLLOW_SYMBOL");
return use;
}
static CppTools::CppModelManager *cppModelManager()
{
return CppTools::CppModelManager::instance();
}
ModelManagerSupportClang::ModelManagerSupportClang()
: m_completionAssistProvider(m_communicator)
, m_refactoringEngine(new RefactoringEngine)
{
QTC_CHECK(!m_instance);
m_instance = this;
if (useClangFollowSymbol())
m_followSymbol.reset(new ClangFollowSymbol);
else
m_followSymbol.reset(new CppTools::FollowSymbolUnderCursor);
Core::EditorManager *editorManager = Core::EditorManager::instance();
connect(editorManager, &Core::EditorManager::editorOpened,
this, &ModelManagerSupportClang::onEditorOpened);
connect(editorManager, &Core::EditorManager::currentEditorChanged,
this, &ModelManagerSupportClang::onCurrentEditorChanged,
Qt::QueuedConnection);
connect(editorManager, &Core::EditorManager::editorsClosed,
this, &ModelManagerSupportClang::onEditorClosed,
Qt::QueuedConnection);
CppTools::CppModelManager *modelManager = cppModelManager();
connect(modelManager, &CppTools::CppModelManager::abstractEditorSupportContentsUpdated,
this, &ModelManagerSupportClang::onAbstractEditorSupportContentsUpdated);
connect(modelManager, &CppTools::CppModelManager::abstractEditorSupportRemoved,
this, &ModelManagerSupportClang::onAbstractEditorSupportRemoved);
connect(modelManager, &CppTools::CppModelManager::projectPartsUpdated,
this, &ModelManagerSupportClang::onProjectPartsUpdated);
connect(modelManager, &CppTools::CppModelManager::projectPartsRemoved,
this, &ModelManagerSupportClang::onProjectPartsRemoved);
m_communicator.registerFallbackProjectPart();
}
ModelManagerSupportClang::~ModelManagerSupportClang()
{
m_instance = 0;
}
CppTools::CppCompletionAssistProvider *ModelManagerSupportClang::completionAssistProvider()
{
return &m_completionAssistProvider;
}
CppTools::FollowSymbolInterface &ModelManagerSupportClang::followSymbolInterface()
{
return *m_followSymbol;
}
CppTools::RefactoringEngineInterface &ModelManagerSupportClang::refactoringEngineInterface()
{
return *m_refactoringEngine;
}
CppTools::BaseEditorDocumentProcessor *ModelManagerSupportClang::editorDocumentProcessor(
TextEditor::TextDocument *baseTextDocument)
{
return new ClangEditorDocumentProcessor(m_communicator, baseTextDocument);
}
void ModelManagerSupportClang::onCurrentEditorChanged(Core::IEditor *)
{
m_communicator.updateTranslationUnitVisiblity();
}
void ModelManagerSupportClang::connectTextDocumentToTranslationUnit(TextEditor::TextDocument *textDocument)
{
// Handle externally changed documents
connect(textDocument, &Core::IDocument::aboutToReload,
this, &ModelManagerSupportClang::onCppDocumentAboutToReloadOnTranslationUnit,
Qt::UniqueConnection);
connect(textDocument, &Core::IDocument::reloadFinished,
this, &ModelManagerSupportClang::onCppDocumentReloadFinishedOnTranslationUnit,
Qt::UniqueConnection);
// Handle changes from e.g. refactoring actions
connectToTextDocumentContentsChangedForTranslationUnit(textDocument);
}
void ModelManagerSupportClang::connectTextDocumentToUnsavedFiles(TextEditor::TextDocument *textDocument)
{
// Handle externally changed documents
connect(textDocument, &Core::IDocument::aboutToReload,
this, &ModelManagerSupportClang::onCppDocumentAboutToReloadOnUnsavedFile,
Qt::UniqueConnection);
connect(textDocument, &Core::IDocument::reloadFinished,
this, &ModelManagerSupportClang::onCppDocumentReloadFinishedOnUnsavedFile,
Qt::UniqueConnection);
// Handle changes from e.g. refactoring actions
connectToTextDocumentContentsChangedForUnsavedFile(textDocument);
}
void ModelManagerSupportClang::connectToTextDocumentContentsChangedForTranslationUnit(
TextEditor::TextDocument *textDocument)
{
connect(textDocument, &TextEditor::TextDocument::contentsChangedWithPosition,
this, &ModelManagerSupportClang::onCppDocumentContentsChangedOnTranslationUnit,
Qt::UniqueConnection);
}
void ModelManagerSupportClang::connectToTextDocumentContentsChangedForUnsavedFile(
TextEditor::TextDocument *textDocument)
{
connect(textDocument, &TextEditor::TextDocument::contentsChangedWithPosition,
this, &ModelManagerSupportClang::onCppDocumentContentsChangedOnUnsavedFile,
Qt::UniqueConnection);
}
void ModelManagerSupportClang::connectToWidgetsMarkContextMenuRequested(QWidget *editorWidget)
{
const auto widget = qobject_cast<TextEditor::TextEditorWidget *>(editorWidget);
if (widget) {
connect(widget, &TextEditor::TextEditorWidget::markContextMenuRequested,
this, &ModelManagerSupportClang::onTextMarkContextMenuRequested);
}
}
void ModelManagerSupportClang::onEditorOpened(Core::IEditor *editor)
{
QTC_ASSERT(editor, return);
Core::IDocument *document = editor->document();
QTC_ASSERT(document, return);
TextEditor::TextDocument *textDocument = qobject_cast<TextEditor::TextDocument *>(document);
if (textDocument && cppModelManager()->isCppEditor(editor)) {
connectTextDocumentToTranslationUnit(textDocument);
connectToWidgetsMarkContextMenuRequested(editor->widget());
// TODO: Ensure that not fully loaded documents are updated?
}
}
void ModelManagerSupportClang::onEditorClosed(const QList<Core::IEditor *> &)
{
m_communicator.updateTranslationUnitVisiblity();
}
void ModelManagerSupportClang::onCppDocumentAboutToReloadOnTranslationUnit()
{
TextEditor::TextDocument *textDocument = qobject_cast<TextEditor::TextDocument *>(sender());
disconnect(textDocument, &TextEditor::TextDocument::contentsChangedWithPosition,
this, &ModelManagerSupportClang::onCppDocumentContentsChangedOnTranslationUnit);
}
void ModelManagerSupportClang::onCppDocumentReloadFinishedOnTranslationUnit(bool success)
{
if (success) {
TextEditor::TextDocument *textDocument = qobject_cast<TextEditor::TextDocument *>(sender());
connectToTextDocumentContentsChangedForTranslationUnit(textDocument);
m_communicator.updateTranslationUnitWithRevisionCheck(textDocument);
}
}
namespace {
void clearDiagnosticFixIts(const QString &filePath)
{
auto processor = ClangEditorDocumentProcessor::get(filePath);
if (processor)
processor->clearDiagnosticsWithFixIts();
}
}
void ModelManagerSupportClang::onCppDocumentContentsChangedOnTranslationUnit(int position,
int /*charsRemoved*/,
int /*charsAdded*/)
{
Core::IDocument *document = qobject_cast<Core::IDocument *>(sender());
m_communicator.updateChangeContentStartPosition(document->filePath().toString(),
position);
m_communicator.updateTranslationUnitIfNotCurrentDocument(document);
clearDiagnosticFixIts(document->filePath().toString());
}
void ModelManagerSupportClang::onCppDocumentAboutToReloadOnUnsavedFile()
{
TextEditor::TextDocument *textDocument = qobject_cast<TextEditor::TextDocument *>(sender());
disconnect(textDocument, &TextEditor::TextDocument::contentsChangedWithPosition,
this, &ModelManagerSupportClang::onCppDocumentContentsChangedOnUnsavedFile);
}
void ModelManagerSupportClang::onCppDocumentReloadFinishedOnUnsavedFile(bool success)
{
if (success) {
TextEditor::TextDocument *textDocument = qobject_cast<TextEditor::TextDocument *>(sender());
connectToTextDocumentContentsChangedForUnsavedFile(textDocument);
m_communicator.updateUnsavedFile(textDocument);
}
}
void ModelManagerSupportClang::onCppDocumentContentsChangedOnUnsavedFile()
{
Core::IDocument *document = qobject_cast<Core::IDocument *>(sender());
m_communicator.updateUnsavedFile(document);
}
void ModelManagerSupportClang::onAbstractEditorSupportContentsUpdated(const QString &filePath,
const QByteArray &content)
{
QTC_ASSERT(!filePath.isEmpty(), return);
const QString mappedPath = m_uiHeaderOnDiskManager.createIfNeeded(filePath);
m_communicator.updateUnsavedFile(mappedPath, content, 0);
}
void ModelManagerSupportClang::onAbstractEditorSupportRemoved(const QString &filePath)
{
QTC_ASSERT(!filePath.isEmpty(), return);
if (!cppModelManager()->cppEditorDocument(filePath)) {
const QString mappedPath = m_uiHeaderOnDiskManager.remove(filePath);
const QString projectPartId = Utils::projectPartIdForFile(filePath);
m_communicator.unregisterUnsavedFilesForEditor({{mappedPath, projectPartId}});
}
}
void addFixItsActionsToMenu(QMenu *menu, const TextEditor::QuickFixOperations &fixItOperations)
{
foreach (const auto &fixItOperation, fixItOperations) {
QAction *action = menu->addAction(fixItOperation->description());
QObject::connect(action, &QAction::triggered, [fixItOperation]() {
fixItOperation->perform();
});
}
}
static int lineToPosition(const QTextDocument *textDocument, int lineNumber)
{
QTC_ASSERT(textDocument, return 0);
const QTextBlock textBlock = textDocument->findBlockByLineNumber(lineNumber);
return textBlock.isValid() ? textBlock.position() - 1 : 0;
}
static TextEditor::AssistInterface createAssistInterface(TextEditor::TextEditorWidget *widget,
int lineNumber)
{
return TextEditor::AssistInterface(widget->document(),
lineToPosition(widget->document(), lineNumber),
widget->textDocument()->filePath().toString(),
TextEditor::IdleEditor);
}
void ModelManagerSupportClang::onTextMarkContextMenuRequested(TextEditor::TextEditorWidget *widget,
int lineNumber,
QMenu *menu)
{
QTC_ASSERT(widget, return);
QTC_ASSERT(lineNumber >= 1, return);
QTC_ASSERT(menu, return);
const auto filePath = widget->textDocument()->filePath().toString();
ClangEditorDocumentProcessor *processor = ClangEditorDocumentProcessor::get(filePath);
if (processor) {
const auto assistInterface = createAssistInterface(widget, lineNumber);
const auto fixItOperations = processor->extraRefactoringOperations(assistInterface);
addFixItsActionsToMenu(menu, fixItOperations);
}
}
void ModelManagerSupportClang::onProjectPartsUpdated(ProjectExplorer::Project *project)
{
QTC_ASSERT(project, return);
const CppTools::ProjectInfo projectInfo = cppModelManager()->projectInfo(project);
QTC_ASSERT(projectInfo.isValid(), return);
m_communicator.registerProjectsParts(projectInfo.projectParts());
m_communicator.registerFallbackProjectPart();
}
void ModelManagerSupportClang::onProjectPartsRemoved(const QStringList &projectPartIds)
{
if (!projectPartIds.isEmpty()) {
unregisterTranslationUnitsWithProjectParts(projectPartIds);
m_communicator.unregisterProjectPartsForEditor(projectPartIds);
m_communicator.registerFallbackProjectPart();
}
}
static QVector<ClangEditorDocumentProcessor *>
clangProcessorsWithProjectParts(const QStringList &projectPartIds)
{
QVector<ClangEditorDocumentProcessor *> result;
foreach (auto *editorDocument, cppModelManager()->cppEditorDocuments()) {
auto *processor = editorDocument->processor();
auto *clangProcessor = qobject_cast<ClangEditorDocumentProcessor *>(processor);
if (clangProcessor && clangProcessor->hasProjectPart()) {
if (projectPartIds.contains(clangProcessor->projectPart()->id()))
result.append(clangProcessor);
}
}
return result;
}
void ModelManagerSupportClang::unregisterTranslationUnitsWithProjectParts(
const QStringList &projectPartIds)
{
const auto processors = clangProcessorsWithProjectParts(projectPartIds);
foreach (ClangEditorDocumentProcessor *processor, processors) {
m_communicator.unregisterTranslationUnitsForEditor({processor->fileContainerWithArguments()});
processor->clearProjectPart();
processor->run();
}
}
ModelManagerSupportClang *ModelManagerSupportClang::instance()
{
return m_instance;
}
BackendCommunicator &ModelManagerSupportClang::communicator()
{
return m_communicator;
}
QString ModelManagerSupportClang::dummyUiHeaderOnDiskPath(const QString &filePath) const
{
return m_uiHeaderOnDiskManager.mapPath(filePath);
}
QString ModelManagerSupportClang::dummyUiHeaderOnDiskDirPath() const
{
return m_uiHeaderOnDiskManager.directoryPath();
}
QString ModelManagerSupportProviderClang::id() const
{
return QLatin1String(Constants::CLANG_MODELMANAGERSUPPORT_ID);
}
QString ModelManagerSupportProviderClang::displayName() const
{
//: Display name
return QCoreApplication::translate("ClangCodeModel::Internal::ModelManagerSupport",
"Clang");
}
CppTools::ModelManagerSupport::Ptr ModelManagerSupportProviderClang::createModelManagerSupport()
{
return CppTools::ModelManagerSupport::Ptr(new ModelManagerSupportClang);
}