forked from qt-creator/qt-creator
CppTools: Remove last traces of CppEditorSupport
Superseded by commit
commit 89bd4ee3c4
C++: Base parsing on editor document instead of widget
Change-Id: Iffa6e47bddc0fa3de1eab9b65fe8f711520d2d7f
Reviewed-by: Christian Stenger <christian.stenger@digia.com>
This commit is contained in:
@@ -934,7 +934,7 @@ void CppModelManager::GC()
|
||||
if (!d->m_enableGC)
|
||||
return;
|
||||
|
||||
// Collect files of CppEditorSupport and AbstractEditorSupport.
|
||||
// Collect files of opened editors and editor supports (e.g. ui code model)
|
||||
QStringList filesInEditorSupports;
|
||||
foreach (const EditorDocumentHandle *cppEditor, cppEditors())
|
||||
filesInEditorSupports << cppEditor->filePath();
|
||||
|
||||
@@ -873,11 +873,6 @@ void CppToolsPlugin::test_modelmanager_defines_per_project()
|
||||
QCOMPARE(Core::DocumentModel::openedDocuments().size(), 1);
|
||||
QVERIFY(mm->isCppEditor(editor));
|
||||
|
||||
// CppEditorSupport *sup = mm->cppEditorSupport(
|
||||
// qobject_cast<TextEditor::BaseTextEditor *>(editor));
|
||||
// while (sup->lastSemanticInfoDocument().isNull())
|
||||
// QCoreApplication::processEvents();
|
||||
|
||||
Document::Ptr doc = mm->document(fileName);
|
||||
QCOMPARE(nameOfFirstDeclaration(doc), firstDeclarationName);
|
||||
}
|
||||
|
||||
@@ -1,632 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** 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 Digia. For licensing terms and
|
||||
** conditions see http://www.qt.io/licensing. 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, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "cppcodemodelsettings.h"
|
||||
#include "cppcompletionassistprovider.h"
|
||||
#include "cpptoolseditorsupport.h"
|
||||
#include "cpptoolsplugin.h"
|
||||
#include "cppmodelmanager.h"
|
||||
#include "cpplocalsymbols.h"
|
||||
|
||||
#include <coreplugin/editormanager/editormanager.h>
|
||||
#include <cpptools/cppworkingcopy.h>
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/runextensions.h>
|
||||
|
||||
#include <QList>
|
||||
#include <QMutexLocker>
|
||||
#include <QTextBlock>
|
||||
#include <QTimer>
|
||||
|
||||
using namespace CppTools;
|
||||
using namespace CppTools::Internal;
|
||||
using namespace CPlusPlus;
|
||||
using namespace TextEditor;
|
||||
|
||||
namespace {
|
||||
class FunctionDefinitionUnderCursor: protected ASTVisitor
|
||||
{
|
||||
unsigned _line;
|
||||
unsigned _column;
|
||||
DeclarationAST *_functionDefinition;
|
||||
|
||||
public:
|
||||
FunctionDefinitionUnderCursor(TranslationUnit *translationUnit)
|
||||
: ASTVisitor(translationUnit),
|
||||
_line(0), _column(0)
|
||||
{ }
|
||||
|
||||
DeclarationAST *operator()(AST *ast, unsigned line, unsigned column)
|
||||
{
|
||||
_functionDefinition = 0;
|
||||
_line = line;
|
||||
_column = column;
|
||||
accept(ast);
|
||||
return _functionDefinition;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual bool preVisit(AST *ast)
|
||||
{
|
||||
if (_functionDefinition)
|
||||
return false;
|
||||
|
||||
if (FunctionDefinitionAST *def = ast->asFunctionDefinition())
|
||||
return checkDeclaration(def);
|
||||
|
||||
if (ObjCMethodDeclarationAST *method = ast->asObjCMethodDeclaration()) {
|
||||
if (method->function_body)
|
||||
return checkDeclaration(method);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool checkDeclaration(DeclarationAST *ast)
|
||||
{
|
||||
unsigned startLine, startColumn;
|
||||
unsigned endLine, endColumn;
|
||||
getTokenStartPosition(ast->firstToken(), &startLine, &startColumn);
|
||||
getTokenEndPosition(ast->lastToken() - 1, &endLine, &endColumn);
|
||||
|
||||
if (_line > startLine || (_line == startLine && _column >= startColumn)) {
|
||||
if (_line < endLine || (_line == endLine && _column < endColumn)) {
|
||||
_functionDefinition = ast;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
CppEditorSupport::CppEditorSupport(CppModelManager *modelManager, BaseTextEditor *textEditor)
|
||||
: QObject(modelManager)
|
||||
, m_modelManager(modelManager)
|
||||
, m_textEditor(textEditor)
|
||||
, m_updateDocumentInterval(UpdateDocumentDefaultInterval)
|
||||
, m_revision(0)
|
||||
, m_editorVisible(textEditor->widget()->isVisible())
|
||||
, m_cachedContentsEditorRevision(-1)
|
||||
, m_fileIsBeingReloaded(false)
|
||||
, m_initialized(false)
|
||||
, m_lastHighlightRevision(0)
|
||||
, m_lastHighlightOnCompleteSemanticInfo(true)
|
||||
, m_highlightingSupport(modelManager->highlightingSupport(textEditor->textDocument()))
|
||||
, m_completionAssistProvider(
|
||||
modelManager->completionAssistProvider(textEditor->document()->mimeType()))
|
||||
{
|
||||
m_editorDocument = qobject_cast<BaseTextDocument*>(m_textEditor->document());
|
||||
QTC_CHECK(m_editorDocument);
|
||||
|
||||
connect(m_modelManager, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
|
||||
this, SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr)));
|
||||
|
||||
if (m_highlightingSupport && m_highlightingSupport->requiresSemanticInfo()) {
|
||||
connect(this, SIGNAL(semanticInfoUpdated(CppTools::SemanticInfo)),
|
||||
this, SLOT(startHighlighting()));
|
||||
}
|
||||
|
||||
m_updateDocumentTimer = new QTimer(this);
|
||||
m_updateDocumentTimer->setObjectName(QLatin1String("CppEditorSupport::m_updateDocumentTimer"));
|
||||
m_updateDocumentTimer->setSingleShot(true);
|
||||
m_updateDocumentTimer->setInterval(m_updateDocumentInterval);
|
||||
connect(m_updateDocumentTimer, SIGNAL(timeout()), this, SLOT(updateDocumentNow()));
|
||||
|
||||
m_updateEditorTimer = new QTimer(this);
|
||||
m_updateEditorTimer->setObjectName(QLatin1String("CppEditorSupport::m_updateEditorTimer"));
|
||||
m_updateEditorTimer->setInterval(UpdateEditorInterval);
|
||||
m_updateEditorTimer->setSingleShot(true);
|
||||
connect(m_updateEditorTimer, SIGNAL(timeout()),
|
||||
this, SLOT(updateEditorNow()));
|
||||
|
||||
connect(m_editorDocument, SIGNAL(contentsChanged()), this, SLOT(updateDocument()));
|
||||
connect(this, SIGNAL(diagnosticsChanged()), this, SLOT(onDiagnosticsChanged()));
|
||||
|
||||
connect(m_editorDocument, SIGNAL(mimeTypeChanged()),
|
||||
this, SLOT(onMimeTypeChanged()));
|
||||
|
||||
connect(m_editorDocument, SIGNAL(aboutToReload()),
|
||||
this, SLOT(onAboutToReload()));
|
||||
connect(m_editorDocument, SIGNAL(reloadFinished(bool)),
|
||||
this, SLOT(onReloadFinished()));
|
||||
|
||||
connect(Core::EditorManager::instance(), SIGNAL(currentEditorChanged(Core::IEditor*)),
|
||||
this, SLOT(onCurrentEditorChanged()));
|
||||
m_editorGCTimer = new QTimer(this);
|
||||
m_editorGCTimer->setObjectName(QLatin1String("CppEditorSupport::m_editorGCTimer"));
|
||||
m_editorGCTimer->setSingleShot(true);
|
||||
m_editorGCTimer->setInterval(EditorHiddenGCTimeout);
|
||||
connect(m_editorGCTimer, SIGNAL(timeout()), this, SLOT(releaseResources()));
|
||||
|
||||
updateDocument();
|
||||
}
|
||||
|
||||
CppEditorSupport::~CppEditorSupport()
|
||||
{
|
||||
m_documentParserFuture.cancel();
|
||||
m_highlighter.cancel();
|
||||
m_futureSemanticInfo.cancel();
|
||||
|
||||
m_documentParserFuture.waitForFinished();
|
||||
m_highlighter.waitForFinished();
|
||||
m_futureSemanticInfo.waitForFinished();
|
||||
}
|
||||
|
||||
QString CppEditorSupport::fileName() const
|
||||
{
|
||||
return m_editorDocument->filePath();
|
||||
}
|
||||
|
||||
QByteArray CppEditorSupport::contents() const
|
||||
{
|
||||
QMutexLocker locker(&m_cachedContentsLock);
|
||||
|
||||
const int editorRev = editorRevision();
|
||||
if (m_cachedContentsEditorRevision != editorRev && !m_fileIsBeingReloaded) {
|
||||
m_cachedContentsEditorRevision = editorRev;
|
||||
m_cachedContents = m_editorDocument->plainText().toUtf8();
|
||||
}
|
||||
|
||||
return m_cachedContents;
|
||||
}
|
||||
|
||||
unsigned CppEditorSupport::editorRevision() const
|
||||
{
|
||||
return m_editorDocument->document()->revision();
|
||||
}
|
||||
|
||||
void CppEditorSupport::setExtraDiagnostics(const QString &key,
|
||||
const QList<Document::DiagnosticMessage> &messages)
|
||||
{
|
||||
{
|
||||
QMutexLocker locker(&m_diagnosticsMutex);
|
||||
m_allDiagnostics.insert(key, messages);
|
||||
}
|
||||
|
||||
emit diagnosticsChanged();
|
||||
}
|
||||
|
||||
void CppEditorSupport::setIfdefedOutBlocks(const QList<BlockRange> &ifdefedOutBlocks)
|
||||
{
|
||||
m_editorUpdates.ifdefedOutBlocks = ifdefedOutBlocks;
|
||||
|
||||
emit diagnosticsChanged();
|
||||
}
|
||||
|
||||
bool CppEditorSupport::initialized()
|
||||
{
|
||||
return m_initialized;
|
||||
}
|
||||
|
||||
SemanticInfo CppEditorSupport::recalculateSemanticInfo()
|
||||
{
|
||||
m_futureSemanticInfo.cancel();
|
||||
return recalculateSemanticInfoNow(currentSource(false), /*emitSignalWhenFinished=*/ false);
|
||||
}
|
||||
|
||||
Document::Ptr CppEditorSupport::lastSemanticInfoDocument() const
|
||||
{
|
||||
return semanticInfo().doc;
|
||||
}
|
||||
|
||||
void CppEditorSupport::recalculateSemanticInfoDetached(ForceReason forceReason)
|
||||
{
|
||||
// Block premature calculation caused by CppEditorPlugin::currentEditorChanged
|
||||
// when the editor is created.
|
||||
if (!m_initialized)
|
||||
return;
|
||||
|
||||
m_futureSemanticInfo.cancel();
|
||||
const bool force = forceReason != NoForce;
|
||||
SemanticInfo::Source source = currentSource(force);
|
||||
m_futureSemanticInfo = QtConcurrent::run<CppEditorSupport, void>(
|
||||
&CppEditorSupport::recalculateSemanticInfoDetached_helper, this, source);
|
||||
|
||||
if (force && m_highlightingSupport && !m_highlightingSupport->requiresSemanticInfo())
|
||||
startHighlighting(forceReason);
|
||||
}
|
||||
|
||||
CppCompletionAssistProvider *CppEditorSupport::completionAssistProvider() const
|
||||
{
|
||||
return m_completionAssistProvider;
|
||||
}
|
||||
|
||||
BuiltinEditorDocumentParser::Ptr CppEditorSupport::documentParser()
|
||||
{
|
||||
BuiltinEditorDocumentParser::Ptr updater = documentParser_internal();
|
||||
if (!updater || updater->filePath() != fileName()) {
|
||||
updater = BuiltinEditorDocumentParser::Ptr(new BuiltinEditorDocumentParser(fileName()));
|
||||
setDocumentParser_internal(updater);
|
||||
|
||||
QSharedPointer<CppCodeModelSettings> cms = CppToolsPlugin::instance()->codeModelSettings();
|
||||
updater->setUsePrecompiledHeaders(cms->pchUsage() != CppCodeModelSettings::PchUse_None);
|
||||
}
|
||||
return updater;
|
||||
}
|
||||
|
||||
void CppEditorSupport::updateDocument()
|
||||
{
|
||||
m_revision = editorRevision();
|
||||
|
||||
if (qobject_cast<BaseTextEditorWidget*>(m_textEditor->widget()) != 0)
|
||||
m_updateEditorTimer->stop();
|
||||
|
||||
m_updateDocumentTimer->start(m_updateDocumentInterval);
|
||||
}
|
||||
|
||||
static void parse(QFutureInterface<void> &future, BuiltinEditorDocumentParser::Ptr updater,
|
||||
WorkingCopy workingCopy)
|
||||
{
|
||||
future.setProgressRange(0, 1);
|
||||
if (future.isCanceled()) {
|
||||
future.setProgressValue(1);
|
||||
return;
|
||||
}
|
||||
|
||||
CppModelManager *cmm = qobject_cast<CppModelManager *>(CppModelManager::instance());
|
||||
updater->update(workingCopy);
|
||||
cmm->finishedRefreshingSourceFiles(QStringList(updater->filePath()));
|
||||
|
||||
future.setProgressValue(1);
|
||||
}
|
||||
|
||||
void CppEditorSupport::updateDocumentNow()
|
||||
{
|
||||
if (m_documentParserFuture.isRunning() || m_revision != editorRevision()) {
|
||||
m_updateDocumentTimer->start(m_updateDocumentInterval);
|
||||
} else {
|
||||
m_updateDocumentTimer->stop();
|
||||
|
||||
if (m_fileIsBeingReloaded || fileName().isEmpty())
|
||||
return;
|
||||
|
||||
if (m_highlightingSupport && !m_highlightingSupport->requiresSemanticInfo())
|
||||
startHighlighting();
|
||||
|
||||
m_documentParserFuture = QtConcurrent::run(&parse, documentParser(),
|
||||
CppModelManager::instance()->workingCopy());
|
||||
}
|
||||
}
|
||||
|
||||
bool CppEditorSupport::isUpdatingDocument()
|
||||
{
|
||||
return m_updateDocumentTimer->isActive() || m_documentParserFuture.isRunning();
|
||||
}
|
||||
|
||||
void CppEditorSupport::onDocumentUpdated(Document::Ptr doc)
|
||||
{
|
||||
if (doc.isNull())
|
||||
return;
|
||||
|
||||
if (doc->fileName() != fileName())
|
||||
return; // some other document got updated
|
||||
|
||||
if (doc->editorRevision() != editorRevision())
|
||||
return; // outdated content, wait for a new document to be parsed
|
||||
|
||||
// Update the ifdeffed-out blocks:
|
||||
if (m_highlightingSupport && !m_highlightingSupport->hightlighterHandlesIfdefedOutBlocks()) {
|
||||
QList<Document::Block> skippedBlocks = doc->skippedBlocks();
|
||||
QList<BlockRange> ifdefedOutBlocks;
|
||||
ifdefedOutBlocks.reserve(skippedBlocks.size());
|
||||
foreach (const Document::Block &block, skippedBlocks)
|
||||
ifdefedOutBlocks.append(BlockRange(block.utf16charsBegin(), block.utf16charsEnd()));
|
||||
setIfdefedOutBlocks(ifdefedOutBlocks);
|
||||
}
|
||||
|
||||
if (m_highlightingSupport && !m_highlightingSupport->hightlighterHandlesDiagnostics()) {
|
||||
// Update the parser errors/warnings:
|
||||
static const QString key = QLatin1String("CppTools.ParserDiagnostics");
|
||||
setExtraDiagnostics(key, doc->diagnosticMessages());
|
||||
}
|
||||
|
||||
// Update semantic info if necessary
|
||||
if (!m_initialized || (m_textEditor->widget()->isVisible() && !isSemanticInfoValid())) {
|
||||
m_initialized = true;
|
||||
recalculateSemanticInfoDetached(ForceDueToInvalidSemanticInfo);
|
||||
}
|
||||
|
||||
// Notify the editor that the document is updated
|
||||
emit documentUpdated();
|
||||
}
|
||||
|
||||
void CppEditorSupport::startHighlighting(ForceReason forceReason)
|
||||
{
|
||||
if (!m_highlightingSupport)
|
||||
return;
|
||||
|
||||
if (m_highlightingSupport->requiresSemanticInfo()) {
|
||||
const SemanticInfo info = semanticInfo();
|
||||
if (info.doc.isNull())
|
||||
return;
|
||||
|
||||
const bool forced = info.forced || !m_lastHighlightOnCompleteSemanticInfo;
|
||||
if (!forced && m_lastHighlightRevision == info.revision)
|
||||
return;
|
||||
|
||||
m_highlighter.cancel();
|
||||
m_highlighter = m_highlightingSupport->highlightingFuture(info.doc, info.snapshot);
|
||||
m_lastHighlightRevision = info.revision;
|
||||
m_lastHighlightOnCompleteSemanticInfo = info.complete;
|
||||
emit highlighterStarted(&m_highlighter, m_lastHighlightRevision);
|
||||
} else {
|
||||
const unsigned revision = editorRevision();
|
||||
if (forceReason != ForceDueEditorRequest && m_lastHighlightRevision == revision)
|
||||
return;
|
||||
|
||||
m_highlighter.cancel();
|
||||
static const Document::Ptr dummyDoc;
|
||||
static const Snapshot dummySnapshot;
|
||||
m_highlighter = m_highlightingSupport->highlightingFuture(dummyDoc, dummySnapshot);
|
||||
m_lastHighlightRevision = revision;
|
||||
emit highlighterStarted(&m_highlighter, m_lastHighlightRevision);
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief This slot puts the new diagnostics into the editorUpdates. This function has to be called
|
||||
/// on the UI thread.
|
||||
void CppEditorSupport::onDiagnosticsChanged()
|
||||
{
|
||||
QList<Document::DiagnosticMessage> allDiagnostics;
|
||||
{
|
||||
QMutexLocker locker(&m_diagnosticsMutex);
|
||||
foreach (const QList<Document::DiagnosticMessage> &msgs, m_allDiagnostics)
|
||||
allDiagnostics.append(msgs);
|
||||
}
|
||||
|
||||
if (!m_textEditor)
|
||||
return;
|
||||
|
||||
// set up the format for the errors
|
||||
QTextCharFormat errorFormat;
|
||||
errorFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline);
|
||||
errorFormat.setUnderlineColor(Qt::red);
|
||||
|
||||
// set up the format for the warnings.
|
||||
QTextCharFormat warningFormat;
|
||||
warningFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline);
|
||||
warningFormat.setUnderlineColor(Qt::darkYellow);
|
||||
|
||||
QTextDocument *doc = m_editorDocument->document();
|
||||
|
||||
m_editorUpdates.selections.clear();
|
||||
foreach (const Document::DiagnosticMessage &m, allDiagnostics) {
|
||||
QTextEdit::ExtraSelection sel;
|
||||
if (m.isWarning())
|
||||
sel.format = warningFormat;
|
||||
else
|
||||
sel.format = errorFormat;
|
||||
|
||||
QTextCursor c(doc->findBlockByNumber(m.line() - 1));
|
||||
const QString text = c.block().text();
|
||||
if (m.length() > 0 && m.column() + m.length() < (unsigned)text.size()) {
|
||||
int column = m.column() > 0 ? m.column() - 1 : 0;
|
||||
c.setPosition(c.position() + column);
|
||||
c.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, m.length());
|
||||
} else {
|
||||
for (int i = 0; i < text.size(); ++i) {
|
||||
if (!text.at(i).isSpace()) {
|
||||
c.setPosition(c.position() + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
c.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
|
||||
}
|
||||
sel.cursor = c;
|
||||
sel.format.setToolTip(m.text());
|
||||
m_editorUpdates.selections.append(sel);
|
||||
}
|
||||
|
||||
m_editorUpdates.revision = doc->revision();
|
||||
|
||||
updateEditor();
|
||||
}
|
||||
void CppEditorSupport::updateEditor()
|
||||
{
|
||||
m_updateEditorTimer->start(UpdateEditorInterval);
|
||||
}
|
||||
|
||||
void CppEditorSupport::updateEditorNow()
|
||||
{
|
||||
if (!m_textEditor || m_editorUpdates.revision == -1)
|
||||
return;
|
||||
|
||||
if (editorRevision() != (unsigned) m_editorUpdates.revision)
|
||||
return; // outdated
|
||||
|
||||
BaseTextEditorWidget *editorWidget = m_textEditor->editorWidget();
|
||||
editorWidget->setExtraSelections(BaseTextEditorWidget::CodeWarningsSelection,
|
||||
m_editorUpdates.selections);
|
||||
editorWidget->setIfdefedOutBlocks(m_editorUpdates.ifdefedOutBlocks);
|
||||
}
|
||||
|
||||
void CppEditorSupport::onCurrentEditorChanged()
|
||||
{
|
||||
bool editorVisible = m_textEditor->widget()->isVisible();
|
||||
|
||||
if (m_editorVisible != editorVisible) {
|
||||
m_editorVisible = editorVisible;
|
||||
if (editorVisible) {
|
||||
m_editorGCTimer->stop();
|
||||
if (!lastSemanticInfoDocument())
|
||||
updateDocumentNow();
|
||||
} else {
|
||||
m_editorGCTimer->start(EditorHiddenGCTimeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CppEditorSupport::releaseResources()
|
||||
{
|
||||
m_highlighter.cancel();
|
||||
m_highlighter = QFuture<TextEditor::HighlightingResult>();
|
||||
documentParser()->releaseResources();
|
||||
setSemanticInfo(SemanticInfo(), /*emitSignal=*/ false);
|
||||
m_lastHighlightOnCompleteSemanticInfo = true;
|
||||
}
|
||||
|
||||
SemanticInfo::Source CppEditorSupport::currentSource(bool force)
|
||||
{
|
||||
int line = 0, column = 0;
|
||||
m_textEditor->convertPosition(m_textEditor->editorWidget()->position(), &line, &column);
|
||||
|
||||
return SemanticInfo::Source(Snapshot(), fileName(), contents(), line, column, editorRevision(),
|
||||
force);
|
||||
}
|
||||
|
||||
SemanticInfo CppEditorSupport::recalculateSemanticInfoNow(const SemanticInfo::Source &source,
|
||||
bool emitSignalWhenFinished,
|
||||
FuturizedTopLevelDeclarationProcessor *processor)
|
||||
{
|
||||
const SemanticInfo lastSemanticInfo = semanticInfo();
|
||||
SemanticInfo newSemanticInfo;
|
||||
|
||||
newSemanticInfo.forced = source.force;
|
||||
newSemanticInfo.revision = source.revision;
|
||||
|
||||
// Try to reuse as much as possible from the last semantic info
|
||||
if (!source.force
|
||||
&& lastSemanticInfo.complete
|
||||
&& lastSemanticInfo.revision == source.revision
|
||||
&& lastSemanticInfo.doc
|
||||
&& lastSemanticInfo.doc->translationUnit()->ast()
|
||||
&& lastSemanticInfo.doc->fileName() == source.fileName) {
|
||||
newSemanticInfo.snapshot = lastSemanticInfo.snapshot; // ### TODO: use the new snapshot.
|
||||
newSemanticInfo.doc = lastSemanticInfo.doc;
|
||||
|
||||
// Otherwise reprocess document
|
||||
} else {
|
||||
const BuiltinEditorDocumentParser::Ptr documentParser = documentParser_internal();
|
||||
QTC_ASSERT(documentParser, return newSemanticInfo);
|
||||
newSemanticInfo.snapshot = documentParser->snapshot();
|
||||
if (!newSemanticInfo.snapshot.contains(source.fileName))
|
||||
return newSemanticInfo; // BuiltinEditorDocumentParser::update() not yet started.
|
||||
Document::Ptr doc = newSemanticInfo.snapshot.preprocessedDocument(source.code,
|
||||
source.fileName);
|
||||
if (processor)
|
||||
doc->control()->setTopLevelDeclarationProcessor(processor);
|
||||
doc->check();
|
||||
if (processor && processor->isCanceled())
|
||||
newSemanticInfo.complete = false;
|
||||
newSemanticInfo.doc = doc;
|
||||
}
|
||||
|
||||
// Update local uses for the document
|
||||
TranslationUnit *translationUnit = newSemanticInfo.doc->translationUnit();
|
||||
AST *ast = translationUnit->ast();
|
||||
FunctionDefinitionUnderCursor functionDefinitionUnderCursor(newSemanticInfo.doc->translationUnit());
|
||||
const LocalSymbols useTable(newSemanticInfo.doc,
|
||||
functionDefinitionUnderCursor(ast, source.line, source.column));
|
||||
newSemanticInfo.localUses = useTable.uses;
|
||||
|
||||
// Update semantic info
|
||||
setSemanticInfo(newSemanticInfo, emitSignalWhenFinished);
|
||||
|
||||
return newSemanticInfo;
|
||||
}
|
||||
|
||||
void CppEditorSupport::recalculateSemanticInfoDetached_helper(QFutureInterface<void> &future, SemanticInfo::Source source)
|
||||
{
|
||||
FuturizedTopLevelDeclarationProcessor processor(future);
|
||||
recalculateSemanticInfoNow(source, true, &processor);
|
||||
}
|
||||
|
||||
bool CppEditorSupport::isSemanticInfoValid() const
|
||||
{
|
||||
const Document::Ptr document = lastSemanticInfoDocument();
|
||||
return document
|
||||
&& document->translationUnit()->ast()
|
||||
&& document->fileName() == fileName();
|
||||
}
|
||||
|
||||
SemanticInfo CppEditorSupport::semanticInfo() const
|
||||
{
|
||||
QMutexLocker locker(&m_lastSemanticInfoLock);
|
||||
return m_lastSemanticInfo;
|
||||
}
|
||||
|
||||
void CppEditorSupport::setSemanticInfo(const SemanticInfo &semanticInfo, bool emitSignal)
|
||||
{
|
||||
{
|
||||
QMutexLocker locker(&m_lastSemanticInfoLock);
|
||||
m_lastSemanticInfo = semanticInfo;
|
||||
}
|
||||
if (emitSignal)
|
||||
emit semanticInfoUpdated(semanticInfo);
|
||||
}
|
||||
|
||||
BuiltinEditorDocumentParser::Ptr CppEditorSupport::documentParser_internal() const
|
||||
{
|
||||
QMutexLocker locker(&m_documentParserLock);
|
||||
return m_documentParser;
|
||||
}
|
||||
|
||||
void CppEditorSupport::setDocumentParser_internal(const BuiltinEditorDocumentParser::Ptr &updater)
|
||||
{
|
||||
QMutexLocker locker(&m_documentParserLock);
|
||||
m_documentParser = updater;
|
||||
}
|
||||
|
||||
void CppEditorSupport::onMimeTypeChanged()
|
||||
{
|
||||
m_highlighter.cancel();
|
||||
m_highlighter.waitForFinished();
|
||||
|
||||
m_highlightingSupport.reset(m_modelManager->highlightingSupport(m_editorDocument));
|
||||
|
||||
disconnect(this, SIGNAL(semanticInfoUpdated(CppTools::SemanticInfo)),
|
||||
this, SLOT(startHighlighting()));
|
||||
if (m_highlightingSupport && m_highlightingSupport->requiresSemanticInfo())
|
||||
connect(this, SIGNAL(semanticInfoUpdated(CppTools::SemanticInfo)),
|
||||
this, SLOT(startHighlighting()));
|
||||
|
||||
m_completionAssistProvider
|
||||
= m_modelManager->completionAssistProvider(m_editorDocument->mimeType());
|
||||
|
||||
updateDocumentNow();
|
||||
}
|
||||
|
||||
void CppEditorSupport::onAboutToReload()
|
||||
{
|
||||
QTC_CHECK(!m_fileIsBeingReloaded);
|
||||
m_fileIsBeingReloaded = true;
|
||||
}
|
||||
|
||||
void CppEditorSupport::onReloadFinished()
|
||||
{
|
||||
QTC_CHECK(m_fileIsBeingReloaded);
|
||||
m_fileIsBeingReloaded = false;
|
||||
updateDocument();
|
||||
}
|
||||
@@ -1,253 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** 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 Digia. For licensing terms and
|
||||
** conditions see http://www.qt.io/licensing. 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, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef CPPTOOLSEDITORSUPPORT_H
|
||||
#define CPPTOOLSEDITORSUPPORT_H
|
||||
|
||||
#include "builtineditordocumentparser.h"
|
||||
#include "cpphighlightingsupport.h"
|
||||
#include "cppmodelmanager.h"
|
||||
#include "cppsemanticinfo.h"
|
||||
|
||||
#include <cplusplus/Control.h>
|
||||
#include <cplusplus/CppDocument.h>
|
||||
|
||||
#include <QFuture>
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
#include <QSharedPointer>
|
||||
#include <QTimer>
|
||||
|
||||
namespace CPlusPlus {
|
||||
class AST;
|
||||
class DeclarationAST;
|
||||
} // namespace CPlusPlus
|
||||
|
||||
namespace TextEditor {
|
||||
class BaseTextEditor;
|
||||
class TextMark;
|
||||
} // namespace TextEditor
|
||||
|
||||
namespace CppTools {
|
||||
|
||||
class CppCompletionAssistProvider;
|
||||
|
||||
/**
|
||||
* \brief The CppEditorSupport class oversees the actions that happen when a C++ text editor updates
|
||||
* its document.
|
||||
*
|
||||
* The following steps are taken:
|
||||
* 1. the text editor document fires a contentsChanged() signal that triggers updateDocument
|
||||
* 2. update document will start a timer, or reset the timer if it was already running. This way
|
||||
* subsequent updates (e.g. keypresses) get bunched together instead of running the subsequent
|
||||
* actions for every key press
|
||||
* 3. when the timer from step 2 fires, updateDocumentNow() is triggered. That tells the
|
||||
* model-manager to update the CPlusPlus::Document by re-indexing it.
|
||||
* 4. when the model-manager finishes, it fires a documentUpdated(CPlusPlus::Document::Ptr) signal,
|
||||
* that is connected to onDocumentUpdated(CPlusPlus::Document::Ptr), which does 4 things:
|
||||
* a) updates the ifdeffed-out blocks in the EditorUpdate
|
||||
* b) calls setExtraDiagnostics with the diagnostics from the parser, which in turn calls
|
||||
* onDiagnosticsChanged on the UI thread, and that schedules an editor update timer. When this
|
||||
* timer fires, updateEditorNow() is called, which will apply the updates to the editor.
|
||||
* c) a semantic-info recalculation is started in a future
|
||||
* d) the documentUpdated() signal is emitted, which can be used by a widget to do things
|
||||
* 5. semantic-info calculation from 4c is done by a future that calls recalculateSemanticInfoNow(),
|
||||
* which emits semanticInfoUpdated() when it is finished. Editors can also listen in on this
|
||||
* signal to do things like highlighting the local usages.
|
||||
* 6. the semanticInfoUpdated() is connected to the startHighlighting() slot, which will start
|
||||
* another future for doing the semantic highlighting. The highlighterStarted signal is emitted,
|
||||
* with the highlighting future as a parameter, so editors can hook it up to a QFutureWatcher
|
||||
* and get notifications.
|
||||
*
|
||||
* Both the semantic info calculation and the highlighting calculation will cancel an already running
|
||||
* future. They will also check that the result of a previous step is not already outdated, meaning
|
||||
* that they check the revision of the editor document to see if a user changed the document while
|
||||
* the calculation was running.
|
||||
*/
|
||||
class CPPTOOLS_EXPORT CppEditorSupport: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
typedef TextEditor::BlockRange BlockRange;
|
||||
|
||||
public:
|
||||
CppEditorSupport(Internal::CppModelManager *modelManager, TextEditor::BaseTextEditor *textEditor);
|
||||
virtual ~CppEditorSupport();
|
||||
|
||||
QString fileName() const;
|
||||
|
||||
QByteArray contents() const;
|
||||
unsigned editorRevision() const;
|
||||
|
||||
void setExtraDiagnostics(const QString &key,
|
||||
const QList<CPlusPlus::Document::DiagnosticMessage> &messages);
|
||||
void setIfdefedOutBlocks(const QList<BlockRange> &ifdefedOutBlocks);
|
||||
|
||||
/// True after the document was parsed/updated for the first time
|
||||
/// and the first semantic info calculation was started.
|
||||
bool initialized();
|
||||
|
||||
/// Retrieve the semantic info, which will get recalculated on the current
|
||||
/// thread if it is outdate. Will not emit the semanticInfoUpdated() signal.
|
||||
SemanticInfo recalculateSemanticInfo();
|
||||
|
||||
CPlusPlus::Document::Ptr lastSemanticInfoDocument() const;
|
||||
|
||||
enum ForceReason {
|
||||
NoForce,
|
||||
ForceDueToInvalidSemanticInfo,
|
||||
ForceDueEditorRequest
|
||||
};
|
||||
|
||||
/// Recalculates the semantic info in a future, and will emit the
|
||||
/// semanticInfoUpdated() signal when finished.
|
||||
/// Requires that initialized() is true.
|
||||
/// \param forceReason the reason to force, if any
|
||||
void recalculateSemanticInfoDetached(ForceReason forceReason);
|
||||
|
||||
CppCompletionAssistProvider *completionAssistProvider() const;
|
||||
|
||||
BuiltinEditorDocumentParser::Ptr documentParser();
|
||||
|
||||
/// Checks whether the document is (re)parsed or about to be (re)parsed.
|
||||
bool isUpdatingDocument();
|
||||
|
||||
signals:
|
||||
void documentUpdated();
|
||||
void diagnosticsChanged();
|
||||
void semanticInfoUpdated(CppTools::SemanticInfo);
|
||||
void highlighterStarted(QFuture<TextEditor::HighlightingResult> *, unsigned revision);
|
||||
|
||||
private slots:
|
||||
void onMimeTypeChanged();
|
||||
|
||||
void onAboutToReload();
|
||||
void onReloadFinished();
|
||||
|
||||
void updateDocument();
|
||||
void updateDocumentNow();
|
||||
|
||||
void onDocumentUpdated(CPlusPlus::Document::Ptr doc);
|
||||
void startHighlighting(ForceReason forceReason = NoForce);
|
||||
|
||||
void onDiagnosticsChanged();
|
||||
|
||||
void updateEditor();
|
||||
void updateEditorNow();
|
||||
|
||||
void onCurrentEditorChanged();
|
||||
void releaseResources();
|
||||
|
||||
private:
|
||||
struct EditorUpdates {
|
||||
EditorUpdates()
|
||||
: revision(-1)
|
||||
{}
|
||||
int revision;
|
||||
QList<QTextEdit::ExtraSelection> selections;
|
||||
QList<BlockRange> ifdefedOutBlocks;
|
||||
};
|
||||
|
||||
enum {
|
||||
UpdateDocumentDefaultInterval = 150,
|
||||
UpdateEditorInterval = 300,
|
||||
EditorHiddenGCTimeout = 2 * 60 * 1000 // 2 minutes
|
||||
};
|
||||
|
||||
private:
|
||||
class FuturizedTopLevelDeclarationProcessor: public CPlusPlus::TopLevelDeclarationProcessor
|
||||
{
|
||||
public:
|
||||
FuturizedTopLevelDeclarationProcessor(QFutureInterface<void> &future): m_future(future) {}
|
||||
bool processDeclaration(CPlusPlus::DeclarationAST *) { return !isCanceled(); }
|
||||
bool isCanceled() { return m_future.isCanceled(); }
|
||||
private:
|
||||
QFutureInterface<void> m_future;
|
||||
};
|
||||
|
||||
SemanticInfo::Source currentSource(bool force);
|
||||
SemanticInfo recalculateSemanticInfoNow(const SemanticInfo::Source &source,
|
||||
bool emitSignalWhenFinished,
|
||||
FuturizedTopLevelDeclarationProcessor *processor = 0);
|
||||
void recalculateSemanticInfoDetached_helper(QFutureInterface<void> &future,
|
||||
SemanticInfo::Source source);
|
||||
|
||||
bool isSemanticInfoValid() const;
|
||||
SemanticInfo semanticInfo() const;
|
||||
void setSemanticInfo(const SemanticInfo &semanticInfo, bool emitSignal = true);
|
||||
|
||||
BuiltinEditorDocumentParser::Ptr documentParser_internal() const;
|
||||
void setDocumentParser_internal(const BuiltinEditorDocumentParser::Ptr &updater);
|
||||
|
||||
private:
|
||||
Internal::CppModelManager *m_modelManager;
|
||||
QPointer<TextEditor::BaseTextEditor> m_textEditor;
|
||||
TextEditor::BaseTextDocument *m_editorDocument;
|
||||
QTimer *m_updateDocumentTimer;
|
||||
int m_updateDocumentInterval;
|
||||
unsigned m_revision;
|
||||
|
||||
QTimer *m_editorGCTimer;
|
||||
bool m_editorVisible;
|
||||
|
||||
// content caching
|
||||
mutable QMutex m_cachedContentsLock;
|
||||
mutable QByteArray m_cachedContents;
|
||||
mutable int m_cachedContentsEditorRevision;
|
||||
bool m_fileIsBeingReloaded;
|
||||
|
||||
QTimer *m_updateEditorTimer;
|
||||
EditorUpdates m_editorUpdates;
|
||||
|
||||
QMutex m_diagnosticsMutex;
|
||||
QHash<QString, QList<CPlusPlus::Document::DiagnosticMessage> > m_allDiagnostics;
|
||||
|
||||
// Semantic info:
|
||||
bool m_initialized;
|
||||
mutable QMutex m_lastSemanticInfoLock;
|
||||
SemanticInfo m_lastSemanticInfo;
|
||||
QFuture<void> m_futureSemanticInfo;
|
||||
mutable QMutex m_documentParserLock;
|
||||
BuiltinEditorDocumentParser::Ptr m_documentParser;
|
||||
QFuture<void> m_documentParserFuture;
|
||||
|
||||
// Highlighting:
|
||||
unsigned m_lastHighlightRevision;
|
||||
bool m_lastHighlightOnCompleteSemanticInfo;
|
||||
QFuture<TextEditor::HighlightingResult> m_highlighter;
|
||||
QScopedPointer<CppTools::CppHighlightingSupport> m_highlightingSupport;
|
||||
|
||||
// Completion:
|
||||
CppCompletionAssistProvider *m_completionAssistProvider;
|
||||
};
|
||||
|
||||
} // namespace CppTools
|
||||
|
||||
#endif // CPPTOOLSEDITORSUPPORT_H
|
||||
Reference in New Issue
Block a user