diff options
author | hjk <hjk@qt.io> | 2017-02-24 16:09:39 +0100 |
---|---|---|
committer | Tobias Hunger <tobias.hunger@qt.io> | 2017-02-24 16:49:18 +0000 |
commit | 3e225628ee6967d224fcdcc30e42015c9be74862 (patch) | |
tree | 09a681abe98f5fa683196af31923e5c17182e256 /src/plugins/clangcodemodel | |
parent | 92d818f327b43c50411f4ffffca30700d82024a0 (diff) | |
download | qt-creator-3e225628ee6967d224fcdcc30e42015c9be74862.tar.gz |
Revert "Clang: Add possibility to "pgo-train" libclang with a batch file"
This reverts commit 07f4ae622770cc99782edc8bf14d2a385bf17778,
which broke compilation
clangbatchfileprocessor.cpp:170:27: error: ambiguous overload for
'operator==' (operand types are 'const QChar' and 'char')
and only worked in QT_TEST=1 cases.
Change-Id: I089427359958221882cb4e4369c4b88d71779acf
Reviewed-by: Tobias Hunger <tobias.hunger@qt.io>
Diffstat (limited to 'src/plugins/clangcodemodel')
-rw-r--r-- | src/plugins/clangcodemodel/clangautomationutils.cpp | 140 | ||||
-rw-r--r-- | src/plugins/clangcodemodel/clangautomationutils.h | 46 | ||||
-rw-r--r-- | src/plugins/clangcodemodel/clangbatchfileprocessor.cpp | 799 | ||||
-rw-r--r-- | src/plugins/clangcodemodel/clangbatchfileprocessor.h | 36 | ||||
-rw-r--r-- | src/plugins/clangcodemodel/clangcodemodel.pro | 4 | ||||
-rw-r--r-- | src/plugins/clangcodemodel/clangcodemodel.qbs | 4 | ||||
-rw-r--r-- | src/plugins/clangcodemodel/clangcodemodelplugin.cpp | 20 | ||||
-rw-r--r-- | src/plugins/clangcodemodel/clangcodemodelplugin.h | 3 | ||||
-rw-r--r-- | src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp | 99 |
9 files changed, 99 insertions, 1052 deletions
diff --git a/src/plugins/clangcodemodel/clangautomationutils.cpp b/src/plugins/clangcodemodel/clangautomationutils.cpp deleted file mode 100644 index 29dc1c7155..0000000000 --- a/src/plugins/clangcodemodel/clangautomationutils.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 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 "clangautomationutils.h" - -#include "clangcompletionassistinterface.h" -#include "clangcompletionassistprovider.h" - -#include <texteditor/codeassist/assistinterface.h> -#include <texteditor/codeassist/assistproposalitem.h> -#include <texteditor/codeassist/completionassistprovider.h> -#include <texteditor/codeassist/genericproposalmodel.h> -#include <texteditor/codeassist/iassistprocessor.h> -#include <texteditor/codeassist/iassistproposal.h> -#include <texteditor/textdocument.h> -#include <texteditor/texteditor.h> - -#include <utils/qtcassert.h> - -#include <QCoreApplication> -#include <QElapsedTimer> - -namespace ClangCodeModel { -namespace Internal { - -class WaitForAsyncCompletions -{ -public: - enum WaitResult { GotResults, GotInvalidResults, Timeout }; - - WaitResult wait(TextEditor::IAssistProcessor *processor, - TextEditor::AssistInterface *assistInterface, - int timeoutInMs) - { - QTC_ASSERT(processor, return Timeout); - QTC_ASSERT(assistInterface, return Timeout); - - bool gotResults = false; - - processor->setAsyncCompletionAvailableHandler( - [this, &gotResults] (TextEditor::IAssistProposal *proposal) { - QTC_ASSERT(proposal, return); - proposalModel = proposal->model(); - delete proposal; - gotResults = true; - }); - - // Are there any immediate results? - if (TextEditor::IAssistProposal *proposal = processor->perform(assistInterface)) { - delete processor; - proposalModel = proposal->model(); - delete proposal; - QTC_ASSERT(proposalModel, return GotInvalidResults); - return GotResults; - } - - // There are not any, so wait for async results. - QElapsedTimer timer; - timer.start(); - while (!gotResults) { - if (timer.elapsed() >= timeoutInMs) - return Timeout; - QCoreApplication::processEvents(); - } - - return proposalModel ? GotResults : GotInvalidResults; - } - -public: - TextEditor::IAssistProposalModel *proposalModel; -}; - -static const CppTools::ProjectPartHeaderPaths toHeaderPaths(const QStringList &paths) -{ - using namespace CppTools; - - ProjectPartHeaderPaths result; - foreach (const QString &path, paths) - result << ProjectPartHeaderPath(path, ProjectPartHeaderPath::IncludePath); - return result; -} - -ProposalModel completionResults(TextEditor::BaseTextEditor *textEditor, - const QStringList &includePaths, - int timeOutInMs) -{ - using namespace TextEditor; - - TextEditorWidget *textEditorWidget = qobject_cast<TextEditorWidget *>(textEditor->widget()); - QTC_ASSERT(textEditorWidget, return ProposalModel()); - AssistInterface *assistInterface = textEditorWidget->createAssistInterface( - TextEditor::Completion, TextEditor::ExplicitlyInvoked); - QTC_ASSERT(assistInterface, return ProposalModel()); - if (!includePaths.isEmpty()) { - auto clangAssistInterface = static_cast<ClangCompletionAssistInterface *>(assistInterface); - clangAssistInterface->setHeaderPaths(toHeaderPaths(includePaths)); - } - - CompletionAssistProvider *assistProvider - = textEditor->textDocument()->completionAssistProvider(); - QTC_ASSERT(qobject_cast<ClangCompletionAssistProvider *>(assistProvider), - return ProposalModel()); - QTC_ASSERT(assistProvider, return ProposalModel()); - QTC_ASSERT(assistProvider->runType() == IAssistProvider::Asynchronous, return ProposalModel()); - - IAssistProcessor *processor = assistProvider->createProcessor(); - QTC_ASSERT(processor, return ProposalModel()); - - WaitForAsyncCompletions waitForCompletions; - const WaitForAsyncCompletions::WaitResult result = waitForCompletions.wait(processor, - assistInterface, - timeOutInMs); - QTC_ASSERT(result == WaitForAsyncCompletions::GotResults, return ProposalModel()); - return QSharedPointer<TextEditor::IAssistProposalModel>(waitForCompletions.proposalModel); -} - -} // namespace Internal -} // namespace ClangCodeModel diff --git a/src/plugins/clangcodemodel/clangautomationutils.h b/src/plugins/clangcodemodel/clangautomationutils.h deleted file mode 100644 index 9942a597fd..0000000000 --- a/src/plugins/clangcodemodel/clangautomationutils.h +++ /dev/null @@ -1,46 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 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. -** -****************************************************************************/ - -#pragma once - -#include <QString> -#include <QSharedPointer> - -namespace TextEditor { -class BaseTextEditor; -class IAssistProposalModel; -} - -namespace ClangCodeModel { -namespace Internal { - -using ProposalModel = QSharedPointer<TextEditor::IAssistProposalModel>; - -ProposalModel completionResults(TextEditor::BaseTextEditor *textEditor, - const QStringList &includePaths = QStringList(), - int timeOutInMs = 10000); - -} // namespace Internal -} // namespace ClangCodeModel diff --git a/src/plugins/clangcodemodel/clangbatchfileprocessor.cpp b/src/plugins/clangcodemodel/clangbatchfileprocessor.cpp deleted file mode 100644 index 1e6557d36d..0000000000 --- a/src/plugins/clangcodemodel/clangbatchfileprocessor.cpp +++ /dev/null @@ -1,799 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 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 "clangbatchfileprocessor.h" - -#include "clangautomationutils.h" - -#include <clangcodemodel/clangeditordocumentprocessor.h> - -#include <coreplugin/editormanager/editormanager.h> -#include <coreplugin/editormanager/ieditor.h> -#include <coreplugin/icore.h> -#include <cpptools/cpptoolsreuse.h> -#include <cpptools/cpptoolstestcase.h> -#include <cpptools/modelmanagertesthelper.h> -#include <cpptools/projectinfo.h> -#include <projectexplorer/projectexplorer.h> -#include <texteditor/codeassist/assistinterface.h> -#include <texteditor/codeassist/assistproposalitem.h> -#include <texteditor/codeassist/completionassistprovider.h> -#include <texteditor/codeassist/genericproposalmodel.h> -#include <texteditor/codeassist/iassistprocessor.h> -#include <texteditor/codeassist/iassistproposal.h> -#include <texteditor/textdocument.h> -#include <texteditor/texteditor.h> - -#include <utils/executeondestruction.h> -#include <utils/qtcassert.h> - -#include <QDebug> -#include <QFileInfo> -#include <QLoggingCategory> -#include <QSharedPointer> -#include <QString> -#include <QtTest> - -using namespace ClangBackEnd; -using namespace ClangCodeModel; -using namespace ClangCodeModel::Internal; -using namespace ProjectExplorer; - -static Q_LOGGING_CATEGORY(debug, "qtc.clangcodemodel.batch"); - -static int timeOutFromEnvironmentVariable() -{ - const QByteArray timeoutAsByteArray = qgetenv("QTC_CLANG_BATCH_TIMEOUT"); - - bool isConversionOk = false; - const int intervalAsInt = timeoutAsByteArray.toInt(&isConversionOk); - if (!isConversionOk) { - qCDebug(debug, "Environment variable QTC_CLANG_BATCH_TIMEOUT is not set, assuming 30000."); - return 30000; - } - - return intervalAsInt; -} - -static int timeOutInMs() -{ - static int timeOut = timeOutFromEnvironmentVariable(); - return timeOut; -} - -namespace { - -class BatchFileLineTokenizer -{ -public: - BatchFileLineTokenizer(const QString &line); - - QString nextToken(); - -private: - const QChar *advanceToTokenBegin(); - const QChar *advanceToTokenEnd(); - - bool atEnd() const; - bool atWhiteSpace() const; - bool atQuotationMark() const; - -private: - bool m_isWithinQuotation = false; - QString m_line; - const QChar *m_currentChar; -}; - -BatchFileLineTokenizer::BatchFileLineTokenizer(const QString &line) - : m_line(line) - , m_currentChar(m_line.unicode()) -{ -} - -QString BatchFileLineTokenizer::nextToken() -{ - if (const QChar *tokenBegin = advanceToTokenBegin()) { - if (const QChar *tokenEnd = advanceToTokenEnd()) { - const int length = tokenEnd - tokenBegin; - return QString(tokenBegin, length); - } - } - - return QString(); -} - -const QChar *BatchFileLineTokenizer::advanceToTokenBegin() -{ - m_isWithinQuotation = false; - - forever { - if (atEnd()) - return 0; - - if (atQuotationMark()) { - m_isWithinQuotation = true; - ++m_currentChar; - return m_currentChar; - } - - if (!atWhiteSpace()) - return m_currentChar; - - ++m_currentChar; - } -} - -const QChar *BatchFileLineTokenizer::advanceToTokenEnd() -{ - forever { - if (m_isWithinQuotation) { - if (atEnd()) { - qWarning("ClangBatchFileProcessor: error: unfinished quotation."); - return 0; - } - - if (atQuotationMark()) - return m_currentChar++; - - } else if (atWhiteSpace() || atEnd()) { - return m_currentChar; - } - - ++m_currentChar; - } -} - -bool BatchFileLineTokenizer::atEnd() const -{ - return *m_currentChar == '\0'; -} - -bool BatchFileLineTokenizer::atWhiteSpace() const -{ - return *m_currentChar == ' ' - || *m_currentChar == '\t' - || *m_currentChar == '\n'; -} - -bool BatchFileLineTokenizer::atQuotationMark() const -{ - return *m_currentChar == '"'; -} - -struct CommandContext { - QString filePath; - int lineNumber = -1; -}; - -class Command -{ -public: - using Ptr = QSharedPointer<Command>; - -public: - Command(const CommandContext &context) : m_commandContext(context) {} - virtual ~Command() {} - - const CommandContext &context() const { return m_commandContext; } - virtual bool run() { return true; } - -private: - const CommandContext m_commandContext; -}; - -class OpenProjectCommand : public Command -{ -public: - OpenProjectCommand(const CommandContext &context, - const QString &projectFilePath); - - bool run() override; - - static Command::Ptr parse(BatchFileLineTokenizer &arguments, - const CommandContext &context); - -private: - QString m_projectFilePath; -}; - -OpenProjectCommand::OpenProjectCommand(const CommandContext &context, - const QString &projectFilePath) - : Command(context) - , m_projectFilePath(projectFilePath) -{ -} - -bool OpenProjectCommand::run() -{ - qCDebug(debug) << "line" << context().lineNumber << "OpenProjectCommand" << m_projectFilePath; - - const ProjectExplorerPlugin::OpenProjectResult openProjectSucceeded - = ProjectExplorerPlugin::openProject(m_projectFilePath); - QTC_ASSERT(openProjectSucceeded, return false); - - Project *project = openProjectSucceeded.project(); - project->configureAsExampleProject({}); - - return CppTools::Tests::TestCase::waitUntilCppModelManagerIsAwareOf(project, timeOutInMs()); -} - -Command::Ptr OpenProjectCommand::parse(BatchFileLineTokenizer &arguments, - const CommandContext &context) -{ - const QString projectFilePath = arguments.nextToken(); - if (projectFilePath.isEmpty()) { - qWarning("%s:%d: error: No project file path given.", - qPrintable(context.filePath), - context.lineNumber); - return Command::Ptr(); - } - - const QString absoluteProjectFilePath = QFileInfo(projectFilePath).absoluteFilePath(); - - return Command::Ptr(new OpenProjectCommand(context, absoluteProjectFilePath)); -} - -class OpenDocumentCommand : public Command -{ -public: - OpenDocumentCommand(const CommandContext &context, - const QString &documentFilePath); - - bool run() override; - - static Command::Ptr parse(BatchFileLineTokenizer &arguments, const CommandContext &context); - -private: - QString m_documentFilePath; -}; - -OpenDocumentCommand::OpenDocumentCommand(const CommandContext &context, - const QString &documentFilePath) - : Command(context) - , m_documentFilePath(documentFilePath) -{ -} - -class WaitForUpdatedCodeWarnings : public QObject -{ - Q_OBJECT - -public: - WaitForUpdatedCodeWarnings(ClangEditorDocumentProcessor *processor); - - bool wait(int timeOutInMs) const; - -private: - void onCodeWarningsUpdated() { m_gotResults = true; } - -private: - - bool m_gotResults = false; -}; - -WaitForUpdatedCodeWarnings::WaitForUpdatedCodeWarnings(ClangEditorDocumentProcessor *processor) -{ - connect(processor, - &ClangEditorDocumentProcessor::codeWarningsUpdated, - this, &WaitForUpdatedCodeWarnings::onCodeWarningsUpdated); -} - -bool WaitForUpdatedCodeWarnings::wait(int timeOutInMs) const -{ - QTime time; - time.start(); - - forever { - if (time.elapsed() > timeOutInMs) { - qWarning("WaitForUpdatedCodeWarnings: timeout of %d ms reached.", timeOutInMs); - return false; - } - - if (m_gotResults) - return true; - - QCoreApplication::processEvents(); - QThread::msleep(20); - } -} - -bool OpenDocumentCommand::run() -{ - qCDebug(debug) << "line" << context().lineNumber << "OpenDocumentCommand" << m_documentFilePath; - - const bool openEditorSucceeded = Core::EditorManager::openEditor(m_documentFilePath); - QTC_ASSERT(openEditorSucceeded, return false); - - auto *processor = ClangEditorDocumentProcessor::get(m_documentFilePath); - QTC_ASSERT(processor, return false); - - WaitForUpdatedCodeWarnings waiter(processor); - return waiter.wait(timeOutInMs()); -} - -Command::Ptr OpenDocumentCommand::parse(BatchFileLineTokenizer &arguments, - const CommandContext &context) -{ - const QString documentFilePath = arguments.nextToken(); - if (documentFilePath.isEmpty()) { - qWarning("%s:%d: error: No document file path given.", - qPrintable(context.filePath), - context.lineNumber); - return Command::Ptr(); - } - - const QString absoluteDocumentFilePath = QFileInfo(documentFilePath).absoluteFilePath(); - - return Command::Ptr(new OpenDocumentCommand(context, absoluteDocumentFilePath)); -} - -class CloseAllDocuments : public Command -{ -public: - CloseAllDocuments(const CommandContext &context); - - bool run() override; - - static Command::Ptr parse(BatchFileLineTokenizer &arguments, const CommandContext &context); -}; - -CloseAllDocuments::CloseAllDocuments(const CommandContext &context) - : Command(context) -{ -} - -bool CloseAllDocuments::run() -{ - qCDebug(debug) << "line" << context().lineNumber << "CloseAllDocuments"; - - return Core::EditorManager::closeAllEditors(/*askAboutModifiedEditors=*/ false); -} - -Command::Ptr CloseAllDocuments::parse(BatchFileLineTokenizer &arguments, - const CommandContext &context) -{ - const QString argument = arguments.nextToken(); - if (!argument.isEmpty()) { - qWarning("%s:%d: error: Unexpected argument.", - qPrintable(context.filePath), - context.lineNumber); - return Command::Ptr(); - } - - return Command::Ptr(new CloseAllDocuments(context)); -} - -class InsertTextCommand : public Command -{ -public: - // line and column are 1-based - InsertTextCommand(const CommandContext &context, const QString &text); - - bool run() override; - - static Command::Ptr parse(BatchFileLineTokenizer &arguments, - const CommandContext &context); - -private: - const QString m_textToInsert; -}; - -InsertTextCommand::InsertTextCommand(const CommandContext &context, const QString &text) - : Command(context) - , m_textToInsert(text) -{ -} - -TextEditor::BaseTextEditor *currentTextEditor() -{ - return qobject_cast<TextEditor::BaseTextEditor*>(Core::EditorManager::currentEditor()); -} - -bool InsertTextCommand::run() -{ - qCDebug(debug) << "line" << context().lineNumber << "InsertTextCommand" << m_textToInsert; - - TextEditor::BaseTextEditor *editor = currentTextEditor(); - QTC_ASSERT(editor, return false); - const QString documentFilePath = editor->document()->filePath().toString(); - auto *processor = ClangEditorDocumentProcessor::get(documentFilePath); - QTC_ASSERT(processor, return false); - - editor->insert(m_textToInsert); - - WaitForUpdatedCodeWarnings waiter(processor); - return waiter.wait(timeOutInMs()); -} - -Command::Ptr InsertTextCommand::parse(BatchFileLineTokenizer &arguments, - const CommandContext &context) -{ - const QString textToInsert = arguments.nextToken(); - if (textToInsert.isEmpty()) { - qWarning("%s:%d: error: No text to insert given.", - qPrintable(context.filePath), - context.lineNumber); - return Command::Ptr(); - } - - return Command::Ptr(new InsertTextCommand(context, textToInsert)); -} - -class CompleteCommand : public Command -{ -public: - CompleteCommand(const CommandContext &context); - - bool run() override; - - static Command::Ptr parse(BatchFileLineTokenizer &arguments, - const CommandContext &context); -}; - -CompleteCommand::CompleteCommand(const CommandContext &context) - : Command(context) -{ -} - -bool CompleteCommand::run() -{ - qCDebug(debug) << "line" << context().lineNumber << "CompleteCommand"; - - TextEditor::BaseTextEditor *editor = currentTextEditor(); - QTC_ASSERT(editor, return false); - - const QString documentFilePath = editor->document()->filePath().toString(); - auto *processor = ClangEditorDocumentProcessor::get(documentFilePath); - QTC_ASSERT(processor, return false); - - return completionResults(editor, QStringList(), timeOutInMs()); -} - -Command::Ptr CompleteCommand::parse(BatchFileLineTokenizer &arguments, - const CommandContext &context) -{ - Q_UNUSED(arguments) - Q_UNUSED(context) - - return Command::Ptr(new CompleteCommand(context)); -} - -class SetCursorCommand : public Command -{ -public: - // line and column are 1-based - SetCursorCommand(const CommandContext &context, int line, int column); - - bool run() override; - - static Command::Ptr parse(BatchFileLineTokenizer &arguments, - const CommandContext &context); - -private: - int m_line; - int m_column; -}; - -SetCursorCommand::SetCursorCommand(const CommandContext &context, int line, int column) - : Command(context) - , m_line(line) - , m_column(column) -{ -} - -bool SetCursorCommand::run() -{ - qCDebug(debug) << "line" << context().lineNumber << "SetCursorCommand" << m_line << m_column; - - TextEditor::BaseTextEditor *editor = currentTextEditor(); - QTC_ASSERT(editor, return false); - - editor->gotoLine(m_line, m_column - 1); - - return true; -} - -Command::Ptr SetCursorCommand::parse(BatchFileLineTokenizer &arguments, - const CommandContext &context) -{ - // Process line - const QString line = arguments.nextToken(); - if (line.isEmpty()) { - qWarning("%s:%d: error: No line number given.", - qPrintable(context.filePath), - context.lineNumber); - return Command::Ptr(); - } - bool converted = false; - const int lineNumber = line.toInt(&converted); - if (!converted) { - qWarning("%s:%d: error: Invalid line number.", - qPrintable(context.filePath), - context.lineNumber); - return Command::Ptr(); - } - - // Process column - const QString column = arguments.nextToken(); - if (column.isEmpty()) { - qWarning("%s:%d: error: No column number given.", - qPrintable(context.filePath), - context.lineNumber); - return Command::Ptr(); - } - converted = false; - const int columnNumber = column.toInt(&converted); - if (!converted) { - qWarning("%s:%d: error: Invalid column number.", - qPrintable(context.filePath), - context.lineNumber); - return Command::Ptr(); - } - - return Command::Ptr(new SetCursorCommand(context, lineNumber, columnNumber)); -} - -class ProcessEventsCommand : public Command -{ -public: - ProcessEventsCommand(const CommandContext &context, int durationInMs); - - bool run() override; - - static Command::Ptr parse(BatchFileLineTokenizer &arguments, - const CommandContext &context); - -private: - int m_durationInMs; -}; - -ProcessEventsCommand::ProcessEventsCommand(const CommandContext &context, - int durationInMs) - : Command(context) - , m_durationInMs(durationInMs) -{ -} - -bool ProcessEventsCommand::run() -{ - qCDebug(debug) << "line" << context().lineNumber << "ProcessEventsCommand" << m_durationInMs; - - QTime time; - time.start(); - - forever { - if (time.elapsed() > m_durationInMs) - return true; - - QCoreApplication::processEvents(); - QThread::msleep(20); - } -} - -Command::Ptr ProcessEventsCommand::parse(BatchFileLineTokenizer &arguments, - const CommandContext &context) -{ - const QString durationInMsText = arguments.nextToken(); - if (durationInMsText.isEmpty()) { - qWarning("%s:%d: error: No duration given.", - qPrintable(context.filePath), - context.lineNumber); - return Command::Ptr(); - } - - bool converted = false; - const int durationInMs = durationInMsText.toInt(&converted); - if (!converted) { - qWarning("%s:%d: error: Invalid duration given.", - qPrintable(context.filePath), - context.lineNumber); - return Command::Ptr(); - } - - return Command::Ptr(new ProcessEventsCommand(context, durationInMs)); -} - -class BatchFileReader -{ -public: - BatchFileReader(const QString &filePath); - - bool isFilePathValid() const; - - QString read() const; - -private: - const QString m_batchFilePath; -}; - -BatchFileReader::BatchFileReader(const QString &filePath) - : m_batchFilePath(filePath) -{ -} - -bool BatchFileReader::isFilePathValid() const -{ - QFileInfo fileInfo(m_batchFilePath); - - return !m_batchFilePath.isEmpty() - && fileInfo.isFile() - && fileInfo.isReadable(); -} - -QString BatchFileReader::read() const -{ - QFile file(m_batchFilePath); - QTC_CHECK(file.open(QFile::ReadOnly | QFile::Text)); - - return QString::fromLocal8Bit(file.readAll()); -} - -class BatchFileParser -{ -public: - BatchFileParser(const QString &filePath, - const QString &commands); - - bool parse(); - QVector<Command::Ptr> commands() const; - -private: - bool advanceLine(); - QString currentLine() const; - bool parseLine(const QString &line); - -private: - using ParseFunction = Command::Ptr (*)(BatchFileLineTokenizer &, const CommandContext &); - using CommandToParseFunction = QHash<QString, ParseFunction>; - CommandToParseFunction m_commandParsers; - - int m_currentLineIndex = -1; - CommandContext m_context; - QStringList m_lines; - QVector<Command::Ptr> m_commands; -}; - -BatchFileParser::BatchFileParser(const QString &filePath, - const QString &commands) - : m_lines(commands.split('\n')) -{ - m_context.filePath = filePath; - - m_commandParsers.insert("openProject", &OpenProjectCommand::parse); - m_commandParsers.insert("openDocument", &OpenDocumentCommand::parse); - m_commandParsers.insert("closeAllDocuments", &CloseAllDocuments::parse); - m_commandParsers.insert("setCursor", &SetCursorCommand::parse); - m_commandParsers.insert("insertText", &InsertTextCommand::parse); - m_commandParsers.insert("complete", &CompleteCommand::parse); - m_commandParsers.insert("processEvents", &ProcessEventsCommand::parse); -} - -bool BatchFileParser::parse() -{ - while (advanceLine()) { - const QString line = currentLine().trimmed(); - if (line.isEmpty() || line.startsWith('#')) - continue; - - if (!parseLine(line)) - return false; - } - - return true; -} - -QVector<Command::Ptr> BatchFileParser::commands() const -{ - return m_commands; -} - -bool BatchFileParser::advanceLine() -{ - ++m_currentLineIndex; - m_context.lineNumber = m_currentLineIndex + 1; - return m_currentLineIndex < m_lines.size(); -} - -QString BatchFileParser::currentLine() const -{ - return m_lines[m_currentLineIndex]; -} - -bool BatchFileParser::parseLine(const QString &line) -{ - BatchFileLineTokenizer tokenizer(line); - QString command = tokenizer.nextToken(); - QTC_CHECK(!command.isEmpty()); - - if (const ParseFunction parseFunction = m_commandParsers.value(command)) { - if (Command::Ptr cmd = parseFunction(tokenizer, m_context)) { - m_commands.append(cmd); - return true; - } - - return false; - } - - qWarning("%s:%d: error: Unknown command \"%s\".", - qPrintable(m_context.filePath), - m_context.lineNumber, - qPrintable(command)); - - return false; -} - -} // anonymous namespace - -namespace ClangCodeModel { -namespace Internal { - -static QString applySubstitutions(const QString &filePath, const QString &text) -{ - const QString dirPath = QFileInfo(filePath).absolutePath(); - - QString result = text; - result.replace("${PWD}", dirPath); - - return result; -} - -bool runClangBatchFile(const QString &filePath) -{ - qWarning("ClangBatchFileProcessor: Running \"%s\".", qPrintable(filePath)); - - BatchFileReader reader(filePath); - QTC_ASSERT(reader.isFilePathValid(), return false); - const QString fileContent = reader.read(); - const QString fileContentWithSubstitutionsApplied = applySubstitutions(filePath, fileContent); - - BatchFileParser parser(filePath, fileContentWithSubstitutionsApplied); - QTC_ASSERT(parser.parse(), return false); - const QVector<Command::Ptr> commands = parser.commands(); - - Utils::ExecuteOnDestruction closeAllEditors([](){ - qWarning("ClangBatchFileProcessor: Finished, closing all documents."); - QTC_CHECK(Core::EditorManager::closeAllEditors(/*askAboutModifiedEditors=*/ false)); - }); - - foreach (const Command::Ptr &command, commands) { - const bool runSucceeded = command->run(); - QCoreApplication::processEvents(); // Update GUI - - if (!runSucceeded) { - const CommandContext context = command->context(); - qWarning("%s:%d: Failed to run.", - qPrintable(context.filePath), - context.lineNumber); - return false; - } - } - - return true; -} - -} // namespace Internal -} // namespace ClangCodeModel - -#include "clangbatchfileprocessor.moc" diff --git a/src/plugins/clangcodemodel/clangbatchfileprocessor.h b/src/plugins/clangcodemodel/clangbatchfileprocessor.h deleted file mode 100644 index 40f0bceee3..0000000000 --- a/src/plugins/clangcodemodel/clangbatchfileprocessor.h +++ /dev/null @@ -1,36 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 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. -** -****************************************************************************/ - -#pragma once - -#include <QString> - -namespace ClangCodeModel { -namespace Internal { - -bool runClangBatchFile(const QString &filePath); - -} // namespace Internal -} // namespace ClangCodeModel diff --git a/src/plugins/clangcodemodel/clangcodemodel.pro b/src/plugins/clangcodemodel/clangcodemodel.pro index 3089483407..ef9302a1d1 100644 --- a/src/plugins/clangcodemodel/clangcodemodel.pro +++ b/src/plugins/clangcodemodel/clangcodemodel.pro @@ -9,9 +9,7 @@ SOURCES += \ clangassistproposal.cpp \ clangassistproposalitem.cpp \ clangassistproposalmodel.cpp \ - clangautomationutils.cpp \ clangbackendipcintegration.cpp \ - clangbatchfileprocessor.cpp \ clangcodemodelplugin.cpp \ clangcompletionassistinterface.cpp \ clangcompletionassistprocessor.cpp \ @@ -41,9 +39,7 @@ HEADERS += \ clangassistproposal.h \ clangassistproposalitem.h \ clangassistproposalmodel.h \ - clangautomationutils.h \ clangbackendipcintegration.h \ - clangbatchfileprocessor.h \ clangcodemodelplugin.h \ clangcompletionassistinterface.h \ clangcompletionassistprocessor.h \ diff --git a/src/plugins/clangcodemodel/clangcodemodel.qbs b/src/plugins/clangcodemodel/clangcodemodel.qbs index 28e4dffcd8..02ff530760 100644 --- a/src/plugins/clangcodemodel/clangcodemodel.qbs +++ b/src/plugins/clangcodemodel/clangcodemodel.qbs @@ -41,12 +41,8 @@ QtcPlugin { "clangassistproposalitem.h", "clangassistproposalmodel.cpp", "clangassistproposalmodel.h", - "clangautomationutils.cpp", - "clangautomationutils.h", "clangbackendipcintegration.cpp", "clangbackendipcintegration.h", - "clangbatchfileprocessor.cpp", - "clangbatchfileprocessor.h", "clangcodemodel.qrc", "clangcodemodelplugin.cpp", "clangcodemodelplugin.h", diff --git a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp index 0aa9cbff3f..ba1bc10376 100644 --- a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp +++ b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp @@ -25,7 +25,6 @@ #include "clangcodemodelplugin.h" -#include "clangbatchfileprocessor.h" #include "clangconstants.h" #include "clangprojectsettingswidget.h" @@ -75,13 +74,8 @@ void addProjectPanelWidget() bool ClangCodeModelPlugin::initialize(const QStringList &arguments, QString *errorMessage) { - Q_UNUSED(arguments); - Q_UNUSED(errorMessage); - - connect(ProjectExplorer::ProjectExplorerPlugin::instance(), - &ProjectExplorer::ProjectExplorerPlugin::finishedInitialization, - this, - &ClangCodeModelPlugin::maybeHandleBatchFileAndExit); + Q_UNUSED(arguments) + Q_UNUSED(errorMessage) CppTools::CppModelManager::instance()->activateClangCodeModel(&m_modelManagerSupportProvider); @@ -95,16 +89,6 @@ void ClangCodeModelPlugin::extensionsInitialized() { } -// For e.g. creation of profile-guided optimization builds. -void ClangCodeModelPlugin::maybeHandleBatchFileAndExit() const -{ - const QString batchFilePath = QString::fromLocal8Bit(qgetenv("QTC_CLANG_BATCH")); - if (!batchFilePath.isEmpty() && QTC_GUARD(QFileInfo::exists(batchFilePath))) { - const bool runSucceeded = runClangBatchFile(batchFilePath); - QCoreApplication::exit(!runSucceeded); - } -} - #ifdef WITH_TESTS QList<QObject *> ClangCodeModelPlugin::createTestObjects() const { diff --git a/src/plugins/clangcodemodel/clangcodemodelplugin.h b/src/plugins/clangcodemodel/clangcodemodelplugin.h index 8e41fe6020..0b94c93c2c 100644 --- a/src/plugins/clangcodemodel/clangcodemodelplugin.h +++ b/src/plugins/clangcodemodel/clangcodemodelplugin.h @@ -42,9 +42,6 @@ public: void extensionsInitialized(); private: - void maybeHandleBatchFileAndExit() const; - -private: ModelManagerSupportProviderClang m_modelManagerSupportProvider; #ifdef WITH_TESTS diff --git a/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp b/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp index f5072068f6..0f9793dc15 100644 --- a/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp +++ b/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp @@ -25,7 +25,6 @@ #include "clangcodecompletion_test.h" -#include "../clangautomationutils.h" #include "../clangbackendipcintegration.h" #include "../clangcompletionassistinterface.h" #include "../clangmodelmanagersupport.h" @@ -39,8 +38,12 @@ #include <cpptools/cpptoolstestcase.h> #include <cpptools/modelmanagertesthelper.h> #include <cpptools/projectinfo.h> +#include <texteditor/codeassist/assistinterface.h> #include <texteditor/codeassist/assistproposalitem.h> +#include <texteditor/codeassist/completionassistprovider.h> #include <texteditor/codeassist/genericproposalmodel.h> +#include <texteditor/codeassist/iassistprocessor.h> +#include <texteditor/codeassist/iassistproposal.h> #include <texteditor/textdocument.h> #include <texteditor/texteditor.h> @@ -177,6 +180,53 @@ void insertTextAtTopOfEditor(TextEditor::BaseTextEditor *editor, const QByteArra cs.apply(&textCursor); } +class WaitForAsyncCompletions +{ +public: + enum WaitResult { GotResults, GotInvalidResults, Timeout }; + WaitResult wait(TextEditor::IAssistProcessor *processor, + TextEditor::AssistInterface *assistInterface); + + TextEditor::IAssistProposalModel *proposalModel; +}; + +WaitForAsyncCompletions::WaitResult WaitForAsyncCompletions::wait( + TextEditor::IAssistProcessor *processor, + TextEditor::AssistInterface *assistInterface) +{ + QTC_ASSERT(processor, return Timeout); + QTC_ASSERT(assistInterface, return Timeout); + + bool gotResults = false; + + processor->setAsyncCompletionAvailableHandler( + [this, &gotResults] (TextEditor::IAssistProposal *proposal) { + QTC_ASSERT(proposal, return); + proposalModel = proposal->model(); + delete proposal; + gotResults = true; + }); + + // Are there any immediate results? + if (TextEditor::IAssistProposal *proposal = processor->perform(assistInterface)) { + delete processor; + proposalModel = proposal->model(); + delete proposal; + QTC_ASSERT(proposalModel, return GotInvalidResults); + return GotResults; + } + + // There are not any, so wait for async results. + QElapsedTimer timer; timer.start(); + while (!gotResults) { + if (timer.elapsed() >= 30 * 1000) + return Timeout; + QCoreApplication::processEvents(); + } + + return proposalModel ? GotResults : GotInvalidResults; +} + class ChangeDocumentReloadSetting { public: @@ -371,6 +421,51 @@ public: QString senderLog; }; +const CppTools::ProjectPartHeaderPaths toHeaderPaths(const QStringList &paths) +{ + using namespace CppTools; + + ProjectPartHeaderPaths result; + foreach (const QString &path, paths) + result << ProjectPartHeaderPath(path, ProjectPartHeaderPath::IncludePath); + return result; +} + +using ProposalModel = QSharedPointer<TextEditor::IAssistProposalModel>; + +ProposalModel completionResults( + TextEditor::BaseTextEditor *textEditor, + const QStringList &includePaths = QStringList()) +{ + using namespace TextEditor; + + TextEditorWidget *textEditorWidget = qobject_cast<TextEditorWidget *>(textEditor->widget()); + QTC_ASSERT(textEditorWidget, return ProposalModel()); + AssistInterface *assistInterface = textEditorWidget->createAssistInterface( + TextEditor::Completion, TextEditor::ExplicitlyInvoked); + QTC_ASSERT(assistInterface, return ProposalModel()); + if (!includePaths.isEmpty()) { + auto clangAssistInterface = static_cast<ClangCompletionAssistInterface *>(assistInterface); + clangAssistInterface->setHeaderPaths(toHeaderPaths(includePaths)); + } + + CompletionAssistProvider *assistProvider + = textEditor->textDocument()->completionAssistProvider(); + QTC_ASSERT(qobject_cast<ClangCompletionAssistProvider *>(assistProvider), + return ProposalModel()); + QTC_ASSERT(assistProvider, return ProposalModel()); + QTC_ASSERT(assistProvider->runType() == IAssistProvider::Asynchronous, return ProposalModel()); + + IAssistProcessor *processor = assistProvider->createProcessor(); + QTC_ASSERT(processor, return ProposalModel()); + + WaitForAsyncCompletions waitForCompletions; + const WaitForAsyncCompletions::WaitResult result = waitForCompletions.wait(processor, + assistInterface); + QTC_ASSERT(result == WaitForAsyncCompletions::GotResults, return ProposalModel()); + return QSharedPointer<TextEditor::IAssistProposalModel>(waitForCompletions.proposalModel); +} + class TestDocument { public: @@ -594,7 +689,7 @@ public: if (!textToInsert.isEmpty()) openEditor.editor()->insert(textToInsert); - proposal = completionResults(openEditor.editor(), includePaths, 15000); + proposal = completionResults(openEditor.editor(), includePaths); } ProposalModel proposal; |