Merge remote-tracking branch 'origin/11.0'

Conflicts:
	tests/manual/subdirfileiterator/tst_subdirfileiterator.cpp

Change-Id: I6fa8fbed152efc4033fa69e1ab67ced7e2ad35bc
This commit is contained in:
Eike Ziller
2023-06-06 16:44:44 +02:00
276 changed files with 5450 additions and 3543 deletions

223
dist/changelog/changes-11.0.0.md vendored Normal file
View File

@@ -0,0 +1,223 @@
Qt Creator 11
=============
Qt Creator version 11 contains bug fixes and new features.
The most important changes are listed in this document. For a complete list of
changes, see the Git log for the Qt Creator sources that you can check out from
the public Git repository. For example:
git clone git://code.qt.io/qt-creator/qt-creator.git
git log --cherry-pick --pretty=oneline origin/10.0..v11.0.0
General
-------
* Added a `Terminal` view (QTCREATORBUG-8511)
* Opt-out via `Preferences` > `Terminal` preferences
* Added support for
* different shells, colors, fonts, and multiple tabs
* opening file paths in Qt Creator with `Ctrl+click` (`Cmd+click` on
macOS)
* Added a more spacious "relaxed" toolbar style `Environment > Interface`
* Added a pin button to progress details instead of automatically resetting
their position (QTCREATORBUG-28829)
* Improved the selection and navigation in the `Issues` view
(QTCREATORBUG-26128, QTCREATORBUG-27006, QTCREATORBUG-27506)
* Locator
* Improved performance
* Added the creation of directories to the `Files in File System` filter
* Added device roots and browsing remote file systems to the
`Files in File System` filter
Editing
-------
* Improved the performance of the multi-cursor support
* Fixed the saving of hardlinked files (QTCREATORBUG-19651)
* Fixed an issue of copy and paste with multiple cursors (QTCREATORBUG-29117)
### C++
* Improved the style of forward declarations in the outline (QTCREATORBUG-312)
* Added highlighting for typed string literals and user-defined literals
(QTCREATORBUG-28869)
* Added the option to create class members from assignments (QTCREATORBUG-1918)
* Fixed that locator showed both the declaration and the definition of symbols
(QTCREATORBUG-13894)
* Fixed the handling of C++20 keywords and concepts
* Built-in
* Fixed support for `if`-statements with initializer (QTCREATORBUG-29182)
### Language Server Protocol
* Added experimental support for GitHub Copilot
([GitHub documentation](https://github.com/features/copilot))
* Added missing actions for opening the `Call Hierarchy` (QTCREATORBUG-28839,
QTCREATORBUG-28842)
### QML
* Fixed the reformatting in the presence of JavaScript directives and function
return type annotations (QTCREATORBUG-29001, QTCREATORBUG-29046)
* Fixed that reformatting changed `of` to `in` (QTCREATORBUG-29123)
* Fixed the completion for Qt Quick Controls (QTCREATORBUG-28648)
### Python
* Added the option to create a virtual environment (`venv`) to the Python
interpreter selector and the wizard (PYSIDE-2152)
### Markdown
* Added a Markdown editor with preview (QTCREATORBUG-27883)
* Added a wizard for Markdown files (QTCREATORBUG-29056)
Projects
--------
* Made it possible to add devices without going through the wizard
* Added support for moving files to a different directory when renaming
(QTCREATORBUG-15981)
### CMake
* Implemented adding files to the project (QTCREATORBUG-25922,
QTCREATORBUG-26006, QTCREATORBUG-27213, QTCREATORBUG-27538,
QTCREATORBUG-28493, QTCREATORBUG-28904, QTCREATORBUG-28985,
QTCREATORBUG-29006)
* Fixed issues with detecting a configured Qt version when importing a build
(QTCREATORBUG-29075)
### Python
* Added an option for the interpreter to the wizards
### vcpkg
* Added experimental support for `vcpkg`
([vcpgk documentation](https://vcpkg.io/en/))
* Added an option for the `vcpkg` installation location
* Added a search dialog for packages
* Added a wizard and an editor for `vcpkg.json` files
Debugging
---------
* Improved the UI for enabling and disabling debuggers (QTCREATORBUG-28627)
### C++
* Added an option for the default number of array elements to show
(`Preferences > Debugger > Locals & Expressions > Default array size`)
* CDB
* Added automatic source file mapping for Qt packages
* Fixed the variables view on remote Windows devices (QTCREATORBUG-29000)
* LLDB
* Fixed that long lines in the application output were broken into multiple
lines (QTCREATORBUG-29098)
### Qt Quick
* Improved the auto-detection if QML debugging is required (QTCREATORBUG-28627)
* Added an option for disabling static analyzer messages to
`Qt Quick > QML/JS Editing` (QTCREATORBUG-29095)
Analyzer
--------
### Clang
* Fixed that a `.clang-tidy` file in the project directory was not used by
default (QTCREATORBUG-28852)
### Axivion
* Added experimental support
Version Control Systems
-----------------------
### Git
* Instant Blame
* Improved the performance (QTCREATORBUG-29151)
* Fixed that it did not show at the end of the document
Platforms
---------
### Android
* Fixed an issue with building library targets (QTCREATORBUG-26980)
### Remote Linux
* Removed the automatic sourcing of target-side shell profiles
### Docker
* Added support for `qmake` based projects (QTCREATORBUG-29140)
* Fixed issues after deleting the Docker image for a registered Docker device
(QTCREATORBUG-28880)
### QNX
* Added `slog2info` as a requirement for devices
* Fixed the support for remote working directories (QTCREATORBUG-28900)
Credits for these changes go to:
--------------------------------
Aleksei German
Alessandro Portale
Alexander Drozdov
Alexander Pershin
Ali Kianian
Alibek Omarov
Amr Essam
Andre Hartmann
André Pönitz
Artem Mukhin
Artem Sokolovskii
Assam Boudjelthia
Björn Schäpers
Brook Cronin
Burak Hancerli
Christian Kandeler
Christian Stenger
Cristian Adam
David Schulz
Eike Ziller
Esa Törmänen
Fabian Kosmale
Filippo Gentile
Friedemann Kleint
Henning Gruendl
Jaroslaw Kobus
Jussi Witick
Kai Köhne
Knud Dollereder
Knut Petter Svendsen
Leena Miettinen
Mahmoud Badri
Marco Bubke
Marcus Tillmanns
Martin Delille
Mats Honkamaa
Miikka Heikkinen
Mitch Curtis
Niels Weber
Orgad Shaneh
Pranta Dastider
Robert Löhning
Samuel Ghinet
Semih Yavuz
Tasuku Suzuki
Thiago Macieira
Thomas Hartmann
Tim Jenssen
Tim Jenßen
Ulf Hermann
Vikas Pachdha
Yasser Grimes
Yixue Wang

View File

@@ -482,8 +482,9 @@
\row \row
\li Add Class Member \li Add Class Member
\li Adds a member declaration for the class member being \li Adds a member declaration for the class member being
initialized if it is not yet declared. You must enter initialized if it is not yet declared. If \QC cannot
the data type of the member. automatically detect the data type of the member, you
must add it.
\li Identifier \li Identifier
\row \row
\li Create Implementations for Member Functions \li Create Implementations for Member Functions

View File

@@ -99,4 +99,7 @@
\image qtcreator-toggle-progress-bar.webp {Toggle Progress Details button} \image qtcreator-toggle-progress-bar.webp {Toggle Progress Details button}
You can drag the progress bar to another position. The position is saved for
later. Select the \inlineimage icons/pin.png
(\uicontrol Pin) button to pin the progress bar back to the toggle button.
*/ */

View File

@@ -18199,16 +18199,16 @@ Möchten Sie sie jetzt auschecken?</translation>
<translation>Bytes</translation> <translation>Bytes</translation>
</message> </message>
<message> <message>
<source>KB</source> <source>KiB</source>
<translation>KB</translation> <translation>KiB</translation>
</message> </message>
<message> <message>
<source>GB</source> <source>GiB</source>
<translation>GB</translation> <translation>GiB</translation>
</message> </message>
<message> <message>
<source>TB</source> <source>TiB</source>
<translation>TB</translation> <translation>TiB</translation>
</message> </message>
<message> <message>
<source>Enable crash reporting</source> <source>Enable crash reporting</source>
@@ -27194,12 +27194,8 @@ zu deaktivieren, deaktiviert auch die folgenden Plugins:
<translation>Privat</translation> <translation>Privat</translation>
</message> </message>
<message> <message>
<source>Create a private check-in that is never synced. <source>Create a private check-in that is never synced. Children of private check-ins are automatically private. Private check-ins are not pushed to the remote repository by default.</source>
Children of private check-ins are automatically private. <translation>Erstelle einen privaten Check-In, der niemals synchronisiert wird. Kinder von privaten Check-Ins sind automatisch privat. Private Check-Ins werden standardmäßig nicht zum entfernten Repository gepusht.</translation>
Private check-ins are not pushed to the remote repository by default.</source>
<translation>Erstelle einen privaten Check-In, der niemals synchronisiert wird.
Kinder von privaten Check-Ins sind automatisch privat.
Private Check-Ins werden standardmäßig nicht zum entfernten Repository gepusht.</translation>
</message> </message>
<message> <message>
<source>Tag names to apply; comma-separated.</source> <source>Tag names to apply; comma-separated.</source>
@@ -39112,7 +39108,7 @@ Sie werden erhalten.</numerusform>
</message> </message>
<message> <message>
<source>&amp;Configure Project</source> <source>&amp;Configure Project</source>
<translation>Projekt &amp;Konfigurieren</translation> <translation>Projekt &amp;konfigurieren</translation>
</message> </message>
<message> <message>
<source>Enable Kit for Project &quot;%1&quot;</source> <source>Enable Kit for Project &quot;%1&quot;</source>
@@ -53204,12 +53200,9 @@ Wird ein Problem gefunden, dann wird die Anwendung angehalten und kann untersuch
<source>Hint: The second line of a commit message should be empty.</source> <source>Hint: The second line of a commit message should be empty.</source>
<translation>Hinweis: Die zweite Zeile der Beschreibung sollte leer sein.</translation> <translation>Hinweis: Die zweite Zeile der Beschreibung sollte leer sein.</translation>
</message> </message>
<message numerus="yes"> <message>
<source>&lt;p&gt;Writing good commit messages&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Avoid very short commit messages.&lt;/li&gt;&lt;li&gt;Consider the first line as subject (like in email) and keep it shorter than %n characters.&lt;/li&gt;&lt;li&gt;After an empty second line, a longer description can be added.&lt;/li&gt;&lt;li&gt;Describe why the change was done, not how it was done.&lt;/li&gt;&lt;/ul&gt;</source> <source>&lt;p&gt;Writing good commit messages&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Avoid very short commit messages.&lt;/li&gt;&lt;li&gt;Consider the first line as a subject (like in emails) and keep it shorter than 72 characters.&lt;/li&gt;&lt;li&gt;After an empty second line, a longer description can be added.&lt;/li&gt;&lt;li&gt;Describe why the change was done, not how it was done.&lt;/li&gt;&lt;/ul&gt;</source>
<translation> <translation>&lt;p&gt;Gute Beschreibungen für Commits schreiben&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Vermeiden Sie sehr kurze Beschreibungen.&lt;/li&gt;&lt;li&gt;Betrachten Sie die erste Zeile als Betreff (wie in einer E-Mail) und halten Sie sie kürzer als 72 Zeichen.&lt;/li&gt;&lt;li&gt;Eine längere Beschreibung kann nach einer leeren zweiten Zeile folgen.&lt;/li&gt;&lt;li&gt;Beschreiben Sie, weshalb die Änderung vorgenommen wurde, nicht wie sie vorgenommen wurde.&lt;/li&gt;&lt;/ul&gt;</translation>
<numerusform>&lt;p&gt;Gute Beschreibungen für Commits schreiben&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Vermeiden Sie sehr kurze Beschreibungen.&lt;/li&gt;&lt;li&gt;Betrachten Sie die erste Zeile als Betreff (wie in einer E-Mail) und halten Sie sie kürzer als ein Zeichen.&lt;/li&gt;&lt;li&gt;Eine längere Beschreibung kann nach einer leeren zweiten Zeile folgen.&lt;/li&gt;&lt;li&gt;Beschreiben Sie, weshalb die Änderung vorgenommen wurde, nicht wie sie vorgenommen wurde.&lt;/li&gt;&lt;/ul&gt;</numerusform>
<numerusform>&lt;p&gt;Gute Beschreibungen für Commits schreiben&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Vermeiden Sie sehr kurze Beschreibungen.&lt;/li&gt;&lt;li&gt;Betrachten Sie die erste Zeile als Betreff (wie in einer E-Mail) und halten Sie sie kürzer als %n Zeichen.&lt;/li&gt;&lt;li&gt;Eine längere Beschreibung kann nach einer leeren zweiten Zeile folgen.&lt;/li&gt;&lt;li&gt;Beschreiben Sie, weshalb die Änderung vorgenommen wurde, nicht wie sie vorgenommen wurde.&lt;/li&gt;&lt;/ul&gt;</numerusform>
</translation>
</message> </message>
<message> <message>
<source>Update in progress</source> <source>Update in progress</source>

View File

@@ -369,61 +369,62 @@ LibraryInfo::LibraryInfo(const QmlDirParser &parser, const QByteArray &fingerpri
QByteArray LibraryInfo::calculateFingerprint() const QByteArray LibraryInfo::calculateFingerprint() const
{ {
QCryptographicHash hash(QCryptographicHash::Sha1); QCryptographicHash hash(QCryptographicHash::Sha1);
hash.addData(reinterpret_cast<const char *>(&_status), sizeof(_status)); auto addData = [&hash](auto p, size_t len) {
hash.addData(QByteArrayView(reinterpret_cast<const char *>(p), len));
};
addData(&_status, sizeof(_status));
int len = _components.size(); int len = _components.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); addData(&len, sizeof(len));
for (const QmlDirParser::Component &component : _components) { for (const QmlDirParser::Component &component : _components) {
len = component.fileName.size(); len = component.fileName.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); addData(&len, sizeof(len));
hash.addData(reinterpret_cast<const char *>(component.fileName.constData()), addData(component.fileName.constData(), len * sizeofQChar);
len * sizeofQChar); addData(&component.majorVersion, sizeof(component.majorVersion));
hash.addData(reinterpret_cast<const char *>(&component.majorVersion), sizeof(component.majorVersion)); addData(&component.minorVersion, sizeof(component.minorVersion));
hash.addData(reinterpret_cast<const char *>(&component.minorVersion), sizeof(component.minorVersion));
len = component.typeName.size(); len = component.typeName.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); addData(&len, sizeof(len));
hash.addData(reinterpret_cast<const char *>(component.typeName.constData()), addData(component.typeName.constData(), component.typeName.size() * sizeofQChar);
component.typeName.size() * sizeofQChar);
int flags = (component.singleton ? (1 << 0) : 0) + (component.internal ? (1 << 1) : 0); int flags = (component.singleton ? (1 << 0) : 0) + (component.internal ? (1 << 1) : 0);
hash.addData(reinterpret_cast<const char *>(&flags), sizeof(flags)); addData(&flags, sizeof(flags));
} }
len = _plugins.size(); len = _plugins.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); addData(&len, sizeof(len));
for (const QmlDirParser::Plugin &plugin : _plugins) { for (const QmlDirParser::Plugin &plugin : _plugins) {
len = plugin.path.size(); len = plugin.path.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); addData(&len, sizeof(len));
hash.addData(reinterpret_cast<const char *>(plugin.path.constData()), len * sizeofQChar); addData(plugin.path.constData(), len * sizeofQChar);
len = plugin.name.size(); len = plugin.name.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); addData(&len, sizeof(len));
hash.addData(reinterpret_cast<const char *>(plugin.name.constData()), len * sizeofQChar); addData(plugin.name.constData(), len * sizeofQChar);
} }
len = _typeinfos.size(); len = _typeinfos.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); addData(&len, sizeof(len));
for (const QString &typeinfo : _typeinfos) { for (const QString &typeinfo : _typeinfos) {
len = typeinfo.size(); len = typeinfo.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); addData(&len, sizeof(len));
hash.addData(reinterpret_cast<const char *>(typeinfo.constData()), addData(typeinfo.constData(), len * sizeofQChar);
len * sizeofQChar);
} }
len = _metaObjects.size(); len = _metaObjects.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); addData(&len, sizeof(len));
QList<QByteArray> metaFingerprints; QList<QByteArray> metaFingerprints;
for (const LanguageUtils::FakeMetaObject::ConstPtr &metaObject : _metaObjects) for (const LanguageUtils::FakeMetaObject::ConstPtr &metaObject : _metaObjects)
metaFingerprints.append(metaObject->fingerprint()); metaFingerprints.append(metaObject->fingerprint());
std::sort(metaFingerprints.begin(), metaFingerprints.end()); std::sort(metaFingerprints.begin(), metaFingerprints.end());
for (const QByteArray &fp : std::as_const(metaFingerprints)) for (const QByteArray &fp : std::as_const(metaFingerprints))
hash.addData(fp); hash.addData(fp);
hash.addData(reinterpret_cast<const char *>(&_dumpStatus), sizeof(_dumpStatus)); addData(&_dumpStatus, sizeof(_dumpStatus));
len = _dumpError.size(); // localization dependent (avoid?) len = _dumpError.size(); // localization dependent (avoid?)
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); addData(&len, sizeof(len));
hash.addData(reinterpret_cast<const char *>(_dumpError.constData()), len * sizeofQChar); addData(_dumpError.constData(), len * sizeofQChar);
len = _moduleApis.size(); len = _moduleApis.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); addData(&len, sizeof(len));
for (const ModuleApiInfo &moduleInfo : _moduleApis) for (const ModuleApiInfo &moduleInfo : _moduleApis)
moduleInfo.addToHash(hash); // make it order independent? moduleInfo.addToHash(hash); // make it order independent?
len = _imports.size(); len = _imports.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len)); addData(&len, sizeof(len));
for (const QmlDirParser::Import &import : _imports) for (const QmlDirParser::Import &import : _imports)
hash.addData(import.module.toUtf8()); // import order matters, keep order-dependent hash.addData(import.module.toUtf8()); // import order matters, keep order-dependent

View File

@@ -1,9 +1,11 @@
add_qtc_library(Tasking OBJECT add_qtc_library(Tasking OBJECT
# Never add dependencies to non-Qt libraries for this library # Never add dependencies to non-Qt libraries for this library
DEPENDS Qt::Core DEPENDS Qt::Concurrent Qt::Core Qt::Network
PUBLIC_DEFINES TASKING_LIBRARY PUBLIC_DEFINES TASKING_LIBRARY
SOURCES SOURCES
barrier.cpp barrier.h barrier.cpp barrier.h
concurrentcall.h
networkquery.cpp networkquery.h
tasking_global.h tasking_global.h
tasktree.cpp tasktree.h tasktree.cpp tasktree.h
) )

View File

@@ -34,7 +34,7 @@ private:
int m_current = -1; int m_current = -1;
}; };
class TASKING_EXPORT BarrierTaskAdapter : public Tasking::TaskAdapter<Barrier> class TASKING_EXPORT BarrierTaskAdapter : public TaskAdapter<Barrier>
{ {
public: public:
BarrierTaskAdapter() { connect(task(), &Barrier::done, this, &TaskInterface::done); } BarrierTaskAdapter() { connect(task(), &Barrier::done, this, &TaskInterface::done); }

View File

@@ -0,0 +1,100 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include "tasking_global.h"
#include "tasktree.h"
#include <QtConcurrent>
namespace Tasking {
// This class introduces the dependency to Qt::Concurrent, otherwise Tasking namespace
// is independent on Qt::Concurrent.
// Possibly, it could be placed inside Qt::Concurrent library, as a wrapper around
// QtConcurrent::run() call.
template <typename ResultType>
class ConcurrentCall
{
Q_DISABLE_COPY_MOVE(ConcurrentCall)
public:
ConcurrentCall() = default;
template <typename Function, typename ...Args>
void setConcurrentCallData(Function &&function, Args &&...args)
{
return wrapConcurrent(std::forward<Function>(function), std::forward<Args>(args)...);
}
void setThreadPool(QThreadPool *pool) { m_threadPool = pool; }
ResultType result() const
{
return m_future.resultCount() ? m_future.result() : ResultType();
}
QFuture<ResultType> future() const { return m_future; }
private:
template <typename Function, typename ...Args>
void wrapConcurrent(Function &&function, Args &&...args)
{
m_startHandler = [=] {
if (m_threadPool)
return QtConcurrent::run(m_threadPool, function, args...);
return QtConcurrent::run(function, args...);
};
}
template <typename Function, typename ...Args>
void wrapConcurrent(std::reference_wrapper<const Function> &&wrapper, Args &&...args)
{
m_startHandler = [=] {
if (m_threadPool) {
return QtConcurrent::run(m_threadPool,
std::forward<const Function>(wrapper.get()), args...);
}
return QtConcurrent::run(std::forward<const Function>(wrapper.get()), args...);
};
}
template <typename T>
friend class ConcurrentCallTaskAdapter;
std::function<QFuture<ResultType>()> m_startHandler;
QThreadPool *m_threadPool = nullptr;
QFuture<ResultType> m_future;
};
template <typename ResultType>
class ConcurrentCallTaskAdapter : public TaskAdapter<ConcurrentCall<ResultType>>
{
public:
~ConcurrentCallTaskAdapter() {
if (m_watcher) {
m_watcher->cancel();
m_watcher->waitForFinished();
}
}
void start() {
if (!this->task()->m_startHandler) {
emit this->done(false); // TODO: Add runtime assert
return;
}
m_watcher.reset(new QFutureWatcher<ResultType>);
this->connect(m_watcher.get(), &QFutureWatcherBase::finished, this, [this] {
emit this->done(!m_watcher->isCanceled());
m_watcher.release()->deleteLater();
});
this->task()->m_future = this->task()->m_startHandler();
m_watcher->setFuture(this->task()->m_future);
}
private:
std::unique_ptr<QFutureWatcher<ResultType>> m_watcher;
};
} // namespace Tasking
TASKING_DECLARE_TEMPLATE_TASK(ConcurrentCallTask, Tasking::ConcurrentCallTaskAdapter);

View File

@@ -0,0 +1,38 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "networkquery.h"
#include <QNetworkAccessManager>
namespace Tasking {
void NetworkQuery::start()
{
if (m_reply) {
qWarning("The NetworkQuery is already running. Ignoring the call to start().");
return;
}
if (!m_manager) {
qWarning("Can't start the NetworkQuery without the QNetworkAccessManager. "
"Stopping with an error.");
emit done(false);
return;
}
m_reply.reset(m_manager->get(m_request));
connect(m_reply.get(), &QNetworkReply::finished, this, [this] {
disconnect(m_reply.get(), nullptr, this, nullptr);
emit done(m_reply->error() == QNetworkReply::NoError);
m_reply.release()->deleteLater();
});
if (m_reply->isRunning())
emit started();
}
NetworkQuery::~NetworkQuery()
{
if (m_reply)
m_reply->abort();
}
} // namespace Tasking

View File

@@ -0,0 +1,55 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#pragma once
#include "tasking_global.h"
#include "tasktree.h"
#include <QNetworkReply>
#include <QNetworkRequest>
#include <memory>
QT_BEGIN_NAMESPACE
class QNetworkAccessManager;
QT_END_NAMESPACE
namespace Tasking {
// This class introduces the dependency to Qt::Network, otherwise Tasking namespace
// is independent on Qt::Network.
// Possibly, it could be placed inside Qt::Network library, as a wrapper around QNetworkReply.
class TASKING_EXPORT NetworkQuery final : public QObject
{
Q_OBJECT
public:
~NetworkQuery();
void setRequest(const QNetworkRequest &request) { m_request = request; }
void setNetworkAccessManager(QNetworkAccessManager *manager) { m_manager = manager; }
QNetworkReply *reply() const { return m_reply.get(); }
void start();
signals:
void started();
void done(bool success);
private:
QNetworkRequest m_request;
QNetworkAccessManager *m_manager = nullptr;
std::unique_ptr<QNetworkReply> m_reply;
};
class TASKING_EXPORT NetworkQueryTaskAdapter : public TaskAdapter<NetworkQuery>
{
public:
NetworkQueryTaskAdapter() { connect(task(), &NetworkQuery::done, this, &TaskInterface::done); }
void start() final { task()->start(); }
};
} // namespace Tasking
TASKING_DECLARE_TASK(NetworkQueryTask, Tasking::NetworkQueryTaskAdapter);

View File

@@ -1,11 +1,14 @@
QtcLibrary { QtcLibrary {
name: "Tasking" name: "Tasking"
Depends { name: "Qt"; submodules: ["core"] } Depends { name: "Qt"; submodules: ["concurrent", "core", "network"] }
cpp.defines: base.concat("TASKING_LIBRARY") cpp.defines: base.concat("TASKING_LIBRARY")
files: [ files: [
"barrier.cpp", "barrier.cpp",
"barrier.h", "barrier.h",
"concurrentcall.h",
"networkquery.cpp",
"networkquery.h",
"tasking_global.h", "tasking_global.h",
"tasktree.cpp", "tasktree.cpp",
"tasktree.h", "tasktree.h",

View File

@@ -9,6 +9,8 @@
#include <QSet> #include <QSet>
#include <QTimer> #include <QTimer>
using namespace std::chrono;
namespace Tasking { namespace Tasking {
// That's cut down qtcassert.{c,h} to avoid the dependency. // That's cut down qtcassert.{c,h} to avoid the dependency.
@@ -41,11 +43,119 @@ private:
}; };
/*! /*!
\class Tasking::TaskItem \class Tasking::GroupItem
\inheaderfile solutions/tasking/tasktree.h \inheaderfile solutions/tasking/tasktree.h
\inmodule QtCreator \inmodule QtCreator
\ingroup mainclasses \ingroup mainclasses
\brief The TaskItem class represents the basic element for composing nested tree structures. \brief The GroupItem class represents the basic element for composing nested tree structures.
*/
/*!
\enum Tasking::WorkflowPolicy
This enum describes the possible behavior of the Group element when any group's child task
finishes its execution. It's also used when the running Group is stopped.
\value StopOnError
Default. Corresponds to the stopOnError global element.
If any child task finishes with an error, the group stops and finishes with an error.
If all child tasks finished with success, the group finishes with success.
If a group is empty, it finishes with success.
\value ContinueOnError
Corresponds to the continueOnError global element.
Similar to stopOnError, but in case any child finishes with an error,
the execution continues until all tasks finish, and the group reports an error
afterwards, even when some other tasks in the group finished with success.
If all child tasks finish successfully, the group finishes with success.
If a group is empty, it finishes with success.
\value StopOnDone
Corresponds to the stopOnDone global element.
If any child task finishes with success, the group stops and finishes with success.
If all child tasks finished with an error, the group finishes with an error.
If a group is empty, it finishes with an error.
\value ContinueOnDone
Corresponds to the continueOnDone global element.
Similar to stopOnDone, but in case any child finishes successfully,
the execution continues until all tasks finish, and the group reports success
afterwards, even when some other tasks in the group finished with an error.
If all child tasks finish with an error, the group finishes with an error.
If a group is empty, it finishes with an error.
\value StopOnFinished
Corresponds to the stopOnFinished global element.
The group starts as many tasks as it can. When any task finishes,
the group stops and reports the task's result.
Useful only in parallel mode.
In sequential mode, only the first task is started, and when finished,
the group finishes too, so the other tasks are always skipped.
If a group is empty, it finishes with an error.
\value FinishAllAndDone
Corresponds to the finishAllAndDone global element.
The group executes all tasks and ignores their return results. When all
tasks finished, the group finishes with success.
If a group is empty, it finishes with success.
\value FinishAllAndError
Corresponds to the finishAllAndError global element.
The group executes all tasks and ignores their return results. When all
tasks finished, the group finishes with an error.
If a group is empty, it finishes with an error.
Whenever a child task's result causes the Group to stop,
i.e. in case of StopOnError, StopOnDone, or StopOnFinished policies,
the Group stops the other running child tasks (if any - for example in parallel mode),
and skips executing tasks it has not started yet (for example, in the sequential mode -
those, that are placed after the failed task). Both stopping and skipping child tasks
may happen when parallelLimit is used.
The table below summarizes the differences between various workflow policies:
\table
\header
\li \l WorkflowPolicy
\li Executes all child tasks
\li Result
\li Result when the group is empty
\row
\li StopOnError
\li Stops when any child task finished with an error and reports an error
\li An error when at least one child task failed, success otherwise
\li Success
\row
\li ContinueOnError
\li Yes
\li An error when at least one child task failed, success otherwise
\li Success
\row
\li StopOnDone
\li Stops when any child task finished with success and reports success
\li Success when at least one child task succeeded, an error otherwise
\li An error
\row
\li ContinueOnDone
\li Yes
\li Success when at least one child task succeeded, an error otherwise
\li An error
\row
\li StopOnFinished
\li Stops when any child task finished and reports child task's result
\li Success or an error, depending on the finished child task's result
\li An error
\row
\li FinishAllAndDone
\li Yes
\li Success
\li Success
\row
\li FinishAllAndError
\li Yes
\li An error
\li An error
\endtable
If a child of a group is also a group, the child group runs its tasks according to its own
workflow policy. When a parent group stops the running child group because
of parent group's workflow policy, i.e. when the StopOnError, StopOnDone, or StopOnFinished
policy was used for the parent, the child group's result is reported according to the
\b Result column and to the \b {child group's workflow policy} row in the table above.
*/ */
/*! /*!
@@ -74,6 +184,43 @@ private:
\sa sequential, parallelLimit \sa sequential, parallelLimit
*/ */
/*!
\variable stopOnError
A convenient global group's element describing the StopOnError workflow policy.
This is the default workflow policy of the Group element.
*/
/*!
\variable continueOnError
A convenient global group's element describing the ContinueOnError workflow policy.
*/
/*!
\variable stopOnDone
A convenient global group's element describing the StopOnDone workflow policy.
*/
/*!
\variable continueOnDone
A convenient global group's element describing the ContinueOnDone workflow policy.
*/
/*!
\variable stopOnFinished
A convenient global group's element describing the StopOnFinished workflow policy.
*/
/*!
\variable finishAllAndDone
A convenient global group's element describing the FinishAllAndDone workflow policy.
*/
/*!
\variable finishAllAndError
A convenient global group's element describing the FinishAllAndError workflow policy.
*/
/*! /*!
\enum Tasking::TaskAction \enum Tasking::TaskAction
@@ -99,7 +246,7 @@ private:
*/ */
/*! /*!
\typealias TaskItem::GroupSetupHandler \typealias GroupItem::GroupSetupHandler
Type alias for \c std::function<TaskAction()>. Type alias for \c std::function<TaskAction()>.
@@ -130,7 +277,7 @@ private:
*/ */
/*! /*!
\typealias TaskItem::GroupEndHandler \typealias GroupItem::GroupEndHandler
Type alias for \c std::function\<void()\>. Type alias for \c std::function\<void()\>.
@@ -143,13 +290,14 @@ private:
*/ */
/*! /*!
\fn template <typename SetupHandler> TaskItem onGroupSetup(SetupHandler &&handler) \fn template <typename SetupHandler> GroupItem onGroupSetup(SetupHandler &&handler)
Constructs a group's element holding the group setup handler. Constructs a group's element holding the group setup handler.
The \a handler is invoked whenever the group starts. The \a handler is invoked whenever the group starts.
The passed \a handler is either of \c std::function<TaskAction()> or \c std::function<void()> The passed \a handler is either of \c std::function<TaskAction()> or \c std::function<void()>
type. For more information on possible argument type, refer to \l {TaskItem::GroupSetupHandler}. type. For more information on possible argument type, refer to
\l {GroupItem::GroupSetupHandler}.
When the \a handler is invoked, none of the group's child tasks are running yet. When the \a handler is invoked, none of the group's child tasks are running yet.
@@ -157,14 +305,14 @@ private:
after the storages are constructed, so that the \a handler may already after the storages are constructed, so that the \a handler may already
perform some initial modifications to the active storages. perform some initial modifications to the active storages.
\sa TaskItem::GroupSetupHandler, onGroupDone, onGroupError \sa GroupItem::GroupSetupHandler, onGroupDone, onGroupError
*/ */
/*! /*!
Constructs a group's element holding the group done handler. Constructs a group's element holding the group done handler.
The \a handler is invoked whenever the group finishes with success. The \a handler is invoked whenever the group finishes with success.
Depending on the group's workflow policy, this handler may also be called Depending on the group's workflow policy, this handler may also be called
when the running group is stopped (e.g. when optional element was used). when the running group is stopped (e.g. when finishAllAndDone element was used).
When the \a handler is invoked, all of the group's child tasks are already finished. When the \a handler is invoked, all of the group's child tasks are already finished.
@@ -172,9 +320,9 @@ private:
before the storages are destructed, so that the \a handler may still before the storages are destructed, so that the \a handler may still
perform a last read of the active storages' data. perform a last read of the active storages' data.
\sa TaskItem::GroupEndHandler, onGroupSetup, onGroupError \sa GroupItem::GroupEndHandler, onGroupSetup, onGroupError
*/ */
TaskItem onGroupDone(const TaskItem::GroupEndHandler &handler) GroupItem onGroupDone(const GroupItem::GroupEndHandler &handler)
{ {
return Group::onGroupDone(handler); return Group::onGroupDone(handler);
} }
@@ -191,9 +339,9 @@ TaskItem onGroupDone(const TaskItem::GroupEndHandler &handler)
before the storages are destructed, so that the \a handler may still before the storages are destructed, so that the \a handler may still
perform a last read of the active storages' data. perform a last read of the active storages' data.
\sa TaskItem::GroupEndHandler, onGroupSetup, onGroupDone \sa GroupItem::GroupEndHandler, onGroupSetup, onGroupDone
*/ */
TaskItem onGroupError(const TaskItem::GroupEndHandler &handler) GroupItem onGroupError(const GroupItem::GroupEndHandler &handler)
{ {
return Group::onGroupError(handler); return Group::onGroupError(handler);
} }
@@ -239,24 +387,34 @@ TaskItem onGroupError(const TaskItem::GroupEndHandler &handler)
\sa sequential, parallel \sa sequential, parallel
*/ */
TaskItem parallelLimit(int limit) GroupItem parallelLimit(int limit)
{ {
return Group::parallelLimit(qMax(limit, 0)); return Group::parallelLimit(qMax(limit, 0));
} }
TaskItem workflowPolicy(WorkflowPolicy policy) /*!
Constructs a group's workflow policy element for a given \a policy.
For convenience, global elements may be used instead.
\sa stopOnError, continueOnError, stopOnDone, continueOnDone, stopOnFinished, finishAllAndDone,
finishAllAndError, WorkflowPolicy
*/
GroupItem workflowPolicy(WorkflowPolicy policy)
{ {
return Group::workflowPolicy(policy); return Group::workflowPolicy(policy);
} }
const TaskItem sequential = parallelLimit(1); const GroupItem sequential = parallelLimit(1);
const TaskItem parallel = parallelLimit(0); const GroupItem parallel = parallelLimit(0);
const TaskItem stopOnError = workflowPolicy(WorkflowPolicy::StopOnError);
const TaskItem continueOnError = workflowPolicy(WorkflowPolicy::ContinueOnError); const GroupItem stopOnError = workflowPolicy(WorkflowPolicy::StopOnError);
const TaskItem stopOnDone = workflowPolicy(WorkflowPolicy::StopOnDone); const GroupItem continueOnError = workflowPolicy(WorkflowPolicy::ContinueOnError);
const TaskItem continueOnDone = workflowPolicy(WorkflowPolicy::ContinueOnDone); const GroupItem stopOnDone = workflowPolicy(WorkflowPolicy::StopOnDone);
const TaskItem stopOnFinished = workflowPolicy(WorkflowPolicy::StopOnFinished); const GroupItem continueOnDone = workflowPolicy(WorkflowPolicy::ContinueOnDone);
const TaskItem optional = workflowPolicy(WorkflowPolicy::Optional); const GroupItem stopOnFinished = workflowPolicy(WorkflowPolicy::StopOnFinished);
const GroupItem finishAllAndDone = workflowPolicy(WorkflowPolicy::FinishAllAndDone);
const GroupItem finishAllAndError = workflowPolicy(WorkflowPolicy::FinishAllAndError);
static TaskAction toTaskAction(bool success) static TaskAction toTaskAction(bool success)
{ {
@@ -326,11 +484,11 @@ void TreeStorageBase::activateStorage(int id) const
m_storageData->m_activeStorage = id; m_storageData->m_activeStorage = id;
} }
void TaskItem::addChildren(const QList<TaskItem> &children) void GroupItem::addChildren(const QList<GroupItem> &children)
{ {
QTC_ASSERT(m_type == Type::Group, qWarning("Only Group may have children, skipping..."); QTC_ASSERT(m_type == Type::Group, qWarning("Only Group may have children, skipping...");
return); return);
for (const TaskItem &child : children) { for (const GroupItem &child : children) {
switch (child.m_type) { switch (child.m_type) {
case Type::Group: case Type::Group:
m_children.append(child); m_children.append(child);
@@ -377,7 +535,7 @@ void TaskItem::addChildren(const QList<TaskItem> &children)
} }
} }
void TaskItem::setTaskSetupHandler(const TaskSetupHandler &handler) void GroupItem::setTaskSetupHandler(const TaskSetupHandler &handler)
{ {
if (!handler) { if (!handler) {
qWarning("Setting empty Setup Handler is no-op, skipping..."); qWarning("Setting empty Setup Handler is no-op, skipping...");
@@ -388,7 +546,7 @@ void TaskItem::setTaskSetupHandler(const TaskSetupHandler &handler)
m_taskHandler.m_setupHandler = handler; m_taskHandler.m_setupHandler = handler;
} }
void TaskItem::setTaskDoneHandler(const TaskEndHandler &handler) void GroupItem::setTaskDoneHandler(const TaskEndHandler &handler)
{ {
if (!handler) { if (!handler) {
qWarning("Setting empty Done Handler is no-op, skipping..."); qWarning("Setting empty Done Handler is no-op, skipping...");
@@ -399,7 +557,7 @@ void TaskItem::setTaskDoneHandler(const TaskEndHandler &handler)
m_taskHandler.m_doneHandler = handler; m_taskHandler.m_doneHandler = handler;
} }
void TaskItem::setTaskErrorHandler(const TaskEndHandler &handler) void GroupItem::setTaskErrorHandler(const TaskEndHandler &handler)
{ {
if (!handler) { if (!handler) {
qWarning("Setting empty Error Handler is no-op, skipping..."); qWarning("Setting empty Error Handler is no-op, skipping...");
@@ -410,6 +568,23 @@ void TaskItem::setTaskErrorHandler(const TaskEndHandler &handler)
m_taskHandler.m_errorHandler = handler; m_taskHandler.m_errorHandler = handler;
} }
GroupItem GroupItem::withTimeout(const GroupItem &item, milliseconds timeout,
const GroupEndHandler &handler)
{
const TimeoutTask::EndHandler taskHandler = handler
? [handler](const milliseconds &) { handler(); } : TimeoutTask::EndHandler();
return Group {
parallel,
stopOnFinished,
Group {
finishAllAndError,
TimeoutTask([timeout](milliseconds &timeoutData) { timeoutData = timeout; },
taskHandler)
},
item
};
}
class TaskTreePrivate; class TaskTreePrivate;
class TaskNode; class TaskNode;
@@ -466,7 +641,7 @@ class TaskContainer
Q_DISABLE_COPY_MOVE(TaskContainer) Q_DISABLE_COPY_MOVE(TaskContainer)
public: public:
TaskContainer(TaskTreePrivate *taskTreePrivate, const TaskItem &task, TaskContainer(TaskTreePrivate *taskTreePrivate, const GroupItem &task,
TaskNode *parentNode, TaskContainer *parentContainer) TaskNode *parentNode, TaskContainer *parentContainer)
: m_constData(taskTreePrivate, task, parentNode, parentContainer, this) {} : m_constData(taskTreePrivate, task, parentNode, parentContainer, this) {}
TaskAction start(); TaskAction start();
@@ -479,7 +654,7 @@ public:
bool isStarting() const { return isRunning() && m_runtimeData->m_startGuard.isLocked(); } bool isStarting() const { return isRunning() && m_runtimeData->m_startGuard.isLocked(); }
struct ConstData { struct ConstData {
ConstData(TaskTreePrivate *taskTreePrivate, const TaskItem &task, TaskNode *parentNode, ConstData(TaskTreePrivate *taskTreePrivate, const GroupItem &task, TaskNode *parentNode,
TaskContainer *parentContainer, TaskContainer *thisContainer); TaskContainer *parentContainer, TaskContainer *thisContainer);
~ConstData() { qDeleteAll(m_children); } ~ConstData() { qDeleteAll(m_children); }
TaskTreePrivate * const m_taskTreePrivate = nullptr; TaskTreePrivate * const m_taskTreePrivate = nullptr;
@@ -488,7 +663,7 @@ public:
const int m_parallelLimit = 1; const int m_parallelLimit = 1;
const WorkflowPolicy m_workflowPolicy = WorkflowPolicy::StopOnError; const WorkflowPolicy m_workflowPolicy = WorkflowPolicy::StopOnError;
const TaskItem::GroupHandler m_groupHandler; const GroupItem::GroupHandler m_groupHandler;
const QList<TreeStorageBase> m_storageList; const QList<TreeStorageBase> m_storageList;
const QList<TaskNode *> m_children; const QList<TaskNode *> m_children;
const int m_taskCount = 0; const int m_taskCount = 0;
@@ -505,8 +680,8 @@ public:
const ConstData &m_constData; const ConstData &m_constData;
const QList<int> m_storageIdList; const QList<int> m_storageIdList;
int m_doneCount = 0;
bool m_successBit = true; bool m_successBit = true;
int m_doneCount = 0;
Guard m_startGuard; Guard m_startGuard;
}; };
@@ -519,7 +694,7 @@ class TaskNode
Q_DISABLE_COPY_MOVE(TaskNode) Q_DISABLE_COPY_MOVE(TaskNode)
public: public:
TaskNode(TaskTreePrivate *taskTreePrivate, const TaskItem &task, TaskNode(TaskTreePrivate *taskTreePrivate, const GroupItem &task,
TaskContainer *parentContainer) TaskContainer *parentContainer)
: m_taskHandler(task.taskHandler()) : m_taskHandler(task.taskHandler())
, m_container(taskTreePrivate, task, this, parentContainer) , m_container(taskTreePrivate, task, this, parentContainer)
@@ -537,7 +712,7 @@ public:
TaskTree *taskTree() const { return m_container.m_constData.m_taskTreePrivate->q; } TaskTree *taskTree() const { return m_container.m_constData.m_taskTreePrivate->q; }
private: private:
const TaskItem::TaskHandler m_taskHandler; const GroupItem::TaskHandler m_taskHandler;
TaskContainer m_container; TaskContainer m_container;
std::unique_ptr<TaskInterface> m_task; std::unique_ptr<TaskInterface> m_task;
}; };
@@ -657,16 +832,16 @@ ReturnType invokeHandler(TaskContainer *container, Handler &&handler, Args &&...
} }
static QList<TaskNode *> createChildren(TaskTreePrivate *taskTreePrivate, TaskContainer *container, static QList<TaskNode *> createChildren(TaskTreePrivate *taskTreePrivate, TaskContainer *container,
const TaskItem &task) const GroupItem &task)
{ {
QList<TaskNode *> result; QList<TaskNode *> result;
const QList<TaskItem> &children = task.children(); const QList<GroupItem> &children = task.children();
for (const TaskItem &child : children) for (const GroupItem &child : children)
result.append(new TaskNode(taskTreePrivate, child, container)); result.append(new TaskNode(taskTreePrivate, child, container));
return result; return result;
} }
TaskContainer::ConstData::ConstData(TaskTreePrivate *taskTreePrivate, const TaskItem &task, TaskContainer::ConstData::ConstData(TaskTreePrivate *taskTreePrivate, const GroupItem &task,
TaskNode *parentNode, TaskContainer *parentContainer, TaskNode *parentNode, TaskContainer *parentContainer,
TaskContainer *thisContainer) TaskContainer *thisContainer)
: m_taskTreePrivate(taskTreePrivate) : m_taskTreePrivate(taskTreePrivate)
@@ -701,13 +876,28 @@ void TaskContainer::RuntimeData::callStorageDoneHandlers()
} }
} }
static bool initialSuccessBit(WorkflowPolicy workflowPolicy)
{
switch (workflowPolicy) {
case WorkflowPolicy::StopOnError:
case WorkflowPolicy::ContinueOnError:
case WorkflowPolicy::FinishAllAndDone:
return true;
case WorkflowPolicy::StopOnDone:
case WorkflowPolicy::ContinueOnDone:
case WorkflowPolicy::StopOnFinished:
case WorkflowPolicy::FinishAllAndError:
return false;
}
QTC_CHECK(false);
return false;
}
TaskContainer::RuntimeData::RuntimeData(const ConstData &constData) TaskContainer::RuntimeData::RuntimeData(const ConstData &constData)
: m_constData(constData) : m_constData(constData)
, m_storageIdList(createStorages(constData)) , m_storageIdList(createStorages(constData))
{ , m_successBit(initialSuccessBit(m_constData.m_workflowPolicy))
m_successBit = m_constData.m_workflowPolicy != WorkflowPolicy::StopOnDone {}
&& m_constData.m_workflowPolicy != WorkflowPolicy::ContinueOnDone;
}
TaskContainer::RuntimeData::~RuntimeData() TaskContainer::RuntimeData::~RuntimeData()
{ {
@@ -720,10 +910,11 @@ TaskContainer::RuntimeData::~RuntimeData()
bool TaskContainer::RuntimeData::updateSuccessBit(bool success) bool TaskContainer::RuntimeData::updateSuccessBit(bool success)
{ {
if (m_constData.m_workflowPolicy == WorkflowPolicy::Optional) if (m_constData.m_workflowPolicy == WorkflowPolicy::FinishAllAndDone
return m_successBit; || m_constData.m_workflowPolicy == WorkflowPolicy::FinishAllAndError
if (m_constData.m_workflowPolicy == WorkflowPolicy::StopOnFinished) { || m_constData.m_workflowPolicy == WorkflowPolicy::StopOnFinished) {
m_successBit = success; if (m_constData.m_workflowPolicy == WorkflowPolicy::StopOnFinished)
m_successBit = success;
return m_successBit; return m_successBit;
} }
@@ -753,7 +944,7 @@ TaskAction TaskContainer::start()
} }
if (startAction == TaskAction::Continue) { if (startAction == TaskAction::Continue) {
if (m_constData.m_children.isEmpty()) if (m_constData.m_children.isEmpty())
startAction = TaskAction::StopWithDone; startAction = toTaskAction(m_runtimeData->m_successBit);
} }
return continueStart(startAction, 0); return continueStart(startAction, 0);
} }
@@ -845,7 +1036,7 @@ void TaskContainer::stop()
void TaskContainer::invokeEndHandler() void TaskContainer::invokeEndHandler()
{ {
const TaskItem::GroupHandler &groupHandler = m_constData.m_groupHandler; const GroupItem::GroupHandler &groupHandler = m_constData.m_groupHandler;
if (m_runtimeData->m_successBit && groupHandler.m_doneHandler) if (m_runtimeData->m_successBit && groupHandler.m_doneHandler)
invokeHandler(this, groupHandler.m_doneHandler); invokeHandler(this, groupHandler.m_doneHandler);
else if (!m_runtimeData->m_successBit && groupHandler.m_errorHandler) else if (!m_runtimeData->m_successBit && groupHandler.m_errorHandler)
@@ -893,6 +1084,7 @@ void TaskNode::stop()
if (!m_task) { if (!m_task) {
m_container.stop(); m_container.stop();
m_container.m_runtimeData->updateSuccessBit(false);
m_container.invokeEndHandler(); m_container.invokeEndHandler();
return; return;
} }
@@ -1331,77 +1523,11 @@ void TaskNode::invokeEndHandler(bool success)
\section2 Workflow Policy \section2 Workflow Policy
The workflow policy element in a Group specifies how the group should behave The workflow policy element in a Group specifies how the group should behave
when any of its \e direct child's tasks finish: when any of its \e direct child's tasks finish. For a detailed description of possible
policies, refer to WorkflowPolicy.
\table If a child of a group is also a group, the child group runs its tasks
\header according to its own workflow policy.
\li Workflow Policy
\li Description
\row
\li stopOnError
\li Default. If a task finishes with an error, the group:
\list 1
\li Stops the running tasks (if any - for example, in parallel
mode).
\li Skips executing tasks it has not started yet (for example, in the
sequential mode - those, that are placed after the failed task).
\li Immediately finishes with an error.
\endlist
If all child tasks finish successfully, the group finishes with success.
\row
\li continueOnError
\li Similar to stopOnError, but in case any child finishes with
an error, the execution continues until all tasks finish,
and the group reports an error afterwards, even when some other
tasks in group finished with success.
If a task finishes with an error, the group:
\list 1
\li Continues executing the tasks that are running or have not
started yet.
\li Finishes with an error when all tasks finish.
\endlist
If all tasks finish successfully, the group finishes with success.
\row
\li stopOnDone
\li If a task finishes with success, the group:
\list 1
\li Stops the running tasks (if any - for example, in parallel
mode).
\li Skips executing tasks it has not started yet (for example, in the
sequential mode - those, that are placed after the successfully finished task).
\li Immediately finishes with success.
\endlist
If all tasks finish with an error, the group finishes with an error.
\row
\li continueOnDone
\li Similar to stopOnDone, but in case any child finishes
successfully, the execution continues until all tasks finish,
and the group reports success afterwards, even when some other
tasks in group finished with an error.
If a task finishes with success, the group:
\list 1
\li Continues executing the tasks that are running or have not
started yet.
\li Finishes with success when all tasks finish.
\endlist
If all tasks finish with an error, the group finishes with an error.
\row
\li stopOnFinished
\li The group starts as many tasks as it can. When a task finishes,
the group stops and reports the task's result.
Useful only in parallel mode.
In sequential mode, only the first task is started, and when finished,
the group finishes too, so the other tasks are ignored.
\row
\li optional
\li The group executes all tasks and ignores their return state. When all
tasks finish, the group finishes with success.
\endtable
When a Group is empty, it finishes immediately with success,
regardless of its workflow policy.
If a child of a group is also a group, the child group
runs its tasks according to its own workflow policy.
\section2 Storage \section2 Storage
@@ -1411,10 +1537,10 @@ void TaskNode::invokeEndHandler(bool success)
it from a source and writing it to a destination might look as follows: it from a source and writing it to a destination might look as follows:
\code \code
static QByteArray load(const FilePath &fileName) { ... } static QByteArray load(const QString &fileName) { ... }
static void save(const FilePath &fileName, const QByteArray &array) { ... } static void save(const QString &fileName, const QByteArray &array) { ... }
static TaskItem diffRecipe(const FilePath &source, const FilePath &destination) static GroupItem copyRecipe(const QString &source, const QString &destination)
{ {
struct CopyStorage { // [1] custom inter-task struct struct CopyStorage { // [1] custom inter-task struct
QByteArray content; // [2] custom inter-task data QByteArray content; // [2] custom inter-task data
@@ -1448,6 +1574,13 @@ void TaskNode::invokeEndHandler(bool success)
}; };
return root; return root;
} }
const QString source = ...;
const QString destination = ...;
TaskTree taskTree(copyRecipe(source, destination));
connect(&taskTree, &TaskTree::done,
&taskTree, [] { qDebug() << "The copying finished successfully."; });
tasktree.start();
\endcode \endcode
In the example above, the inter-task data consists of a QByteArray content In the example above, the inter-task data consists of a QByteArray content
@@ -1659,9 +1792,16 @@ bool TaskTree::isRunning() const
return d->m_root && d->m_root->isRunning(); return d->m_root && d->m_root->isRunning();
} }
bool TaskTree::runBlocking(const QFuture<void> &future, int timeoutMs) bool TaskTree::runBlocking()
{ {
if (isRunning() || future.isCanceled()) QPromise<void> dummy;
dummy.start();
return runBlocking(dummy.future());
}
bool TaskTree::runBlocking(const QFuture<void> &future)
{
if (future.isCanceled())
return false; return false;
bool ok = false; bool ok = false;
@@ -1680,17 +1820,7 @@ bool TaskTree::runBlocking(const QFuture<void> &future, int timeoutMs)
connect(this, &TaskTree::done, &loop, [finalize] { finalize(true); }); connect(this, &TaskTree::done, &loop, [finalize] { finalize(true); });
connect(this, &TaskTree::errorOccurred, &loop, [finalize] { finalize(false); }); connect(this, &TaskTree::errorOccurred, &loop, [finalize] { finalize(false); });
start(); QTimer::singleShot(0, this, &TaskTree::start);
if (!isRunning())
return ok;
QTimer timer;
if (timeoutMs) {
timer.setSingleShot(true);
timer.setInterval(timeoutMs);
connect(&timer, &QTimer::timeout, this, &TaskTree::stop);
timer.start();
}
loop.exec(QEventLoop::ExcludeUserInputEvents); loop.exec(QEventLoop::ExcludeUserInputEvents);
if (!ok) { if (!ok) {
@@ -1700,11 +1830,19 @@ bool TaskTree::runBlocking(const QFuture<void> &future, int timeoutMs)
return ok; return ok;
} }
bool TaskTree::runBlocking(int timeoutMs) bool TaskTree::runBlocking(const Group &recipe, milliseconds timeout)
{ {
QPromise<void> dummy; QPromise<void> dummy;
dummy.start(); dummy.start();
return runBlocking(dummy.future(), timeoutMs); return TaskTree::runBlocking(recipe, dummy.future(), timeout);
}
bool TaskTree::runBlocking(const Group &recipe, const QFuture<void> &future, milliseconds timeout)
{
const Group root = timeout == milliseconds::max() ? recipe
: Group { recipe.withTimeout(timeout) };
TaskTree taskTree(root);
return taskTree.runBlocking(future);
} }
int TaskTree::taskCount() const int TaskTree::taskCount() const
@@ -1749,4 +1887,95 @@ void TaskTreeTaskAdapter::start()
task()->start(); task()->start();
} }
using TimeoutCallback = std::function<void()>;
struct TimerData
{
system_clock::time_point m_deadline;
QPointer<QObject> m_context;
TimeoutCallback m_callback;
};
QMutex s_mutex;
std::atomic_int s_timerId = 0;
QHash<int, TimerData> s_timerIdToTimerData = {};
QMultiMap<system_clock::time_point, int> s_deadlineToTimerId = {};
static QList<TimerData> prepareForActivation(int timerId)
{
QMutexLocker lock(&s_mutex);
const auto it = s_timerIdToTimerData.constFind(timerId);
if (it == s_timerIdToTimerData.cend())
return {}; // the timer was already activated
const system_clock::time_point deadline = it->m_deadline;
QList<TimerData> toActivate;
auto itMap = s_deadlineToTimerId.cbegin();
while (itMap != s_deadlineToTimerId.cend()) {
if (itMap.key() > deadline)
break;
const auto it = s_timerIdToTimerData.constFind(itMap.value());
if (it != s_timerIdToTimerData.cend()) {
toActivate.append(it.value());
s_timerIdToTimerData.erase(it);
}
itMap = s_deadlineToTimerId.erase(itMap);
}
return toActivate;
}
static void removeTimerId(int timerId)
{
QMutexLocker lock(&s_mutex);
const auto it = s_timerIdToTimerData.constFind(timerId);
QTC_ASSERT(it != s_timerIdToTimerData.cend(),
qWarning("Removing active timerId failed."); return);
const system_clock::time_point deadline = it->m_deadline;
s_timerIdToTimerData.erase(it);
const int removedCount = s_deadlineToTimerId.remove(deadline, timerId);
QTC_ASSERT(removedCount == 1, qWarning("Removing active timerId failed."); return);
}
static void handleTimeout(int timerId)
{
const QList<TimerData> toActivate = prepareForActivation(timerId);
for (const TimerData &timerData : toActivate) {
if (timerData.m_context)
QMetaObject::invokeMethod(timerData.m_context.get(), timerData.m_callback);
}
}
static int scheduleTimeout(milliseconds timeout, QObject *context, const TimeoutCallback &callback)
{
const int timerId = s_timerId.fetch_add(1) + 1;
const system_clock::time_point deadline = system_clock::now() + timeout;
QTimer::singleShot(timeout, context, [timerId] { handleTimeout(timerId); });
QMutexLocker lock(&s_mutex);
s_timerIdToTimerData.emplace(timerId, TimerData{deadline, context, callback});
s_deadlineToTimerId.insert(deadline, timerId);
return timerId;
}
TimeoutTaskAdapter::TimeoutTaskAdapter()
{
*task() = std::chrono::milliseconds::zero();
}
TimeoutTaskAdapter::~TimeoutTaskAdapter()
{
if (m_timerId)
removeTimerId(*m_timerId);
}
void TimeoutTaskAdapter::start()
{
if (*task() == milliseconds::zero())
QTimer::singleShot(0, this, [this] { emit done(true); });
else
m_timerId = scheduleTimeout(*task(), this, [this] { m_timerId = {}; emit done(true); });
}
} // namespace Tasking } // namespace Tasking

View File

@@ -16,6 +16,8 @@ QT_END_NAMESPACE
namespace Tasking { namespace Tasking {
Q_NAMESPACE_EXPORT(TASKING_EXPORT)
class ExecutionContextActivator; class ExecutionContextActivator;
class TaskContainer; class TaskContainer;
class TaskTreePrivate; class TaskTreePrivate;
@@ -99,16 +101,19 @@ private:
// b) On first done - continue executing all children and report done afterwards. // b) On first done - continue executing all children and report done afterwards.
// 3. Stops on first finished child. In sequential mode it will never run other children then the first one. // 3. Stops on first finished child. In sequential mode it will never run other children then the first one.
// Useful only in parallel mode. // Useful only in parallel mode.
// 4. Always run all children, ignore their result and report done afterwards. // 4. Always run all children, let them finish, ignore their results and report done afterwards.
// 5. Always run all children, let them finish, ignore their results and report error afterwards.
enum class WorkflowPolicy { enum class WorkflowPolicy {
StopOnError, // 1a - Reports error on first child error, otherwise done (if all children were done). StopOnError, // 1a - Reports error on first child error, otherwise done (if all children were done).
ContinueOnError, // 1b - The same, but children execution continues. Reports done when no children. ContinueOnError, // 1b - The same, but children execution continues. Reports done when no children.
StopOnDone, // 2a - Reports done on first child done, otherwise error (if all children were error). StopOnDone, // 2a - Reports done on first child done, otherwise error (if all children were error).
ContinueOnDone, // 2b - The same, but children execution continues. Reports error when no children. ContinueOnDone, // 2b - The same, but children execution continues. Reports error when no children.
StopOnFinished, // 3 - Stops on first finished child and report its result. StopOnFinished, // 3 - Stops on first finished child and report its result.
Optional // 4 - Reports done after all children finished. FinishAllAndDone, // 4 - Reports done after all children finished.
FinishAllAndError // 5 - Reports error after all children finished.
}; };
Q_ENUM_NS(WorkflowPolicy);
enum class TaskAction enum class TaskAction
{ {
@@ -116,8 +121,9 @@ enum class TaskAction
StopWithDone, StopWithDone,
StopWithError StopWithError
}; };
Q_ENUM_NS(TaskAction);
class TASKING_EXPORT TaskItem class TASKING_EXPORT GroupItem
{ {
public: public:
// Internal, provided by QTC_DECLARE_CUSTOM_TASK // Internal, provided by QTC_DECLARE_CUSTOM_TASK
@@ -150,7 +156,7 @@ public:
std::optional<WorkflowPolicy> m_workflowPolicy = {}; std::optional<WorkflowPolicy> m_workflowPolicy = {};
}; };
QList<TaskItem> children() const { return m_children; } QList<GroupItem> children() const { return m_children; }
GroupData groupData() const { return m_groupData; } GroupData groupData() const { return m_groupData; }
QList<TreeStorageBase> storageList() const { return m_storageList; } QList<TreeStorageBase> storageList() const { return m_storageList; }
TaskHandler taskHandler() const { return m_taskHandler; } TaskHandler taskHandler() const { return m_taskHandler; }
@@ -163,52 +169,59 @@ protected:
TaskHandler TaskHandler
}; };
TaskItem() = default; GroupItem() = default;
TaskItem(const GroupData &data) GroupItem(const GroupData &data)
: m_type(Type::GroupData) : m_type(Type::GroupData)
, m_groupData(data) {} , m_groupData(data) {}
TaskItem(const TreeStorageBase &storage) GroupItem(const TreeStorageBase &storage)
: m_type(Type::Storage) : m_type(Type::Storage)
, m_storageList{storage} {} , m_storageList{storage} {}
TaskItem(const TaskHandler &handler) GroupItem(const TaskHandler &handler)
: m_type(Type::TaskHandler) : m_type(Type::TaskHandler)
, m_taskHandler(handler) {} , m_taskHandler(handler) {}
void addChildren(const QList<TaskItem> &children); void addChildren(const QList<GroupItem> &children);
void setTaskSetupHandler(const TaskSetupHandler &handler); void setTaskSetupHandler(const TaskSetupHandler &handler);
void setTaskDoneHandler(const TaskEndHandler &handler); void setTaskDoneHandler(const TaskEndHandler &handler);
void setTaskErrorHandler(const TaskEndHandler &handler); void setTaskErrorHandler(const TaskEndHandler &handler);
static TaskItem groupHandler(const GroupHandler &handler) { return TaskItem({handler}); } static GroupItem groupHandler(const GroupHandler &handler) { return GroupItem({handler}); }
static TaskItem parallelLimit(int limit) { return TaskItem({{}, limit}); } static GroupItem parallelLimit(int limit) { return GroupItem({{}, limit}); }
static TaskItem workflowPolicy(WorkflowPolicy policy) { return TaskItem({{}, {}, policy}); } static GroupItem workflowPolicy(WorkflowPolicy policy) { return GroupItem({{}, {}, policy}); }
static GroupItem withTimeout(const GroupItem &item, std::chrono::milliseconds timeout,
const GroupEndHandler &handler = {});
private: private:
Type m_type = Type::Group; Type m_type = Type::Group;
QList<TaskItem> m_children; QList<GroupItem> m_children;
GroupData m_groupData; GroupData m_groupData;
QList<TreeStorageBase> m_storageList; QList<TreeStorageBase> m_storageList;
TaskHandler m_taskHandler; TaskHandler m_taskHandler;
}; };
class TASKING_EXPORT Group : public TaskItem class TASKING_EXPORT Group : public GroupItem
{ {
public: public:
Group(const QList<TaskItem> &children) { addChildren(children); } Group(const QList<GroupItem> &children) { addChildren(children); }
Group(std::initializer_list<TaskItem> children) { addChildren(children); } Group(std::initializer_list<GroupItem> children) { addChildren(children); }
// GroupData related: // GroupData related:
template <typename SetupHandler> template <typename SetupHandler>
static TaskItem onGroupSetup(SetupHandler &&handler) { static GroupItem onGroupSetup(SetupHandler &&handler) {
return groupHandler({wrapGroupSetup(std::forward<SetupHandler>(handler))}); return groupHandler({wrapGroupSetup(std::forward<SetupHandler>(handler))});
} }
static TaskItem onGroupDone(const GroupEndHandler &handler) { static GroupItem onGroupDone(const GroupEndHandler &handler) {
return groupHandler({{}, handler}); return groupHandler({{}, handler});
} }
static TaskItem onGroupError(const GroupEndHandler &handler) { static GroupItem onGroupError(const GroupEndHandler &handler) {
return groupHandler({{}, {}, handler}); return groupHandler({{}, {}, handler});
} }
using TaskItem::parallelLimit; // Default: 1 (sequential). 0 means unlimited (parallel). using GroupItem::parallelLimit; // Default: 1 (sequential). 0 means unlimited (parallel).
using TaskItem::workflowPolicy; // Default: WorkflowPolicy::StopOnError. using GroupItem::workflowPolicy; // Default: WorkflowPolicy::StopOnError.
GroupItem withTimeout(std::chrono::milliseconds timeout,
const GroupEndHandler &handler = {}) const {
return GroupItem::withTimeout(*this, timeout, handler);
}
private: private:
template<typename SetupHandler> template<typename SetupHandler>
@@ -231,29 +244,31 @@ private:
}; };
template <typename SetupHandler> template <typename SetupHandler>
static TaskItem onGroupSetup(SetupHandler &&handler) static GroupItem onGroupSetup(SetupHandler &&handler)
{ {
return Group::onGroupSetup(std::forward<SetupHandler>(handler)); return Group::onGroupSetup(std::forward<SetupHandler>(handler));
} }
TASKING_EXPORT TaskItem onGroupDone(const TaskItem::GroupEndHandler &handler); TASKING_EXPORT GroupItem onGroupDone(const GroupItem::GroupEndHandler &handler);
TASKING_EXPORT TaskItem onGroupError(const TaskItem::GroupEndHandler &handler); TASKING_EXPORT GroupItem onGroupError(const GroupItem::GroupEndHandler &handler);
TASKING_EXPORT TaskItem parallelLimit(int limit); TASKING_EXPORT GroupItem parallelLimit(int limit);
TASKING_EXPORT TaskItem workflowPolicy(WorkflowPolicy policy); TASKING_EXPORT GroupItem workflowPolicy(WorkflowPolicy policy);
TASKING_EXPORT extern const TaskItem sequential; TASKING_EXPORT extern const GroupItem sequential;
TASKING_EXPORT extern const TaskItem parallel; TASKING_EXPORT extern const GroupItem parallel;
TASKING_EXPORT extern const TaskItem stopOnError;
TASKING_EXPORT extern const TaskItem continueOnError;
TASKING_EXPORT extern const TaskItem stopOnDone;
TASKING_EXPORT extern const TaskItem continueOnDone;
TASKING_EXPORT extern const TaskItem stopOnFinished;
TASKING_EXPORT extern const TaskItem optional;
class TASKING_EXPORT Storage : public TaskItem TASKING_EXPORT extern const GroupItem stopOnError;
TASKING_EXPORT extern const GroupItem continueOnError;
TASKING_EXPORT extern const GroupItem stopOnDone;
TASKING_EXPORT extern const GroupItem continueOnDone;
TASKING_EXPORT extern const GroupItem stopOnFinished;
TASKING_EXPORT extern const GroupItem finishAllAndDone;
TASKING_EXPORT extern const GroupItem finishAllAndError;
class TASKING_EXPORT Storage : public GroupItem
{ {
public: public:
Storage(const TreeStorageBase &storage) : TaskItem(storage) { } Storage(const TreeStorageBase &storage) : GroupItem(storage) { }
}; };
// Synchronous invocation. Similarly to Group - isn't counted as a task inside taskCount() // Synchronous invocation. Similarly to Group - isn't counted as a task inside taskCount()
@@ -266,7 +281,7 @@ public:
private: private:
template<typename Function> template<typename Function>
static QList<TaskItem> init(Function &&function) { static QList<GroupItem> init(Function &&function) {
constexpr bool isInvocable = std::is_invocable_v<std::decay_t<Function>>; constexpr bool isInvocable = std::is_invocable_v<std::decay_t<Function>>;
static_assert(isInvocable, static_assert(isInvocable,
"Sync element: The synchronous function can't take any arguments."); "Sync element: The synchronous function can't take any arguments.");
@@ -295,17 +310,17 @@ private:
}; };
template <typename Adapter> template <typename Adapter>
class CustomTask : public TaskItem class CustomTask : public GroupItem
{ {
public: public:
using Task = typename Adapter::Type; using Task = typename Adapter::Type;
using EndHandler = std::function<void(const Task &)>; using EndHandler = std::function<void(const Task &)>;
static Adapter *createAdapter() { return new Adapter; } static Adapter *createAdapter() { return new Adapter; }
CustomTask() : TaskItem({&createAdapter}) {} CustomTask() : GroupItem({&createAdapter}) {}
template <typename SetupFunction> template <typename SetupFunction>
CustomTask(SetupFunction &&function, const EndHandler &done = {}, const EndHandler &error = {}) CustomTask(SetupFunction &&function, const EndHandler &done = {}, const EndHandler &error = {})
: TaskItem({&createAdapter, wrapSetup(std::forward<SetupFunction>(function)), : GroupItem({&createAdapter, wrapSetup(std::forward<SetupFunction>(function)),
wrapEnd(done), wrapEnd(error)}) {} wrapEnd(done), wrapEnd(error)}) {}
template <typename SetupFunction> template <typename SetupFunction>
CustomTask &onSetup(SetupFunction &&function) { CustomTask &onSetup(SetupFunction &&function) {
@@ -321,9 +336,14 @@ public:
return *this; return *this;
} }
GroupItem withTimeout(std::chrono::milliseconds timeout,
const GroupEndHandler &handler = {}) const {
return GroupItem::withTimeout(*this, timeout, handler);
}
private: private:
template<typename SetupFunction> template<typename SetupFunction>
static TaskItem::TaskSetupHandler wrapSetup(SetupFunction &&function) { static GroupItem::TaskSetupHandler wrapSetup(SetupFunction &&function) {
static constexpr bool isDynamic = std::is_same_v<TaskAction, static constexpr bool isDynamic = std::is_same_v<TaskAction,
std::invoke_result_t<std::decay_t<SetupFunction>, typename Adapter::Type &>>; std::invoke_result_t<std::decay_t<SetupFunction>, typename Adapter::Type &>>;
constexpr bool isVoid = std::is_same_v<void, constexpr bool isVoid = std::is_same_v<void,
@@ -370,8 +390,12 @@ public:
// Helper methods. They execute a local event loop with ExcludeUserInputEvents. // Helper methods. They execute a local event loop with ExcludeUserInputEvents.
// The passed future is used for listening to the cancel event. // The passed future is used for listening to the cancel event.
// Don't use it in main thread. To be used in non-main threads or in auto tests. // Don't use it in main thread. To be used in non-main threads or in auto tests.
bool runBlocking(const QFuture<void> &future, int timeoutMs = 0); bool runBlocking();
bool runBlocking(int timeoutMs = 0); bool runBlocking(const QFuture<void> &future);
static bool runBlocking(const Group &recipe,
std::chrono::milliseconds timeout = std::chrono::milliseconds::max());
static bool runBlocking(const Group &recipe, const QFuture<void> &future,
std::chrono::milliseconds timeout = std::chrono::milliseconds::max());
int taskCount() const; int taskCount() const;
int progressMaximum() const { return taskCount(); } int progressMaximum() const { return taskCount(); }
@@ -418,6 +442,17 @@ public:
void start() final; void start() final;
}; };
class TASKING_EXPORT TimeoutTaskAdapter : public TaskAdapter<std::chrono::milliseconds>
{
public:
TimeoutTaskAdapter();
~TimeoutTaskAdapter();
void start() final;
private:
std::optional<int> m_timerId;
};
} // namespace Tasking } // namespace Tasking
#define TASKING_DECLARE_TASK(CustomTaskName, TaskAdapterClass)\ #define TASKING_DECLARE_TASK(CustomTaskName, TaskAdapterClass)\
@@ -430,3 +465,4 @@ using CustomTaskName = CustomTask<TaskAdapterClass<Args...>>;\
} // namespace Tasking } // namespace Tasking
TASKING_DECLARE_TASK(TaskTreeTask, TaskTreeTaskAdapter); TASKING_DECLARE_TASK(TaskTreeTask, TaskTreeTaskAdapter);
TASKING_DECLARE_TASK(TimeoutTask, TimeoutTaskAdapter);

View File

@@ -670,6 +670,7 @@ public:
// Used to block recursive editingFinished signals for example when return is pressed, and // Used to block recursive editingFinished signals for example when return is pressed, and
// the validation changes focus by opening a dialog // the validation changes focus by opening a dialog
bool m_blockAutoApply = false; bool m_blockAutoApply = false;
bool m_allowPathFromDevice = true;
template<class Widget> void updateWidgetFromCheckStatus(StringAspect *aspect, Widget *w) template<class Widget> void updateWidgetFromCheckStatus(StringAspect *aspect, Widget *w)
{ {
@@ -977,6 +978,13 @@ void StringAspect::setCommandVersionArguments(const QStringList &arguments)
d->m_pathChooserDisplay->setCommandVersionArguments(arguments); d->m_pathChooserDisplay->setCommandVersionArguments(arguments);
} }
void StringAspect::setAllowPathFromDevice(bool allowPathFromDevice)
{
d->m_allowPathFromDevice = allowPathFromDevice;
if (d->m_pathChooserDisplay)
d->m_pathChooserDisplay->setAllowPathFromDevice(allowPathFromDevice);
}
/*! /*!
Sets \a elideMode as label elide mode. Sets \a elideMode as label elide mode.
*/ */
@@ -1122,6 +1130,7 @@ void StringAspect::addToLayout(LayoutItem &parent)
d->m_pathChooserDisplay->setPromptDialogFilter(d->m_prompDialogFilter); d->m_pathChooserDisplay->setPromptDialogFilter(d->m_prompDialogFilter);
d->m_pathChooserDisplay->setPromptDialogTitle(d->m_prompDialogTitle); d->m_pathChooserDisplay->setPromptDialogTitle(d->m_prompDialogTitle);
d->m_pathChooserDisplay->setCommandVersionArguments(d->m_commandVersionArguments); d->m_pathChooserDisplay->setCommandVersionArguments(d->m_commandVersionArguments);
d->m_pathChooserDisplay->setAllowPathFromDevice(d->m_allowPathFromDevice);
if (defaultValue() == value()) if (defaultValue() == value())
d->m_pathChooserDisplay->setDefaultValue(defaultValue()); d->m_pathChooserDisplay->setDefaultValue(defaultValue());
else else
@@ -2153,8 +2162,11 @@ void DoubleAspect::setSingleStep(double step)
Its visual representation is a QComboBox with three items. Its visual representation is a QComboBox with three items.
*/ */
TriStateAspect::TriStateAspect(const QString &onString, const QString &offString, TriStateAspect::TriStateAspect(AspectContainer *container,
const QString &onString,
const QString &offString,
const QString &defaultString) const QString &defaultString)
: SelectionAspect(container)
{ {
setDisplayStyle(DisplayStyle::ComboBox); setDisplayStyle(DisplayStyle::ComboBox);
setDefaultValue(TriState::Default); setDefaultValue(TriState::Default);
@@ -2308,7 +2320,7 @@ QList<int> IntegersAspect::value() const
void IntegersAspect::setValue(const QList<int> &value) void IntegersAspect::setValue(const QList<int> &value)
{ {
BaseAspect::setValue(transform(value, &QVariant::fromValue<int>)); BaseAspect::setValue(transform(value, [](int i) { return QVariant::fromValue<int>(i); }));
} }
QList<int> IntegersAspect::defaultValue() const QList<int> IntegersAspect::defaultValue() const
@@ -2319,7 +2331,7 @@ QList<int> IntegersAspect::defaultValue() const
void IntegersAspect::setDefaultValue(const QList<int> &value) void IntegersAspect::setDefaultValue(const QList<int> &value)
{ {
BaseAspect::setDefaultValue(transform(value, &QVariant::fromValue<int>)); BaseAspect::setDefaultValue(transform(value, [](int i) { return QVariant::fromValue<int>(i); }));
} }
@@ -2334,6 +2346,10 @@ void IntegersAspect::setDefaultValue(const QList<int> &value)
A text display does not have a real value. A text display does not have a real value.
*/ */
TextDisplay::TextDisplay(AspectContainer *container)
: BaseAspect(container)
{}
/*! /*!
Constructs a text display showing the \a message with an icon representing Constructs a text display showing the \a message with an icon representing
type \a type. type \a type.

View File

@@ -406,6 +406,7 @@ public:
void setOpenTerminalHandler(const std::function<void()> &openTerminal); void setOpenTerminalHandler(const std::function<void()> &openTerminal);
void setAutoApplyOnEditingFinished(bool applyOnEditingFinished); void setAutoApplyOnEditingFinished(bool applyOnEditingFinished);
void setElideMode(Qt::TextElideMode elideMode); void setElideMode(Qt::TextElideMode elideMode);
void setAllowPathFromDevice(bool allowPathFromDevice);
void validateInput(); void validateInput();
@@ -548,7 +549,8 @@ class QTCREATOR_UTILS_EXPORT TriStateAspect : public SelectionAspect
Q_OBJECT Q_OBJECT
public: public:
TriStateAspect(const QString &onString = {}, TriStateAspect(AspectContainer *container = nullptr,
const QString &onString = {},
const QString &offString = {}, const QString &offString = {},
const QString &defaultString = {}); const QString &defaultString = {});
@@ -608,6 +610,7 @@ class QTCREATOR_UTILS_EXPORT TextDisplay : public BaseAspect
Q_OBJECT Q_OBJECT
public: public:
explicit TextDisplay(AspectContainer *container);
TextDisplay(const QString &message = {}, TextDisplay(const QString &message = {},
InfoLabel::InfoType type = InfoLabel::None); InfoLabel::InfoType type = InfoLabel::None);
~TextDisplay() override; ~TextDisplay() override;

View File

@@ -493,6 +493,20 @@ bool DesktopDeviceFileAccess::hasHardLinks(const FilePath &filePath) const
if (s.st_nlink > 1) if (s.st_nlink > 1)
return true; return true;
} }
#elif defined(Q_OS_WIN)
const HANDLE handle = CreateFile((wchar_t *) filePath.toUserOutput().utf16(),
0,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL);
if (handle == INVALID_HANDLE_VALUE)
return false;
FILE_STANDARD_INFO info;
if (GetFileInformationByHandleEx(handle, FileStandardInfo, &info, sizeof(info)))
return info.NumberOfLinks > 1;
#else #else
Q_UNUSED(filePath) Q_UNUSED(filePath)
#endif #endif

View File

@@ -104,7 +104,7 @@ RunResult DeviceShell::run(const CommandLine &cmd, const QByteArray &stdInData)
QWaitCondition waiter; QWaitCondition waiter;
const int id = ++m_currentId; const int id = ++m_currentId;
const auto it = m_commandOutput.insert(id, CommandRun{{-1, {}, {}}, &waiter}); m_commandOutput.insert(id, CommandRun{{-1, {}, {}}, &waiter});
QMetaObject::invokeMethod(m_shellProcess.get(), [this, id, cmd, stdInData] { QMetaObject::invokeMethod(m_shellProcess.get(), [this, id, cmd, stdInData] {
const QString command = QString("%1 \"%2\" %3\n").arg(id) const QString command = QString("%1 \"%2\" %3\n").arg(id)
@@ -115,6 +115,7 @@ RunResult DeviceShell::run(const CommandLine &cmd, const QByteArray &stdInData)
waiter.wait(&m_commandMutex); waiter.wait(&m_commandMutex);
const auto it = m_commandOutput.constFind(id);
const RunResult result = *it; const RunResult result = *it;
m_commandOutput.erase(it); m_commandOutput.erase(it);

View File

@@ -7,7 +7,7 @@
#include "fileutils.h" #include "fileutils.h"
#include <QMap> #include <QHash>
#include <QMutex> #include <QMutex>
#include <QProcess> #include <QProcess>
#include <QThread> #include <QThread>
@@ -78,8 +78,7 @@ private:
int m_currentId{0}; int m_currentId{0};
QMutex m_commandMutex; QMutex m_commandMutex;
// QMap is used here to preserve iterators QHash<quint64, CommandRun> m_commandOutput;
QMap<quint64, CommandRun> m_commandOutput;
QByteArray m_commandBuffer; QByteArray m_commandBuffer;
State m_shellScriptState = State::Unknown; State m_shellScriptState = State::Unknown;

View File

@@ -11,6 +11,7 @@
#include "utilsicons.h" #include "utilsicons.h"
#include "utilstr.h" #include "utilstr.h"
#include <QApplication>
#include <QKeyEvent> #include <QKeyEvent>
#include <QKeySequence> #include <QKeySequence>
#include <QMenu> #include <QMenu>
@@ -126,7 +127,7 @@ FancyLineEditPrivate::FancyLineEditPrivate(FancyLineEdit *parent) :
m_completionShortcut(completionShortcut()->key(), parent), m_completionShortcut(completionShortcut()->key(), parent),
m_okTextColor(creatorTheme()->color(Theme::TextColorNormal)), m_okTextColor(creatorTheme()->color(Theme::TextColorNormal)),
m_errorTextColor(creatorTheme()->color(Theme::TextColorError)), m_errorTextColor(creatorTheme()->color(Theme::TextColorError)),
m_placeholderTextColor(creatorTheme()->color(Theme::PalettePlaceholderText)) m_placeholderTextColor(QApplication::palette().color(QPalette::PlaceholderText))
{ {
m_completionShortcut.setContext(Qt::WidgetShortcut); m_completionShortcut.setContext(Qt::WidgetShortcut);

View File

@@ -1914,7 +1914,7 @@ FilePath FilePath::canonicalPath() const
return *this; return *this;
} }
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WIN
DWORD flagsAndAttrs = FILE_ATTRIBUTE_NORMAL; DWORD flagsAndAttrs = FILE_ATTRIBUTE_NORMAL;
if (isDir()) if (isDir())
flagsAndAttrs |= FILE_FLAG_BACKUP_SEMANTICS; flagsAndAttrs |= FILE_FLAG_BACKUP_SEMANTICS;

View File

@@ -30,7 +30,7 @@ public:
void start() { void start() {
QTC_ASSERT(!m_taskTree, return); QTC_ASSERT(!m_taskTree, return);
const TaskItem task = m_filePath.needsDevice() ? remoteTask() : localTask(); const GroupItem task = m_filePath.needsDevice() ? remoteTask() : localTask();
m_taskTree.reset(new TaskTree({task})); m_taskTree.reset(new TaskTree({task}));
const auto finalize = [this](bool success) { const auto finalize = [this](bool success) {
m_taskTree.release()->deleteLater(); m_taskTree.release()->deleteLater();
@@ -49,8 +49,8 @@ protected:
std::unique_ptr<TaskTree> m_taskTree; std::unique_ptr<TaskTree> m_taskTree;
private: private:
virtual TaskItem remoteTask() = 0; virtual GroupItem remoteTask() = 0;
virtual TaskItem localTask() = 0; virtual GroupItem localTask() = 0;
}; };
static void localRead(QPromise<QByteArray> &promise, const FilePath &filePath) static void localRead(QPromise<QByteArray> &promise, const FilePath &filePath)
@@ -84,7 +84,7 @@ signals:
void readyRead(const QByteArray &newData); void readyRead(const QByteArray &newData);
private: private:
TaskItem remoteTask() final { GroupItem remoteTask() final {
const auto setup = [this](Process &process) { const auto setup = [this](Process &process) {
const QStringList args = {"if=" + m_filePath.path()}; const QStringList args = {"if=" + m_filePath.path()};
const FilePath dd = m_filePath.withNewPath("dd"); const FilePath dd = m_filePath.withNewPath("dd");
@@ -96,7 +96,7 @@ private:
}; };
return ProcessTask(setup); return ProcessTask(setup);
} }
TaskItem localTask() final { GroupItem localTask() final {
const auto setup = [this](Async<QByteArray> &async) { const auto setup = [this](Async<QByteArray> &async) {
async.setConcurrentCallData(localRead, m_filePath); async.setConcurrentCallData(localRead, m_filePath);
Async<QByteArray> *asyncPtr = &async; Async<QByteArray> *asyncPtr = &async;
@@ -251,7 +251,7 @@ signals:
void started(); void started();
private: private:
TaskItem remoteTask() final { GroupItem remoteTask() final {
const auto setup = [this](Process &process) { const auto setup = [this](Process &process) {
m_writeBuffer = new WriteBuffer(false, &process); m_writeBuffer = new WriteBuffer(false, &process);
connect(m_writeBuffer, &WriteBuffer::writeRequested, &process, &Process::writeRaw); connect(m_writeBuffer, &WriteBuffer::writeRequested, &process, &Process::writeRaw);
@@ -272,7 +272,7 @@ private:
}; };
return ProcessTask(setup, finalize, finalize); return ProcessTask(setup, finalize, finalize);
} }
TaskItem localTask() final { GroupItem localTask() final {
const auto setup = [this](Async<void> &async) { const auto setup = [this](Async<void> &async) {
m_writeBuffer = new WriteBuffer(isBuffered(), &async); m_writeBuffer = new WriteBuffer(isBuffered(), &async);
async.setConcurrentCallData(localWrite, m_filePath, m_writeData, m_writeBuffer); async.setConcurrentCallData(localWrite, m_filePath, m_writeData, m_writeBuffer);
@@ -375,8 +375,7 @@ static void transfer(QPromise<void> &promise, const FilePath &source, const File
if (promise.isCanceled()) if (promise.isCanceled())
return; return;
TaskTree taskTree(transferTask(source, destination)); if (!TaskTree::runBlocking(transferTask(source, destination), promise.future()))
if (!taskTree.runBlocking(promise.future()))
promise.future().cancel(); promise.future().cancel();
} }
@@ -391,7 +390,7 @@ public:
StreamResult m_streamResult = StreamResult::FinishedWithError; StreamResult m_streamResult = StreamResult::FinishedWithError;
std::unique_ptr<TaskTree> m_taskTree; std::unique_ptr<TaskTree> m_taskTree;
TaskItem task() { GroupItem task() {
if (m_streamerMode == StreamMode::Reader) if (m_streamerMode == StreamMode::Reader)
return readerTask(); return readerTask();
if (m_streamerMode == StreamMode::Writer) if (m_streamerMode == StreamMode::Writer)
@@ -400,7 +399,7 @@ public:
} }
private: private:
TaskItem readerTask() { GroupItem readerTask() {
const auto setup = [this](FileStreamReader &reader) { const auto setup = [this](FileStreamReader &reader) {
m_readBuffer.clear(); m_readBuffer.clear();
reader.setFilePath(m_source); reader.setFilePath(m_source);
@@ -410,14 +409,14 @@ private:
}; };
return FileStreamReaderTask(setup); return FileStreamReaderTask(setup);
} }
TaskItem writerTask() { GroupItem writerTask() {
const auto setup = [this](FileStreamWriter &writer) { const auto setup = [this](FileStreamWriter &writer) {
writer.setFilePath(m_destination); writer.setFilePath(m_destination);
writer.setWriteData(m_writeBuffer); writer.setWriteData(m_writeBuffer);
}; };
return FileStreamWriterTask(setup); return FileStreamWriterTask(setup);
} }
TaskItem transferTask() { GroupItem transferTask() {
const auto setup = [this](Async<void> &async) { const auto setup = [this](Async<void> &async) {
async.setConcurrentCallData(transfer, m_source, m_destination); async.setConcurrentCallData(transfer, m_source, m_destination);
}; };

View File

@@ -629,12 +629,18 @@ FilePathInfo::FileFlags fileInfoFlagsfromStatMode(const QString &hexString, int
FilePathInfo::FileFlags result; FilePathInfo::FileFlags result;
if (mode & IRUSR) if (mode & IRUSR) {
result |= FilePathInfo::ReadOwnerPerm; result |= FilePathInfo::ReadOwnerPerm;
if (mode & IWUSR) result |= FilePathInfo::ReadUserPerm;
}
if (mode & IWUSR) {
result |= FilePathInfo::WriteOwnerPerm; result |= FilePathInfo::WriteOwnerPerm;
if (mode & IXUSR) result |= FilePathInfo::WriteUserPerm;
}
if (mode & IXUSR) {
result |= FilePathInfo::ExeOwnerPerm; result |= FilePathInfo::ExeOwnerPerm;
result |= FilePathInfo::ExeUserPerm;
}
if (mode & IRGRP) if (mode & IRGRP)
result |= FilePathInfo::ReadGroupPerm; result |= FilePathInfo::ReadGroupPerm;
if (mode & IWGRP) if (mode & IWGRP)

View File

@@ -676,24 +676,22 @@ LayoutItem st()
LayoutItem noMargin() LayoutItem noMargin()
{ {
LayoutItem item; return customMargin({});
item.onAdd = [](LayoutBuilder &builder) {
if (auto layout = builder.stack.last().layout)
layout->setContentsMargins(0, 0, 0, 0);
else if (auto widget = builder.stack.last().widget)
widget->setContentsMargins(0, 0, 0, 0);
};
return item;
} }
LayoutItem normalMargin() LayoutItem normalMargin()
{
return customMargin({9, 9, 9, 9});
}
LayoutItem customMargin(const QMargins &margin)
{ {
LayoutItem item; LayoutItem item;
item.onAdd = [](LayoutBuilder &builder) { item.onAdd = [margin](LayoutBuilder &builder) {
if (auto layout = builder.stack.last().layout) if (auto layout = builder.stack.last().layout)
layout->setContentsMargins(9, 9, 9, 9); layout->setContentsMargins(margin);
else if (auto widget = builder.stack.last().widget) else if (auto widget = builder.stack.last().widget)
widget->setContentsMargins(9, 9, 9, 9); widget->setContentsMargins(margin);
}; };
return item; return item;
} }

View File

@@ -11,12 +11,15 @@
#if defined(UTILS_LIBRARY) #if defined(UTILS_LIBRARY)
# define QTCREATOR_UTILS_EXPORT Q_DECL_EXPORT # define QTCREATOR_UTILS_EXPORT Q_DECL_EXPORT
#elif defined(UTILS_STATIC_LIBRARY)
# define QTCREATOR_UTILS_EXPORT
#else #else
# define QTCREATOR_UTILS_EXPORT Q_DECL_IMPORT # define QTCREATOR_UTILS_EXPORT Q_DECL_IMPORT
#endif #endif
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QLayout; class QLayout;
class QMargins;
class QObject; class QObject;
class QWidget; class QWidget;
template <class T> T qobject_cast(QObject *object); template <class T> T qobject_cast(QObject *object);
@@ -200,6 +203,7 @@ QTCREATOR_UTILS_EXPORT LayoutItem empty();
QTCREATOR_UTILS_EXPORT LayoutItem hr(); QTCREATOR_UTILS_EXPORT LayoutItem hr();
QTCREATOR_UTILS_EXPORT LayoutItem noMargin(); QTCREATOR_UTILS_EXPORT LayoutItem noMargin();
QTCREATOR_UTILS_EXPORT LayoutItem normalMargin(); QTCREATOR_UTILS_EXPORT LayoutItem normalMargin();
QTCREATOR_UTILS_EXPORT LayoutItem customMargin(const QMargins &margin);
QTCREATOR_UTILS_EXPORT LayoutItem withFormAlignment(); QTCREATOR_UTILS_EXPORT LayoutItem withFormAlignment();
// "Setters" // "Setters"

View File

@@ -133,9 +133,13 @@ TextTip::TextTip(QWidget *parent) : TipLabel(parent)
setWindowOpacity(style()->styleHint(QStyle::SH_ToolTipLabel_Opacity, nullptr, this) / 255.0); setWindowOpacity(style()->styleHint(QStyle::SH_ToolTipLabel_Opacity, nullptr, this) / 255.0);
} }
static bool likelyContainsLink(const QString &s) static bool likelyContainsLink(const QString &s, const Qt::TextFormat &format)
{ {
return s.contains(QLatin1String("href"), Qt::CaseInsensitive); if (s.contains(QLatin1String("href"), Qt::CaseInsensitive))
return true;
if (format == Qt::MarkdownText)
return s.contains("](");
return false;
} }
void TextTip::setContent(const QVariant &content) void TextTip::setContent(const QVariant &content)
@@ -148,13 +152,13 @@ void TextTip::setContent(const QVariant &content)
m_format = item.second; m_format = item.second;
} }
bool containsLink = likelyContainsLink(m_text); bool containsLink = likelyContainsLink(m_text, m_format);
setOpenExternalLinks(containsLink); setOpenExternalLinks(containsLink);
} }
bool TextTip::isInteractive() const bool TextTip::isInteractive() const
{ {
return likelyContainsLink(m_text); return likelyContainsLink(m_text, m_format);
} }
void TextTip::configure(const QPoint &pos) void TextTip::configure(const QPoint &pos)

View File

@@ -122,7 +122,7 @@ private:
QMap<QString, FilePath> m_filesToPull; QMap<QString, FilePath> m_filesToPull;
QStringList m_androidABIs; QStringList m_androidABIs;
BoolAspect *m_uninstallPreviousPackage = nullptr; BoolAspect m_uninstallPreviousPackage{this};
bool m_uninstallPreviousPackageRun = false; bool m_uninstallPreviousPackageRun = false;
bool m_useAndroiddeployqt = false; bool m_useAndroiddeployqt = false;
bool m_askForUninstall = false; bool m_askForUninstall = false;
@@ -143,17 +143,16 @@ AndroidDeployQtStep::AndroidDeployQtStep(BuildStepList *parent, Id id)
setImmutable(true); setImmutable(true);
setUserExpanded(true); setUserExpanded(true);
m_uninstallPreviousPackage = addAspect<BoolAspect>(); m_uninstallPreviousPackage.setSettingsKey(UninstallPreviousPackageKey);
m_uninstallPreviousPackage->setSettingsKey(UninstallPreviousPackageKey); m_uninstallPreviousPackage.setLabel(Tr::tr("Uninstall the existing app before deployment"),
m_uninstallPreviousPackage->setLabel(Tr::tr("Uninstall the existing app before deployment"),
BoolAspect::LabelPlacement::AtCheckBox); BoolAspect::LabelPlacement::AtCheckBox);
m_uninstallPreviousPackage->setValue(false); m_uninstallPreviousPackage.setValue(false);
const QtSupport::QtVersion * const qt = QtSupport::QtKitAspect::qtVersion(kit()); const QtSupport::QtVersion * const qt = QtSupport::QtKitAspect::qtVersion(kit());
const bool forced = qt && qt->qtVersion() < QVersionNumber(5, 4, 0); const bool forced = qt && qt->qtVersion() < QVersionNumber(5, 4, 0);
if (forced) { if (forced) {
m_uninstallPreviousPackage->setValue(true); m_uninstallPreviousPackage.setValue(true);
m_uninstallPreviousPackage->setEnabled(false); m_uninstallPreviousPackage.setEnabled(false);
} }
connect(this, &AndroidDeployQtStep::askForUninstall, connect(this, &AndroidDeployQtStep::askForUninstall,
@@ -274,7 +273,7 @@ bool AndroidDeployQtStep::init()
emit addOutput(Tr::tr("Deploying to %1").arg(m_serialNumber), OutputFormat::NormalMessage); emit addOutput(Tr::tr("Deploying to %1").arg(m_serialNumber), OutputFormat::NormalMessage);
m_uninstallPreviousPackageRun = m_uninstallPreviousPackage->value(); m_uninstallPreviousPackageRun = m_uninstallPreviousPackage();
if (m_uninstallPreviousPackageRun) if (m_uninstallPreviousPackageRun)
m_manifestName = AndroidManager::manifestPath(target()); m_manifestName = AndroidManager::manifestPath(target());

View File

@@ -20,8 +20,8 @@ CTestSettings::CTestSettings(Id settingsId)
setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY); setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY);
setDisplayName(Tr::tr("CTest")); setDisplayName(Tr::tr("CTest"));
setLayouter([this](QWidget *w) { setLayouter([this] {
Row { Form { return Row { Form {
outputOnFail, br, outputOnFail, br,
scheduleRandom, br, scheduleRandom, br,
stopOnFailure, br, stopOnFailure, br,
@@ -39,7 +39,7 @@ CTestSettings::CTestSettings(Id settingsId)
Row { testLoad, threshold} Row { testLoad, threshold}
} }
} }
}, st }.attachTo(w); }, st };
}); });
outputOnFail.setSettingsKey("OutputOnFail"); outputOnFail.setSettingsKey("OutputOnFail");

View File

@@ -35,7 +35,7 @@ using namespace ProjectExplorer;
static bool isProjectParsing() static bool isProjectParsing()
{ {
const BuildSystem *bs = ProjectManager::startupBuildSystem(); const BuildSystem *bs = ProjectManager::startupBuildSystem();
return bs && bs->isParsing(); return bs && (bs->isParsing() || bs->isWaitingForParse());
} }
TestCodeParser::TestCodeParser() TestCodeParser::TestCodeParser()
@@ -360,7 +360,7 @@ void TestCodeParser::scanForTests(const QSet<FilePath> &filePaths,
using namespace Tasking; using namespace Tasking;
QList<TaskItem> tasks{parallelLimit(std::max(QThread::idealThreadCount() / 4, 1))}; QList<GroupItem> tasks{parallelLimit(std::max(QThread::idealThreadCount() / 4, 1))};
for (const FilePath &file : filteredFiles) { for (const FilePath &file : filteredFiles) {
const auto setup = [this, codeParsers, file](Async<TestParseResultPtr> &async) { const auto setup = [this, codeParsers, file](Async<TestParseResultPtr> &async) {
async.setConcurrentCallData(parseFileForTests, codeParsers, file); async.setConcurrentCallData(parseFileForTests, codeParsers, file);

View File

@@ -347,7 +347,7 @@ void TestRunner::runTestsHelper()
std::unique_ptr<TestOutputReader> m_outputReader; std::unique_ptr<TestOutputReader> m_outputReader;
}; };
QList<TaskItem> tasks{optional}; QList<GroupItem> tasks{finishAllAndDone};
for (ITestConfiguration *config : m_selectedTests) { for (ITestConfiguration *config : m_selectedTests) {
QTC_ASSERT(config, continue); QTC_ASSERT(config, continue);
@@ -442,7 +442,7 @@ void TestRunner::runTestsHelper()
} }
}; };
const Group group { const Group group {
optional, finishAllAndDone,
Storage(storage), Storage(storage),
onGroupSetup(onSetup), onGroupSetup(onSetup),
ProcessTask(onProcessSetup, onProcessDone, onProcessDone) ProcessTask(onProcessSetup, onProcessDone, onProcessDone)

View File

@@ -42,23 +42,23 @@ private:
void doRun() final; void doRun() final;
bool m_runAutogen = false; bool m_runAutogen = false;
StringAspect m_arguments{this};
}; };
AutogenStep::AutogenStep(BuildStepList *bsl, Id id) : AbstractProcessStep(bsl, id) AutogenStep::AutogenStep(BuildStepList *bsl, Id id) : AbstractProcessStep(bsl, id)
{ {
auto arguments = addAspect<StringAspect>(); m_arguments.setSettingsKey("AutotoolsProjectManager.AutogenStep.AdditionalArguments");
arguments->setSettingsKey("AutotoolsProjectManager.AutogenStep.AdditionalArguments"); m_arguments.setLabelText(Tr::tr("Arguments:"));
arguments->setLabelText(Tr::tr("Arguments:")); m_arguments.setDisplayStyle(StringAspect::LineEditDisplay);
arguments->setDisplayStyle(StringAspect::LineEditDisplay); m_arguments.setHistoryCompleter("AutotoolsPM.History.AutogenStepArgs");
arguments->setHistoryCompleter("AutotoolsPM.History.AutogenStepArgs");
connect(arguments, &BaseAspect::changed, this, [this] { m_runAutogen = true; }); connect(&m_arguments, &BaseAspect::changed, this, [this] { m_runAutogen = true; });
setWorkingDirectoryProvider([this] { return project()->projectDirectory(); }); setWorkingDirectoryProvider([this] { return project()->projectDirectory(); });
setCommandLineProvider([this, arguments] { setCommandLineProvider([this] {
return CommandLine(project()->projectDirectory() / "autogen.sh", return CommandLine(project()->projectDirectory() / "autogen.sh",
arguments->value(), m_arguments(),
CommandLine::Raw); CommandLine::Raw);
}); });

View File

@@ -66,10 +66,10 @@ BazaarSettings::BazaarSettings()
timeout.setLabelText(Tr::tr("Timeout:")); timeout.setLabelText(Tr::tr("Timeout:"));
timeout.setSuffix(Tr::tr("s")); timeout.setSuffix(Tr::tr("s"));
setLayouter([this](QWidget *widget) { setLayouter([this] {
using namespace Layouting; using namespace Layouting;
Column { return Column {
Group { Group {
title(Tr::tr("Configuration")), title(Tr::tr("Configuration")),
Row { binaryPath } Row { binaryPath }
@@ -88,7 +88,7 @@ BazaarSettings::BazaarSettings()
Row { logCount, timeout, st } Row { logCount, timeout, st }
}, },
st st
}.attachTo(widget); };
}); });
} }

View File

@@ -297,7 +297,7 @@ void AbstractSettings::read()
void AbstractSettings::readDocumentation() void AbstractSettings::readDocumentation()
{ {
const FilePath filename = documentationFilePath(); const FilePath filename = documentationFilePath;
if (filename.isEmpty()) { if (filename.isEmpty()) {
BeautifierPlugin::showError(Tr::tr("No documentation file specified.")); BeautifierPlugin::showError(Tr::tr("No documentation file specified."));
return; return;

View File

@@ -51,7 +51,8 @@ public:
Utils::FilePathAspect command{this}; Utils::FilePathAspect command{this};
Utils::StringAspect supportedMimeTypes{this}; Utils::StringAspect supportedMimeTypes{this};
Utils::FilePathAspect documentationFilePath; // Intentionally not saved.
Utils::FilePath documentationFilePath;
QVersionNumber version() const; QVersionNumber version() const;

View File

@@ -59,11 +59,11 @@ ArtisticStyleSettings::ArtisticStyleSettings()
customStyle.setSettingsKey("customStyle"); customStyle.setSettingsKey("customStyle");
documentationFilePath.setFilePath( documentationFilePath =
Core::ICore::userResourcePath(Beautifier::Constants::SETTINGS_DIRNAME) Core::ICore::userResourcePath(Beautifier::Constants::SETTINGS_DIRNAME)
.pathAppended(Beautifier::Constants::DOCUMENTATION_DIRNAME) .pathAppended(Beautifier::Constants::DOCUMENTATION_DIRNAME)
.pathAppended(SETTINGS_NAME) .pathAppended(SETTINGS_NAME)
.stringAppended(".xml")); .stringAppended(".xml");
read(); read();
} }
@@ -77,7 +77,7 @@ void ArtisticStyleSettings::createDocumentationFile() const
if (process.result() != ProcessResult::FinishedWithSuccess) if (process.result() != ProcessResult::FinishedWithSuccess)
return; return;
QFile file(documentationFilePath().toFSPathString()); QFile file(documentationFilePath.toFSPathString());
const QFileInfo fi(file); const QFileInfo fi(file);
if (!fi.exists()) if (!fi.exists())
fi.dir().mkpath(fi.absolutePath()); fi.dir().mkpath(fi.absolutePath());

View File

@@ -63,16 +63,16 @@ ClangFormatSettings::ClangFormatSettings()
customStyle.setSettingsKey("customStyle"); customStyle.setSettingsKey("customStyle");
documentationFilePath.setFilePath(Core::ICore::userResourcePath(Constants::SETTINGS_DIRNAME) documentationFilePath = Core::ICore::userResourcePath(Constants::SETTINGS_DIRNAME)
.pathAppended(Constants::DOCUMENTATION_DIRNAME) .pathAppended(Constants::DOCUMENTATION_DIRNAME)
.pathAppended(SETTINGS_NAME).stringAppended(".xml")); .pathAppended(SETTINGS_NAME).stringAppended(".xml");
read(); read();
} }
void ClangFormatSettings::createDocumentationFile() const void ClangFormatSettings::createDocumentationFile() const
{ {
QFile file(documentationFilePath().toFSPathString()); QFile file(documentationFilePath.toFSPathString());
const QFileInfo fi(file); const QFileInfo fi(file);
if (!fi.exists()) if (!fi.exists())
fi.dir().mkpath(fi.absolutePath()); fi.dir().mkpath(fi.absolutePath());

View File

@@ -66,9 +66,9 @@ UncrustifySettings::UncrustifySettings()
specificConfigFile.setExpectedKind(Utils::PathChooser::File); specificConfigFile.setExpectedKind(Utils::PathChooser::File);
specificConfigFile.setPromptDialogFilter(Tr::tr("Uncrustify file (*.cfg)")); specificConfigFile.setPromptDialogFilter(Tr::tr("Uncrustify file (*.cfg)"));
documentationFilePath.setFilePath(Core::ICore::userResourcePath(Constants::SETTINGS_DIRNAME) documentationFilePath = Core::ICore::userResourcePath(Constants::SETTINGS_DIRNAME)
.pathAppended(Constants::DOCUMENTATION_DIRNAME) .pathAppended(Constants::DOCUMENTATION_DIRNAME)
.pathAppended(SETTINGS_NAME).stringAppended(".xml")); .pathAppended(SETTINGS_NAME).stringAppended(".xml");
read(); read();
} }
@@ -82,7 +82,7 @@ void UncrustifySettings::createDocumentationFile() const
if (process.result() != ProcessResult::FinishedWithSuccess) if (process.result() != ProcessResult::FinishedWithSuccess)
return; return;
QFile file(documentationFilePath().toFSPathString()); QFile file(documentationFilePath.toFSPathString());
const QFileInfo fi(file); const QFileInfo fi(file);
if (!fi.exists()) if (!fi.exists())
fi.dir().mkpath(fi.absolutePath()); fi.dir().mkpath(fi.absolutePath());

View File

@@ -421,8 +421,12 @@ void doSemanticHighlighting(
if (ClangdClient * const client = ClangModelManagerSupport::clientForFile(filePath)) if (ClangdClient * const client = ClangModelManagerSupport::clientForFile(filePath))
client->setVirtualRanges(filePath, virtualRanges, docRevision); client->setVirtualRanges(filePath, virtualRanges, docRevision);
}, Qt::QueuedConnection); }, Qt::QueuedConnection);
#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
promise.addResults(results);
#else
for (const HighlightingResult &r : results) for (const HighlightingResult &r : results)
promise.addResult(r); promise.addResult(r);
#endif
} }
} }

View File

@@ -17,6 +17,7 @@
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h> #include <coreplugin/messagemanager.h>
#include <coreplugin/session.h> #include <coreplugin/session.h>
#include <coreplugin/vcsmanager.h>
#include <cppeditor/cppcodemodelsettings.h> #include <cppeditor/cppcodemodelsettings.h>
#include <cppeditor/cppeditorconstants.h> #include <cppeditor/cppeditorconstants.h>
@@ -686,6 +687,7 @@ void ClangModelManagerSupport::claimNonProjectSources(ClangdClient *client)
// for the respective project to force re-parsing of open documents and re-indexing. // for the respective project to force re-parsing of open documents and re-indexing.
// While this is not 100% bullet-proof, chances are good that in a typical session-based // While this is not 100% bullet-proof, chances are good that in a typical session-based
// workflow, e.g. a git branch switch will hit at least one open file. // workflow, e.g. a git branch switch will hit at least one open file.
// We also look for repository changes explicitly.
void ClangModelManagerSupport::watchForExternalChanges() void ClangModelManagerSupport::watchForExternalChanges()
{ {
connect(DocumentManager::instance(), &DocumentManager::filesChangedExternally, connect(DocumentManager::instance(), &DocumentManager::filesChangedExternally,
@@ -709,6 +711,23 @@ void ClangModelManagerSupport::watchForExternalChanges()
return; return;
} }
}); });
connect(VcsManager::instance(), &VcsManager::repositoryChanged,
this, [this](const FilePath &repoDir) {
if (sessionModeEnabled()) {
if (ClangdClient * const client = clientForProject(nullptr))
scheduleClientRestart(client);
return;
}
for (const Project * const project : ProjectManager::projects()) {
const FilePath &projectDir = project->projectDirectory();
if (repoDir == projectDir || repoDir.isChildOf(projectDir)
|| projectDir.isChildOf(repoDir)) {
if (ClangdClient * const client = clientForProject(project))
scheduleClientRestart(client);
}
}
});
} }
// If Qt Creator changes a file that is not open (e.g. as part of a quickfix), we have to // If Qt Creator changes a file that is not open (e.g. as part of a quickfix), we have to

View File

@@ -183,7 +183,7 @@ void ClangToolRunWorker::start()
m_filesAnalyzed.clear(); m_filesAnalyzed.clear();
m_filesNotAnalyzed.clear(); m_filesNotAnalyzed.clear();
QList<TaskItem> tasks{parallelLimit(qMax(1, m_runSettings.parallelJobs()))}; QList<GroupItem> tasks{parallelLimit(qMax(1, m_runSettings.parallelJobs()))};
for (const AnalyzeUnit &unit : std::as_const(unitsToProcess)) { for (const AnalyzeUnit &unit : std::as_const(unitsToProcess)) {
if (!m_diagnosticConfig.isEnabled(tool) if (!m_diagnosticConfig.isEnabled(tool)
&& !m_runSettings.hasConfigFileForSourceFile(unit.file)) { && !m_runSettings.hasConfigFileForSourceFile(unit.file)) {

View File

@@ -101,9 +101,9 @@ static FilePath createOutputFilePath(const FilePath &dirPath, const FilePath &fi
return {}; return {};
} }
TaskItem clangToolTask(const AnalyzeInputData &input, GroupItem clangToolTask(const AnalyzeInputData &input,
const AnalyzeSetupHandler &setupHandler, const AnalyzeSetupHandler &setupHandler,
const AnalyzeOutputHandler &outputHandler) const AnalyzeOutputHandler &outputHandler)
{ {
struct ClangToolStorage { struct ClangToolStorage {
QString name; QString name;
@@ -186,7 +186,7 @@ TaskItem clangToolTask(const AnalyzeInputData &input,
Storage(storage), Storage(storage),
onGroupSetup(onSetup), onGroupSetup(onSetup),
Group { Group {
optional, finishAllAndDone,
ProcessTask(onProcessSetup, onProcessDone, onProcessError) ProcessTask(onProcessSetup, onProcessDone, onProcessError)
} }
}; };

View File

@@ -10,7 +10,7 @@
#include <utils/environment.h> #include <utils/environment.h>
namespace Tasking { class TaskItem; } namespace Tasking { class GroupItem; }
namespace ClangTools { namespace ClangTools {
namespace Internal { namespace Internal {
@@ -50,9 +50,9 @@ struct AnalyzeOutputData
using AnalyzeSetupHandler = std::function<bool()>; using AnalyzeSetupHandler = std::function<bool()>;
using AnalyzeOutputHandler = std::function<void(const AnalyzeOutputData &)>; using AnalyzeOutputHandler = std::function<void(const AnalyzeOutputData &)>;
Tasking::TaskItem clangToolTask(const AnalyzeInputData &input, Tasking::GroupItem clangToolTask(const AnalyzeInputData &input,
const AnalyzeSetupHandler &setupHandler, const AnalyzeSetupHandler &setupHandler,
const AnalyzeOutputHandler &outputHandler); const AnalyzeOutputHandler &outputHandler);
} // namespace Internal } // namespace Internal
} // namespace ClangTools } // namespace ClangTools

View File

@@ -190,7 +190,7 @@ void DocumentClangToolRunner::run()
vfso().update(); vfso().update();
const ClangDiagnosticConfig config = diagnosticConfig(runSettings.diagnosticConfigId()); const ClangDiagnosticConfig config = diagnosticConfig(runSettings.diagnosticConfigId());
const Environment env = projectBuildEnvironment(project); const Environment env = projectBuildEnvironment(project);
QList<TaskItem> tasks{parallel}; QList<GroupItem> tasks{parallel};
const auto addClangTool = [this, &runSettings, &config, &env, &tasks](ClangToolType tool) { const auto addClangTool = [this, &runSettings, &config, &env, &tasks](ClangToolType tool) {
if (!toolEnabled(tool, config, runSettings)) if (!toolEnabled(tool, config, runSettings))
return; return;
@@ -209,7 +209,7 @@ void DocumentClangToolRunner::run()
return !m_document->isModified() || isVFSOverlaySupported(executable); return !m_document->isModified() || isVFSOverlaySupported(executable);
}; };
const auto outputHandler = [this](const AnalyzeOutputData &output) { onDone(output); }; const auto outputHandler = [this](const AnalyzeOutputData &output) { onDone(output); };
tasks.append(Group{optional, clangToolTask(input, setupHandler, outputHandler)}); tasks.append(Group{finishAllAndDone, clangToolTask(input, setupHandler, outputHandler)});
}; };
addClangTool(ClangToolType::Tidy); addClangTool(ClangToolType::Tidy);
addClangTool(ClangToolType::Clazy); addClangTool(ClangToolType::Clazy);

View File

@@ -378,7 +378,7 @@ void Manager::gotoLocations(const QList<QVariant> &list)
int line; int line;
int column; int column;
textEditor->convertPosition(textEditor->position(), &line, &column); textEditor->convertPosition(textEditor->position(), &line, &column);
const SymbolLocation current(filePath, line, column); const SymbolLocation current(filePath, line, column + 1);
if (auto it = locations.constFind(current), end = locations.constEnd(); it != end) { if (auto it = locations.constFind(current), end = locations.constEnd(); it != end) {
// we already are at the symbol, cycle to next location // we already are at the symbol, cycle to next location
++it; ++it;

View File

@@ -227,6 +227,10 @@ private:
Q_INVOKABLE void updateStatusActions(); Q_INVOKABLE void updateStatusActions();
QString commitDisplayName() const final; QString commitDisplayName() const final;
QString commitAbortTitle() const final;
QString commitAbortMessage() const final;
QString commitErrorMessage(const QString &error) const final;
void checkOutCurrentFile(); void checkOutCurrentFile();
void addCurrentFile(); void addCurrentFile();
void undoCheckOutCurrent(); void undoCheckOutCurrent();
@@ -948,9 +952,27 @@ void ClearCasePluginPrivate::updateActions(VcsBasePluginPrivate::ActionState as)
QString ClearCasePluginPrivate::commitDisplayName() const QString ClearCasePluginPrivate::commitDisplayName() const
{ {
//: Name of the "commit" action of the VCS
return Tr::tr("Check In"); return Tr::tr("Check In");
} }
QString ClearCasePluginPrivate::commitAbortTitle() const
{
return Tr::tr("Close Check In Editor");
}
QString ClearCasePluginPrivate::commitAbortMessage() const
{
return Tr::tr("Closing this editor will abort the check in.");
}
QString ClearCasePluginPrivate::commitErrorMessage(const QString &error) const
{
if (error.isEmpty())
return Tr::tr("Cannot check in.");
return Tr::tr("Cannot check in: %1.").arg(error);
}
void ClearCasePluginPrivate::checkOutCurrentFile() void ClearCasePluginPrivate::checkOutCurrentFile()
{ {
const VcsBasePluginState state = currentState(); const VcsBasePluginState state = currentState();

View File

@@ -157,7 +157,7 @@ void CMakeEditorWidget::findLinkAt(const QTextCursor &cursor,
// find the beginning of a filename // find the beginning of a filename
QString buffer; QString buffer;
int beginPos = column - 1; int beginPos = column;
while (beginPos >= 0) { while (beginPos >= 0) {
if (isValidFileNameChar(block, beginPos)) { if (isValidFileNameChar(block, beginPos)) {
buffer.prepend(block.at(beginPos)); buffer.prepend(block.at(beginPos));

View File

@@ -65,9 +65,9 @@ public:
autoFormatMime.setDefaultValue("text/x-cmake"); autoFormatMime.setDefaultValue("text/x-cmake");
autoFormatMime.setLabelText(Tr::tr("Restrict to MIME types:")); autoFormatMime.setLabelText(Tr::tr("Restrict to MIME types:"));
setLayouter([this](QWidget *widget) { setLayouter([this] {
using namespace Layouting; using namespace Layouting;
Column { return Column {
Row { Tr::tr("CMakeFormat command:"), command }, Row { Tr::tr("CMakeFormat command:"), command },
Space(10), Space(10),
Group { Group {
@@ -79,7 +79,7 @@ public:
} }
}, },
st st
}.attachTo(widget); };
}); });
ActionContainer *menu = ActionManager::createMenu(Constants::CMAKEFORMATTER_MENU_ID); ActionContainer *menu = ActionManager::createMenu(Constants::CMAKEFORMATTER_MENU_ID);

View File

@@ -112,7 +112,7 @@ ConanInstallStep::ConanInstallStep(BuildStepList *bsl, Id id)
return param.summary(displayName()); return param.summary(displayName());
}); });
connect(ProjectManager::instance(), &ProjectManager::projectAdded, this, [this](Project * project) { connect(ProjectManager::instance(), &ProjectManager::projectAdded, this, [](Project * project) {
connect(project, &Project::addedTarget, project, [project] (Target *target) { connect(project, &Project::addedTarget, project, [project] (Target *target) {
connectTarget(project, target); connectTarget(project, target);
}); });

View File

@@ -93,7 +93,7 @@ void AuthWidget::updateClient(const Utils::FilePath &nodeJs, const Utils::FilePa
m_client = nullptr; m_client = nullptr;
setState(Tr::tr("Sign in"), false); setState(Tr::tr("Sign in"), false);
m_button->setEnabled(false); m_button->setEnabled(false);
if (!nodeJs.exists() || !agent.exists()) { if (!nodeJs.isExecutableFile() || !agent.exists()) {
return; return;
} }

View File

@@ -5,6 +5,7 @@ QtcPlugin {
Depends { name: "Core" } Depends { name: "Core" }
Depends { name: "LanguageClient" } Depends { name: "LanguageClient" }
Depends { name: "ProjectExplorer" }
Depends { name: "TextEditor" } Depends { name: "TextEditor" }
Depends { name: "Qt"; submodules: ["widgets", "xml", "network"] } Depends { name: "Qt"; submodules: ["widgets", "xml", "network"] }

View File

@@ -139,6 +139,7 @@ void CopilotHoverHandler::identifyMatch(TextEditorWidget *editorWidget,
void CopilotHoverHandler::operateTooltip(TextEditorWidget *editorWidget, const QPoint &point) void CopilotHoverHandler::operateTooltip(TextEditorWidget *editorWidget, const QPoint &point)
{ {
Q_UNUSED(point)
auto *suggestion = dynamic_cast<CopilotSuggestion *>(TextDocumentLayout::suggestion(m_block)); auto *suggestion = dynamic_cast<CopilotSuggestion *>(TextDocumentLayout::suggestion(m_block));
if (!suggestion) if (!suggestion)
@@ -147,8 +148,12 @@ void CopilotHoverHandler::operateTooltip(TextEditorWidget *editorWidget, const Q
auto tooltipWidget = new CopilotCompletionToolTip(suggestion->completions(), auto tooltipWidget = new CopilotCompletionToolTip(suggestion->completions(),
suggestion->currentCompletion(), suggestion->currentCompletion(),
editorWidget); editorWidget);
const qreal deltay = 2 * editorWidget->textDocument()->fontSettings().lineSpacing();
ToolTip::show(point - QPoint{0, int(deltay)}, tooltipWidget, editorWidget); const QRect cursorRect = editorWidget->cursorRect(editorWidget->textCursor());
QPoint pos = editorWidget->viewport()->mapToGlobal(cursorRect.topLeft())
- Utils::ToolTip::offsetFromPosition();
pos.ry() -= tooltipWidget->sizeHint().height();
ToolTip::show(pos, tooltipWidget, editorWidget);
} }
} // namespace Copilot::Internal } // namespace Copilot::Internal

View File

@@ -120,6 +120,9 @@ void CopilotPlugin::extensionsInitialized()
void CopilotPlugin::restartClient() void CopilotPlugin::restartClient()
{ {
LanguageClient::LanguageClientManager::shutdownClient(m_client); LanguageClient::LanguageClientManager::shutdownClient(m_client);
if (!CopilotSettings::instance().nodeJsPath().isExecutableFile())
return;
m_client = new CopilotClient(CopilotSettings::instance().nodeJsPath(), m_client = new CopilotClient(CopilotSettings::instance().nodeJsPath(),
CopilotSettings::instance().distPath()); CopilotSettings::instance().distPath());
} }

View File

@@ -53,7 +53,7 @@ CopilotSettings::CopilotSettings()
nodeJsPath.setHistoryCompleter("Copilot.NodePath.History"); nodeJsPath.setHistoryCompleter("Copilot.NodePath.History");
nodeJsPath.setDisplayName(Tr::tr("Node.js Path")); nodeJsPath.setDisplayName(Tr::tr("Node.js Path"));
nodeJsPath.setToolTip( nodeJsPath.setToolTip(
Tr::tr("Select path to node.js executable. See https://nodejs.org/de/download/" Tr::tr("Select path to node.js executable. See https://nodejs.org/en/download/"
"for installation instructions.")); "for installation instructions."));
distPath.setExpectedKind(PathChooser::File); distPath.setExpectedKind(PathChooser::File);
@@ -63,7 +63,7 @@ CopilotSettings::CopilotSettings()
distPath.setHistoryCompleter("Copilot.DistPath.History"); distPath.setHistoryCompleter("Copilot.DistPath.History");
distPath.setDisplayName(Tr::tr("Agent.js path")); distPath.setDisplayName(Tr::tr("Agent.js path"));
distPath.setToolTip(Tr::tr( distPath.setToolTip(Tr::tr(
"Select path to agent.js in copilot neovim plugin. See " "Select path to agent.js in Copilot Neovim plugin. See "
"https://github.com/github/copilot.vim#getting-started for installation instructions.")); "https://github.com/github/copilot.vim#getting-started for installation instructions."));
autoComplete.setDisplayName(Tr::tr("Auto Complete")); autoComplete.setDisplayName(Tr::tr("Auto Complete"));
@@ -71,7 +71,7 @@ CopilotSettings::CopilotSettings()
autoComplete.setLabelText(Tr::tr("Request completions automatically")); autoComplete.setLabelText(Tr::tr("Request completions automatically"));
autoComplete.setDefaultValue(true); autoComplete.setDefaultValue(true);
autoComplete.setToolTip(Tr::tr("Automatically request suggestions for the current text cursor " autoComplete.setToolTip(Tr::tr("Automatically request suggestions for the current text cursor "
"position after changes to the document")); "position after changes to the document."));
initEnableAspect(enableCopilot); initEnableAspect(enableCopilot);
} }

View File

@@ -347,8 +347,10 @@ void Internal::CommandPrivate::setCurrentContext(const Context &context)
m_context = context; m_context = context;
QAction *currentAction = nullptr; QAction *currentAction = nullptr;
for (int i = 0; i < m_context.size(); ++i) { for (const Id &id : std::as_const(m_context)) {
if (QAction *a = m_contextActionMap.value(m_context.at(i), nullptr)) { if (id == Constants::C_GLOBAL_CUTOFF)
break;
if (QAction *a = m_contextActionMap.value(id, nullptr)) {
currentAction = a; currentAction = a;
break; break;
} }

View File

@@ -6,18 +6,17 @@
#include <coreplugin/coreplugintr.h> #include <coreplugin/coreplugintr.h>
#include <coreplugin/dialogs/shortcutsettings.h> #include <coreplugin/dialogs/shortcutsettings.h>
#include <utils/headerviewstretcher.h>
#include <utils/fancylineedit.h> #include <utils/fancylineedit.h>
#include <utils/headerviewstretcher.h>
#include <utils/layoutbuilder.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <QDebug> #include <QDebug>
#include <QGroupBox> #include <QGroupBox>
#include <QHBoxLayout>
#include <QLabel> #include <QLabel>
#include <QPointer> #include <QPointer>
#include <QPushButton> #include <QPushButton>
#include <QTreeWidgetItem> #include <QTreeWidgetItem>
#include <QVBoxLayout>
Q_DECLARE_METATYPE(Core::Internal::ShortcutItem*) Q_DECLARE_METATYPE(Core::Internal::ShortcutItem*)
@@ -32,13 +31,10 @@ public:
CommandMappingsPrivate(CommandMappings *parent) CommandMappingsPrivate(CommandMappings *parent)
: q(parent) : q(parent)
{ {
groupBox = new QGroupBox(parent); filterEdit = new FancyLineEdit;
groupBox->setTitle(::Core::Tr::tr("Command Mappings"));
filterEdit = new FancyLineEdit(groupBox);
filterEdit->setFiltering(true); filterEdit->setFiltering(true);
commandList = new QTreeWidget(groupBox); commandList = new QTreeWidget;
commandList->setRootIsDecorated(false); commandList->setRootIsDecorated(false);
commandList->setUniformRowHeights(true); commandList->setUniformRowHeights(true);
commandList->setSortingEnabled(true); commandList->setSortingEnabled(true);
@@ -49,33 +45,28 @@ public:
item->setText(1, ::Core::Tr::tr("Label")); item->setText(1, ::Core::Tr::tr("Label"));
item->setText(0, ::Core::Tr::tr("Command")); item->setText(0, ::Core::Tr::tr("Command"));
defaultButton = new QPushButton(::Core::Tr::tr("Reset All"), groupBox); defaultButton = new QPushButton(::Core::Tr::tr("Reset All"));
defaultButton->setToolTip(::Core::Tr::tr("Reset all to default.")); defaultButton->setToolTip(::Core::Tr::tr("Reset all to default."));
resetButton = new QPushButton(::Core::Tr::tr("Reset"), groupBox); resetButton = new QPushButton(::Core::Tr::tr("Reset"));
resetButton->setToolTip(::Core::Tr::tr("Reset to default.")); resetButton->setToolTip(::Core::Tr::tr("Reset to default."));
resetButton->setVisible(false); resetButton->setVisible(false);
importButton = new QPushButton(::Core::Tr::tr("Import..."), groupBox); importButton = new QPushButton(::Core::Tr::tr("Import..."));
exportButton = new QPushButton(::Core::Tr::tr("Export..."), groupBox); exportButton = new QPushButton(::Core::Tr::tr("Export..."));
auto hboxLayout1 = new QHBoxLayout(); using namespace Layouting;
hboxLayout1->addWidget(defaultButton); Column {
hboxLayout1->addWidget(resetButton); Group {
hboxLayout1->addStretch(); title(::Core::Tr::tr("Command Mappings")),
hboxLayout1->addWidget(importButton); bindTo(&groupBox),
hboxLayout1->addWidget(exportButton); Column {
filterEdit,
auto hboxLayout = new QHBoxLayout(); commandList,
hboxLayout->addWidget(filterEdit); Row { defaultButton, resetButton, st, importButton, exportButton },
},
auto vboxLayout1 = new QVBoxLayout(groupBox); },
vboxLayout1->addLayout(hboxLayout); }.attachTo(parent);
vboxLayout1->addWidget(commandList);
vboxLayout1->addLayout(hboxLayout1);
auto vboxLayout = new QVBoxLayout(parent);
vboxLayout->addWidget(groupBox);
q->connect(exportButton, &QPushButton::clicked, q->connect(exportButton, &QPushButton::clicked,
q, &CommandMappings::exportAction); q, &CommandMappings::exportAction);

View File

@@ -46,6 +46,11 @@ const char C_EDITORMANAGER[] = "Core.EditorManager";
const char C_NAVIGATION_PANE[] = "Core.NavigationPane"; const char C_NAVIGATION_PANE[] = "Core.NavigationPane";
const char C_PROBLEM_PANE[] = "Core.ProblemPane"; const char C_PROBLEM_PANE[] = "Core.ProblemPane";
const char C_GENERAL_OUTPUT_PANE[] = "Core.GeneralOutputPane"; const char C_GENERAL_OUTPUT_PANE[] = "Core.GeneralOutputPane";
// Special context that leads to all "more specific" contexts to be ignored.
// If you use Context(mycontextId, C_GLOBAL_CUTOFF) for a widget that has focus,
// mycontextId will be enabled but the contexts for all parent widgets, the manually added
// "additional" contexts, and the global context will be turned off.
const char C_GLOBAL_CUTOFF[] = "Global Cutoff";
// Default editor kind // Default editor kind
const char K_DEFAULT_TEXT_EDITOR_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::Core", "Plain Text Editor"); const char K_DEFAULT_TEXT_EDITOR_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::Core", "Plain Text Editor");

View File

@@ -199,15 +199,6 @@ void IOptionsPage::setSettings(AspectContainer *settings)
m_settings = settings; m_settings = settings;
} }
void IOptionsPage::setLayouter(const std::function<void(QWidget *w)> &layouter)
{
m_widgetCreator = [layouter] {
auto widget = new IOptionsPageWidget;
layouter(widget);
return widget;
};
}
void IOptionsPage::setLayouter(const std::function<Layouting::LayoutItem ()> &layouter) void IOptionsPage::setLayouter(const std::function<Layouting::LayoutItem ()> &layouter)
{ {
m_widgetCreator = [layouter] { m_widgetCreator = [layouter] {

View File

@@ -74,7 +74,6 @@ protected:
void setCategoryIcon(const Utils::Icon &categoryIcon) { m_categoryIcon = categoryIcon; } void setCategoryIcon(const Utils::Icon &categoryIcon) { m_categoryIcon = categoryIcon; }
void setCategoryIconPath(const Utils::FilePath &categoryIconPath); void setCategoryIconPath(const Utils::FilePath &categoryIconPath);
void setSettings(Utils::AspectContainer *settings); void setSettings(Utils::AspectContainer *settings);
void setLayouter(const std::function<void(QWidget *w)> &layouter);
void setLayouter(const std::function<Layouting::LayoutItem()> &layouter); void setLayouter(const std::function<Layouting::LayoutItem()> &layouter);
// Used in FontSettingsPage. FIXME? // Used in FontSettingsPage. FIXME?

View File

@@ -42,7 +42,6 @@
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/checkablemessagebox.h> #include <utils/checkablemessagebox.h>
#include <utils/environment.h> #include <utils/environment.h>
#include <utils/executeondestruction.h>
#include <utils/filepath.h> #include <utils/filepath.h>
#include <utils/hostosinfo.h> #include <utils/hostosinfo.h>
#include <utils/infobar.h> #include <utils/infobar.h>
@@ -70,6 +69,7 @@
#include <QPushButton> #include <QPushButton>
#include <QRegularExpression> #include <QRegularExpression>
#include <QRegularExpressionMatch> #include <QRegularExpressionMatch>
#include <QScopeGuard>
#include <QSet> #include <QSet>
#include <QSettings> #include <QSettings>
#include <QSplitter> #include <QSplitter>
@@ -3325,9 +3325,7 @@ IEditor *EditorManager::openEditorWithContents(Id editorId,
EditorManager::gotoOtherSplit(); EditorManager::gotoOtherSplit();
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
Utils::ExecuteOnDestruction appRestoreCursor(&QApplication::restoreOverrideCursor); const auto cleanup = qScopeGuard(&QApplication::restoreOverrideCursor);
Q_UNUSED(appRestoreCursor)
const QString title = makeTitleUnique(titlePattern); const QString title = makeTitleUnique(titlePattern);

View File

@@ -450,7 +450,7 @@ void LocatorMatcher::start()
collectorStorage->m_collector = nullptr; collectorStorage->m_collector = nullptr;
}; };
QList<TaskItem> parallelTasks {parallelLimit(d->m_parallelLimit)}; QList<GroupItem> parallelTasks {parallelLimit(d->m_parallelLimit)};
const auto onSetup = [this, collectorStorage](const TreeStorage<LocatorStorage> &storage, const auto onSetup = [this, collectorStorage](const TreeStorage<LocatorStorage> &storage,
int index) { int index) {
@@ -470,7 +470,7 @@ void LocatorMatcher::start()
for (const LocatorMatcherTask &task : std::as_const(d->m_tasks)) { for (const LocatorMatcherTask &task : std::as_const(d->m_tasks)) {
const auto storage = task.storage; const auto storage = task.storage;
const Group group { const Group group {
optional, finishAllAndDone,
Storage(storage), Storage(storage),
onGroupSetup(onSetup(storage, index)), onGroupSetup(onSetup(storage, index)),
onGroupDone(onDone(storage)), onGroupDone(onDone(storage)),
@@ -597,7 +597,7 @@ QString ILocatorFilter::shortcutString() const
\internal \internal
Sets the refresh recipe for refreshing cached data. Sets the refresh recipe for refreshing cached data.
*/ */
void ILocatorFilter::setRefreshRecipe(const std::optional<TaskItem> &recipe) void ILocatorFilter::setRefreshRecipe(const std::optional<GroupItem> &recipe)
{ {
m_refreshRecipe = recipe; m_refreshRecipe = recipe;
} }
@@ -606,7 +606,7 @@ void ILocatorFilter::setRefreshRecipe(const std::optional<TaskItem> &recipe)
Returns the refresh recipe for refreshing cached data. By default, the locator filter has Returns the refresh recipe for refreshing cached data. By default, the locator filter has
no recipe set, so that it won't be refreshed. no recipe set, so that it won't be refreshed.
*/ */
std::optional<TaskItem> ILocatorFilter::refreshRecipe() const std::optional<GroupItem> ILocatorFilter::refreshRecipe() const
{ {
return m_refreshRecipe; return m_refreshRecipe;
} }

View File

@@ -139,7 +139,7 @@ class CORE_EXPORT LocatorMatcherTask final
public: public:
// The main task. Initial data (searchTerm) should be taken from storage.input(). // The main task. Initial data (searchTerm) should be taken from storage.input().
// Results reporting is done via the storage.reportOutput(). // Results reporting is done via the storage.reportOutput().
Tasking::TaskItem task = Tasking::Group{}; Tasking::GroupItem task = Tasking::Group{};
// When constructing the task, don't place the storage inside the task above. // When constructing the task, don't place the storage inside the task above.
Tasking::TreeStorage<LocatorStorage> storage; Tasking::TreeStorage<LocatorStorage> storage;
@@ -270,8 +270,8 @@ protected:
virtual void saveState(QJsonObject &object) const; virtual void saveState(QJsonObject &object) const;
virtual void restoreState(const QJsonObject &object); virtual void restoreState(const QJsonObject &object);
void setRefreshRecipe(const std::optional<Tasking::TaskItem> &recipe); void setRefreshRecipe(const std::optional<Tasking::GroupItem> &recipe);
std::optional<Tasking::TaskItem> refreshRecipe() const; std::optional<Tasking::GroupItem> refreshRecipe() const;
static bool isOldSetting(const QByteArray &state); static bool isOldSetting(const QByteArray &state);
@@ -289,7 +289,7 @@ private:
QString m_description; QString m_description;
QString m_defaultShortcut; QString m_defaultShortcut;
std::optional<QString> m_defaultSearchText; std::optional<QString> m_defaultSearchText;
std::optional<Tasking::TaskItem> m_refreshRecipe; std::optional<Tasking::GroupItem> m_refreshRecipe;
QKeySequence m_defaultKeySequence; QKeySequence m_defaultKeySequence;
bool m_defaultIncludedByDefault = false; bool m_defaultIncludedByDefault = false;
bool m_includedByDefault = m_defaultIncludedByDefault; bool m_includedByDefault = m_defaultIncludedByDefault;

View File

@@ -381,14 +381,14 @@ void Locator::refresh(const QList<ILocatorFilter *> &filters)
m_refreshingFilters = Utils::filteredUnique(m_refreshingFilters + filters); m_refreshingFilters = Utils::filteredUnique(m_refreshingFilters + filters);
using namespace Tasking; using namespace Tasking;
QList<TaskItem> tasks{parallel}; QList<GroupItem> tasks{parallel};
for (ILocatorFilter *filter : std::as_const(m_refreshingFilters)) { for (ILocatorFilter *filter : std::as_const(m_refreshingFilters)) {
const auto task = filter->refreshRecipe(); const auto task = filter->refreshRecipe();
if (!task.has_value()) if (!task.has_value())
continue; continue;
const Group group { const Group group {
optional, finishAllAndDone,
*task, *task,
onGroupDone([this, filter] { m_refreshingFilters.removeOne(filter); }) onGroupDone([this, filter] { m_refreshingFilters.removeOne(filter); })
}; };

View File

@@ -11,7 +11,6 @@
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/basetreeview.h> #include <utils/basetreeview.h>
#include <utils/executeondestruction.h>
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <utils/listmodel.h> #include <utils/listmodel.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
@@ -33,6 +32,7 @@
#include <QMessageBox> #include <QMessageBox>
#include <QPushButton> #include <QPushButton>
#include <QRegularExpression> #include <QRegularExpression>
#include <QScopeGuard>
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
#include <QStyledItemDelegate> #include <QStyledItemDelegate>
#include <QToolButton> #include <QToolButton>
@@ -591,7 +591,7 @@ void LoggingViewManagerWidget::saveLoggingsToFile() const
{ {
// should we just let it continue without temporarily disabling? // should we just let it continue without temporarily disabling?
const bool enabled = m_manager->isEnabled(); const bool enabled = m_manager->isEnabled();
Utils::ExecuteOnDestruction exec([this, enabled] { m_manager->setEnabled(enabled); }); const auto cleanup = qScopeGuard([this, enabled] { m_manager->setEnabled(enabled); });
if (enabled) if (enabled)
m_manager->setEnabled(false); m_manager->setEnabled(false);
const Utils::FilePath fp = Utils::FileUtils::getSaveFilePath(ICore::dialogParent(), const Utils::FilePath fp = Utils::FileUtils::getSaveFilePath(ICore::dialogParent(),

View File

@@ -17,14 +17,11 @@
#include <extensionsystem/pluginview.h> #include <extensionsystem/pluginview.h>
#include <utils/fancylineedit.h> #include <utils/fancylineedit.h>
#include <utils/layoutbuilder.h>
#include <QCheckBox>
#include <QDialog> #include <QDialog>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton> #include <QPushButton>
#include <QVBoxLayout>
using namespace Utils; using namespace Utils;
@@ -35,30 +32,27 @@ PluginDialog::PluginDialog(QWidget *parent)
: QDialog(parent), : QDialog(parent),
m_view(new ExtensionSystem::PluginView(this)) m_view(new ExtensionSystem::PluginView(this))
{ {
auto vl = new QVBoxLayout(this);
auto filterLayout = new QHBoxLayout;
vl->addLayout(filterLayout);
auto filterEdit = new Utils::FancyLineEdit(this); auto filterEdit = new Utils::FancyLineEdit(this);
filterEdit->setFocus(); filterEdit->setFocus();
filterEdit->setFiltering(true); filterEdit->setFiltering(true);
connect(filterEdit, &Utils::FancyLineEdit::filterChanged, connect(filterEdit, &Utils::FancyLineEdit::filterChanged,
m_view, &ExtensionSystem::PluginView::setFilter); m_view, &ExtensionSystem::PluginView::setFilter);
filterLayout->addWidget(filterEdit);
vl->addWidget(m_view);
m_detailsButton = new QPushButton(Tr::tr("Details"), this);
m_errorDetailsButton = new QPushButton(Tr::tr("Error Details"), this);
m_installButton = new QPushButton(Tr::tr("Install Plugin..."), this);
m_detailsButton->setEnabled(false);
m_errorDetailsButton->setEnabled(false);
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
buttonBox->addButton(m_detailsButton, QDialogButtonBox::ActionRole); m_detailsButton = buttonBox->addButton(Tr::tr("Details"), QDialogButtonBox::ActionRole);
buttonBox->addButton(m_errorDetailsButton, QDialogButtonBox::ActionRole); m_detailsButton->setEnabled(false);
buttonBox->addButton(m_installButton, QDialogButtonBox::ActionRole); m_errorDetailsButton = buttonBox->addButton(Tr::tr("Error Details"),
vl->addWidget(buttonBox); QDialogButtonBox::ActionRole);
m_errorDetailsButton->setEnabled(false);
m_installButton = buttonBox->addButton(Tr::tr("Install Plugin..."),
QDialogButtonBox::ActionRole);
using namespace Layouting;
Column {
filterEdit,
m_view,
buttonBox,
}.attachTo(this);
resize(650, 400); resize(650, 400);
setWindowTitle(Tr::tr("Installed Plugins")); setWindowTitle(Tr::tr("Installed Plugins"));
@@ -116,13 +110,16 @@ void PluginDialog::openDetails(ExtensionSystem::PluginSpec *spec)
return; return;
QDialog dialog(this); QDialog dialog(this);
dialog.setWindowTitle(Tr::tr("Plugin Details of %1").arg(spec->name())); dialog.setWindowTitle(Tr::tr("Plugin Details of %1").arg(spec->name()));
auto layout = new QVBoxLayout;
dialog.setLayout(layout);
auto details = new ExtensionSystem::PluginDetailsView(&dialog); auto details = new ExtensionSystem::PluginDetailsView(&dialog);
layout->addWidget(details);
details->update(spec); details->update(spec);
QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Close, Qt::Horizontal, &dialog); QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Close, Qt::Horizontal, &dialog);
layout->addWidget(buttons);
using namespace Layouting;
Column {
details,
buttons,
}.attachTo(&dialog);
connect(buttons, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); connect(buttons, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); connect(buttons, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
dialog.resize(400, 500); dialog.resize(400, 500);
@@ -136,13 +133,16 @@ void PluginDialog::openErrorDetails()
return; return;
QDialog dialog(this); QDialog dialog(this);
dialog.setWindowTitle(Tr::tr("Plugin Errors of %1").arg(spec->name())); dialog.setWindowTitle(Tr::tr("Plugin Errors of %1").arg(spec->name()));
auto layout = new QVBoxLayout;
dialog.setLayout(layout);
auto errors = new ExtensionSystem::PluginErrorView(&dialog); auto errors = new ExtensionSystem::PluginErrorView(&dialog);
layout->addWidget(errors);
errors->update(spec); errors->update(spec);
QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Close, Qt::Horizontal, &dialog); QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Close, Qt::Horizontal, &dialog);
layout->addWidget(buttons);
using namespace Layouting;
Column {
errors,
buttons,
}.attachTo(&dialog);
connect(buttons, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); connect(buttons, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); connect(buttons, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
dialog.resize(500, 300); dialog.resize(500, 300);

View File

@@ -15,6 +15,7 @@
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <utils/hostosinfo.h> #include <utils/hostosinfo.h>
#include <utils/infolabel.h> #include <utils/infolabel.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h> #include <utils/pathchooser.h>
#include <utils/process.h> #include <utils/process.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
@@ -33,7 +34,6 @@
#include <QPushButton> #include <QPushButton>
#include <QRadioButton> #include <QRadioButton>
#include <QTextEdit> #include <QTextEdit>
#include <QVBoxLayout>
#include <memory> #include <memory>
@@ -80,19 +80,15 @@ public:
, m_data(data) , m_data(data)
{ {
setTitle(Tr::tr("Source")); setTitle(Tr::tr("Source"));
auto vlayout = new QVBoxLayout;
setLayout(vlayout);
auto label = new QLabel( auto label = new QLabel(
"<p>" "<p>"
+ Tr::tr("Choose source location. This can be a plugin library file or a zip file.") + Tr::tr("Choose source location. This can be a plugin library file or a zip file.")
+ "</p>"); + "</p>");
label->setWordWrap(true); label->setWordWrap(true);
vlayout->addWidget(label);
auto chooser = new PathChooser; auto chooser = new PathChooser;
chooser->setExpectedKind(PathChooser::Any); chooser->setExpectedKind(PathChooser::Any);
vlayout->addWidget(chooser);
connect(chooser, &PathChooser::textChanged, this, [this, chooser] { connect(chooser, &PathChooser::textChanged, this, [this, chooser] {
m_data->sourcePath = chooser->filePath(); m_data->sourcePath = chooser->filePath();
updateWarnings(); updateWarnings();
@@ -101,7 +97,8 @@ public:
m_info = new InfoLabel; m_info = new InfoLabel;
m_info->setType(InfoLabel::Error); m_info->setType(InfoLabel::Error);
m_info->setVisible(false); m_info->setVisible(false);
vlayout->addWidget(m_info);
Layouting::Column { label, chooser, m_info }.attachTo(this);
} }
void updateWarnings() void updateWarnings()
@@ -153,8 +150,6 @@ public:
, m_data(data) , m_data(data)
{ {
setTitle(Tr::tr("Check Archive")); setTitle(Tr::tr("Check Archive"));
auto vlayout = new QVBoxLayout;
setLayout(vlayout);
m_label = new InfoLabel; m_label = new InfoLabel;
m_label->setElideMode(Qt::ElideNone); m_label->setElideMode(Qt::ElideNone);
@@ -163,13 +158,11 @@ public:
m_output = new QTextEdit; m_output = new QTextEdit;
m_output->setReadOnly(true); m_output->setReadOnly(true);
auto hlayout = new QHBoxLayout; using namespace Layouting;
hlayout->addWidget(m_label, 1); Column {
hlayout->addStretch(); Row { m_label, st, m_cancelButton },
hlayout->addWidget(m_cancelButton); m_output,
}.attachTo(this);
vlayout->addLayout(hlayout);
vlayout->addWidget(m_output);
} }
void initializePage() final void initializePage() final
@@ -322,13 +315,9 @@ public:
, m_data(data) , m_data(data)
{ {
setTitle(Tr::tr("Install Location")); setTitle(Tr::tr("Install Location"));
auto vlayout = new QVBoxLayout;
setLayout(vlayout);
auto label = new QLabel("<p>" + Tr::tr("Choose install location.") + "</p>"); auto label = new QLabel("<p>" + Tr::tr("Choose install location.") + "</p>");
label->setWordWrap(true); label->setWordWrap(true);
vlayout->addWidget(label);
vlayout->addSpacing(10);
auto localInstall = new QRadioButton(Tr::tr("User plugins")); auto localInstall = new QRadioButton(Tr::tr("User plugins"));
localInstall->setChecked(!m_data->installIntoApplication); localInstall->setChecked(!m_data->installIntoApplication);
@@ -338,10 +327,6 @@ public:
localLabel->setWordWrap(true); localLabel->setWordWrap(true);
localLabel->setAttribute(Qt::WA_MacSmallSize, true); localLabel->setAttribute(Qt::WA_MacSmallSize, true);
vlayout->addWidget(localInstall);
vlayout->addWidget(localLabel);
vlayout->addSpacing(10);
auto appInstall = new QRadioButton( auto appInstall = new QRadioButton(
Tr::tr("%1 installation").arg(Constants::IDE_DISPLAY_NAME)); Tr::tr("%1 installation").arg(Constants::IDE_DISPLAY_NAME));
appInstall->setChecked(m_data->installIntoApplication); appInstall->setChecked(m_data->installIntoApplication);
@@ -351,8 +336,11 @@ public:
.arg(Constants::IDE_DISPLAY_NAME)); .arg(Constants::IDE_DISPLAY_NAME));
appLabel->setWordWrap(true); appLabel->setWordWrap(true);
appLabel->setAttribute(Qt::WA_MacSmallSize, true); appLabel->setAttribute(Qt::WA_MacSmallSize, true);
vlayout->addWidget(appInstall);
vlayout->addWidget(appLabel); using namespace Layouting;
Column {
label, Space(10), localInstall, localLabel, Space(10), appInstall, appLabel,
}.attachTo(this);
auto group = new QButtonGroup(this); auto group = new QButtonGroup(this);
group->addButton(localInstall); group->addButton(localInstall);
@@ -375,12 +363,9 @@ public:
{ {
setTitle(Tr::tr("Summary")); setTitle(Tr::tr("Summary"));
auto vlayout = new QVBoxLayout;
setLayout(vlayout);
m_summaryLabel = new QLabel(this); m_summaryLabel = new QLabel(this);
m_summaryLabel->setWordWrap(true); m_summaryLabel->setWordWrap(true);
vlayout->addWidget(m_summaryLabel); Layouting::Column { m_summaryLabel }.attachTo(this);
} }
void initializePage() final void initializePage() final

View File

@@ -51,8 +51,7 @@ const char showCrashButtonKey[] = "ShowCrashButton";
// TODO: move to somewhere in Utils // TODO: move to somewhere in Utils
static QString formatSize(qint64 size) static QString formatSize(qint64 size)
{ {
QStringList units {Tr::tr("Bytes"), Tr::tr("KB"), Tr::tr("MB"), QStringList units{Tr::tr("Bytes"), Tr::tr("KiB"), Tr::tr("MiB"), Tr::tr("GiB"), Tr::tr("TiB")};
Tr::tr("GB"), Tr::tr("TB")};
double outputSize = size; double outputSize = size;
int i; int i;
for (i = 0; i < units.size() - 1; ++i) { for (i = 0; i < units.size() - 1; ++i) {

View File

@@ -108,11 +108,11 @@ CppcheckOptions::CppcheckOptions()
readSettings(); readSettings();
} }
std::function<void(QWidget *widget)> CppcheckOptions::layouter() std::function<Layouting::LayoutItem()> CppcheckOptions::layouter()
{ {
return [this](QWidget *widget) { return [this] {
using namespace Layouting; using namespace Layouting;
Form { return Form {
binary, br, binary, br,
Tr::tr("Checks:"), Flow { Tr::tr("Checks:"), Flow {
warning, warning,
@@ -132,7 +132,7 @@ std::function<void(QWidget *widget)> CppcheckOptions::layouter()
addIncludePaths, addIncludePaths,
guessArguments guessArguments
} }
}.attachTo(widget); };
}; };
} }

View File

@@ -12,7 +12,7 @@ class CppcheckOptions final : public Core::PagedSettings
public: public:
CppcheckOptions(); CppcheckOptions();
std::function<void(QWidget *widget)> layouter(); std::function<Layouting::LayoutItem()> layouter();
Utils::FilePathAspect binary{this}; Utils::FilePathAspect binary{this};
Utils::BoolAspect warning{this}; Utils::BoolAspect warning{this};

View File

@@ -25,6 +25,7 @@
#include <debugger/analyzer/analyzerconstants.h> #include <debugger/analyzer/analyzerconstants.h>
#include <debugger/debuggermainwindow.h> #include <debugger/debuggermainwindow.h>
#include <utils/layoutbuilder.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/utilsicons.h> #include <utils/utilsicons.h>
@@ -111,8 +112,7 @@ void CppcheckPluginPrivate::startManualRun()
manualRunTool.updateOptions(); manualRunTool.updateOptions();
auto optionsWidget = new QWidget; auto optionsWidget = options.layouter()().emerge();
options.layouter()(optionsWidget);
ManualRunDialog dialog(optionsWidget, project); ManualRunDialog dialog(optionsWidget, project);
if (dialog.exec() == ManualRunDialog::Rejected) if (dialog.exec() == ManualRunDialog::Rejected)

View File

@@ -64,7 +64,7 @@ void CppProjectUpdater::update(const ProjectUpdateInfo &projectUpdateInfo,
if (async.isResultAvailable()) if (async.isResultAvailable())
storage->projectInfo = async.result(); storage->projectInfo = async.result();
}; };
QList<TaskItem> tasks{parallel}; QList<GroupItem> tasks{parallel};
tasks.append(AsyncTask<ProjectInfo::ConstPtr>(setupInfoGenerator, onInfoGeneratorDone)); tasks.append(AsyncTask<ProjectInfo::ConstPtr>(setupInfoGenerator, onInfoGeneratorDone));
for (QPointer<ExtraCompiler> compiler : compilers) { for (QPointer<ExtraCompiler> compiler : compilers) {
if (compiler && compiler->isDirty()) if (compiler && compiler->isDirty())

View File

@@ -414,6 +414,7 @@ F2TestCase::F2TestCase(CppEditorAction action,
} else { } else {
currentTextEditor->convertPosition(targetTestFile->m_targetCursorPosition, currentTextEditor->convertPosition(targetTestFile->m_targetCursorPosition,
&expectedLine, &expectedColumn); &expectedLine, &expectedColumn);
++expectedColumn;
if (useClangd && (tag == "classDestructor" || tag == "fromDestructorDefinitionSymbol" if (useClangd && (tag == "classDestructor" || tag == "fromDestructorDefinitionSymbol"
|| tag == "fromDestructorBody")) { || tag == "fromDestructorBody")) {
--expectedColumn; // clangd goes before the ~, built-in code model after --expectedColumn; // clangd goes before the ~, built-in code model after

View File

@@ -166,7 +166,7 @@ void ResourcePreviewHoverHandler::operateTooltip(TextEditorWidget *editorWidget,
{ {
const QString tt = makeTooltip(); const QString tt = makeTooltip();
if (!tt.isEmpty()) if (!tt.isEmpty())
Utils::ToolTip::show(point, tt, editorWidget); Utils::ToolTip::show(point, tt, Qt::MarkdownText, editorWidget);
else else
Utils::ToolTip::hide(); Utils::ToolTip::hide();
} }
@@ -180,10 +180,8 @@ QString ResourcePreviewHoverHandler::makeTooltip() const
const Utils::MimeType mimeType = Utils::mimeTypeForFile(m_resPath); const Utils::MimeType mimeType = Utils::mimeTypeForFile(m_resPath);
if (mimeType.name().startsWith("image", Qt::CaseInsensitive)) if (mimeType.name().startsWith("image", Qt::CaseInsensitive))
ret += QString("<img src=\"file:///%1\" /><br/>").arg(m_resPath); ret += QString("![image](%1) \n").arg(m_resPath);
ret += QString("[%1](%2)").arg(QDir::toNativeSeparators(m_resPath), m_resPath);
ret += QString("<a href=\"file:///%1\">%2</a>")
.arg(m_resPath, QDir::toNativeSeparators(m_resPath));
return ret; return ret;
} }

View File

@@ -64,6 +64,7 @@ void SemanticHighlighter::run()
m_revision = documentRevision(); m_revision = documentRevision();
m_seenBlocks.clear(); m_seenBlocks.clear();
m_nextResultToHandle = m_resultCount = 0;
qCDebug(log) << "starting runner for document revision" << m_revision; qCDebug(log) << "starting runner for document revision" << m_revision;
m_watcher->setFuture(m_highlightingRunner()); m_watcher->setFuture(m_highlightingRunner());
} }
@@ -92,6 +93,21 @@ void SemanticHighlighter::onHighlighterResultAvailable(int from, int to)
return; return;
} }
QTC_CHECK(from == m_resultCount);
m_resultCount = to;
if (to - m_nextResultToHandle >= 100) {
handleHighlighterResults();
m_nextResultToHandle = to;
}
}
void SemanticHighlighter::handleHighlighterResults()
{
int from = m_nextResultToHandle;
const int to = m_resultCount;
if (from >= to)
return;
QElapsedTimer t; QElapsedTimer t;
t.start(); t.start();
@@ -177,6 +193,8 @@ void SemanticHighlighter::onHighlighterFinished()
{ {
QTC_ASSERT(m_watcher, return); QTC_ASSERT(m_watcher, return);
handleHighlighterResults();
QElapsedTimer t; QElapsedTimer t;
t.start(); t.start();

View File

@@ -67,6 +67,7 @@ public:
private: private:
void onHighlighterResultAvailable(int from, int to); void onHighlighterResultAvailable(int from, int to);
void handleHighlighterResults();
void onHighlighterFinished(); void onHighlighterFinished();
void connectWatcher(); void connectWatcher();
@@ -82,6 +83,8 @@ private:
QScopedPointer<QFutureWatcher<TextEditor::HighlightingResult>> m_watcher; QScopedPointer<QFutureWatcher<TextEditor::HighlightingResult>> m_watcher;
QHash<int, QTextCharFormat> m_formatMap; QHash<int, QTextCharFormat> m_formatMap;
std::set<int> m_seenBlocks; std::set<int> m_seenBlocks;
int m_nextResultToHandle = 0;
int m_resultCount = 0;
HighlightingRunner m_highlightingRunner; HighlightingRunner m_highlightingRunner;
}; };

View File

@@ -59,9 +59,9 @@ CvsSettings::CvsSettings()
diffIgnoreBlankLines.setSettingsKey("DiffIgnoreBlankLines"); diffIgnoreBlankLines.setSettingsKey("DiffIgnoreBlankLines");
setLayouter([this](QWidget *widget) { setLayouter([this] {
using namespace Layouting; using namespace Layouting;
Column { return Column {
Group { Group {
title(Tr::tr("Configuration")), title(Tr::tr("Configuration")),
Form { Form {
@@ -80,7 +80,7 @@ CvsSettings::CvsSettings()
} }
}, },
st st
}.attachTo(widget); };
}); });
} }

View File

@@ -101,11 +101,11 @@ DebuggerRunConfigurationAspect::DebuggerRunConfigurationAspect(Target *target)
addDataExtractor(this, &DebuggerRunConfigurationAspect::useMultiProcess, &Data::useMultiProcess); addDataExtractor(this, &DebuggerRunConfigurationAspect::useMultiProcess, &Data::useMultiProcess);
addDataExtractor(this, &DebuggerRunConfigurationAspect::overrideStartup, &Data::overrideStartup); addDataExtractor(this, &DebuggerRunConfigurationAspect::overrideStartup, &Data::overrideStartup);
m_cppAspect = new TriStateAspect(Tr::tr("Enabled"), Tr::tr("Disabled"), Tr::tr("Automatic")); m_cppAspect = new TriStateAspect(nullptr, Tr::tr("Enabled"), Tr::tr("Disabled"), Tr::tr("Automatic"));
m_cppAspect->setLabelText(Tr::tr("C++ debugger:")); m_cppAspect->setLabelText(Tr::tr("C++ debugger:"));
m_cppAspect->setSettingsKey("RunConfiguration.UseCppDebugger"); m_cppAspect->setSettingsKey("RunConfiguration.UseCppDebugger");
m_qmlAspect = new TriStateAspect(Tr::tr("Enabled"), Tr::tr("Disabled"), Tr::tr("Automatic")); m_qmlAspect = new TriStateAspect(nullptr, Tr::tr("Enabled"), Tr::tr("Disabled"), Tr::tr("Automatic"));
m_qmlAspect->setLabelText(Tr::tr("QML debugger:")); m_qmlAspect->setLabelText(Tr::tr("QML debugger:"));
m_qmlAspect->setSettingsKey("RunConfiguration.UseQmlDebugger"); m_qmlAspect->setSettingsKey("RunConfiguration.UseQmlDebugger");

View File

@@ -32,7 +32,7 @@ public:
setCategory(Constants::DEBUGGER_SETTINGS_CATEGORY); setCategory(Constants::DEBUGGER_SETTINGS_CATEGORY);
setSettings(&debuggerSettings()->page2); setSettings(&debuggerSettings()->page2);
setLayouter([](QWidget *w) { setLayouter([] {
using namespace Layouting; using namespace Layouting;
DebuggerSettings &s = *debuggerSettings(); DebuggerSettings &s = *debuggerSettings();
@@ -84,7 +84,7 @@ public:
Column { s.gdbPostAttachCommands }, Column { s.gdbPostAttachCommands },
}; };
Grid { general, extended, br, startup, attach }.attachTo(w); return Grid { general, extended, br, startup, attach };
}); });
} }
}; };

View File

@@ -128,7 +128,7 @@ DiffFilesController::DiffFilesController(IDocument *document)
outputList->resize(inputList.size()); outputList->resize(inputList.size());
using namespace std::placeholders; using namespace std::placeholders;
QList<TaskItem> tasks {parallel, optional}; QList<GroupItem> tasks {parallel, finishAllAndDone};
for (int i = 0; i < inputList.size(); ++i) { for (int i = 0; i < inputList.size(); ++i) {
tasks.append(AsyncTask<FileData>(std::bind(setupDiff, _1, inputList.at(i)), tasks.append(AsyncTask<FileData>(std::bind(setupDiff, _1, inputList.at(i)),
std::bind(onDiffDone, _1, i))); std::bind(onDiffDone, _1, i)));

View File

@@ -10,6 +10,13 @@
// Qt Creator. The idea is to keep this file here in a "clean" state that // Qt Creator. The idea is to keep this file here in a "clean" state that
// allows easy reuse with any QTextEdit or QPlainTextEdit derived class. // allows easy reuse with any QTextEdit or QPlainTextEdit derived class.
#ifndef FAKEVIM_STANDALONE
#include <texteditor/icodestylepreferences.h>
#include <texteditor/tabsettings.h>
#include <texteditor/texteditorsettings.h>
#include <texteditor/typingsettings.h>
#endif
#include <utils/hostosinfo.h> #include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h> #include <utils/layoutbuilder.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
@@ -18,8 +25,7 @@
using namespace Utils; using namespace Utils;
namespace FakeVim { namespace FakeVim::Internal {
namespace Internal {
#ifdef FAKEVIM_STANDALONE #ifdef FAKEVIM_STANDALONE
FvBaseAspect::FvBaseAspect() FvBaseAspect::FvBaseAspect()
@@ -62,13 +68,31 @@ QString FvBaseAspect::settingsKey() const
void setAutoApply(bool ) {} void setAutoApply(bool ) {}
#endif #endif
static FakeVimSettings *s_settings;
FakeVimSettings &settings()
{
return *s_settings;
}
FakeVimSettings::FakeVimSettings() FakeVimSettings::FakeVimSettings()
{ {
setAutoApply(false); s_settings = this;
#ifndef FAKEVIM_STANDALONE #ifndef FAKEVIM_STANDALONE
const char SETTINGS_CATEGORY[] = "D.FakeVim";
const char SETTINGS_ID[] = "A.FakeVim.General";
setId(SETTINGS_ID);
setDisplayName(Tr::tr("General"));
setCategory(SETTINGS_CATEGORY);
setDisplayCategory(Tr::tr("FakeVim"));
setCategoryIconPath(":/fakevim/images/settingscategory_fakevim.png");
setup(&useFakeVim, false, "UseFakeVim", {}, Tr::tr("Use FakeVim")); setup(&useFakeVim, false, "UseFakeVim", {}, Tr::tr("Use FakeVim"));
#endif #endif
// Specific FakeVim settings // Specific FakeVim settings
setup(&readVimRc, false, "ReadVimRc", {}, Tr::tr("Read .vimrc from location:")); setup(&readVimRc, false, "ReadVimRc", {}, Tr::tr("Read .vimrc from location:"));
setup(&vimRcPath, QString(), "VimRcPath", {}, {}); // Tr::tr("Path to .vimrc") setup(&vimRcPath, QString(), "VimRcPath", {}, {}); // Tr::tr("Path to .vimrc")
@@ -135,6 +159,121 @@ FakeVimSettings::FakeVimSettings()
"%USERPROFILE%\\_vimrc on Windows, ~/.vimrc otherwise.")); "%USERPROFILE%\\_vimrc on Windows, ~/.vimrc otherwise."));
vimRcPath.setPlaceHolderText(Tr::tr("Default: %1").arg(vimrcDefault)); vimRcPath.setPlaceHolderText(Tr::tr("Default: %1").arg(vimrcDefault));
vimRcPath.setDisplayStyle(FvStringAspect::PathChooserDisplay); vimRcPath.setDisplayStyle(FvStringAspect::PathChooserDisplay);
setLayouter([this] {
using namespace Layouting;
using namespace TextEditor;
Row bools {
Column {
autoIndent,
smartIndent,
expandTab,
smartTab,
hlSearch,
showCmd,
startOfLine,
passKeys,
blinkingCursor
},
Column {
incSearch,
useCoreSearch,
ignoreCase,
smartCase,
wrapScan,
showMarks,
passControlKey,
relativeNumber,
tildeOp
}
};
Row ints { shiftWidth, tabStop, scrollOff, st };
vimRcPath.setEnabler(&readVimRc);
Column strings {
backspace,
isKeyword,
Row {readVimRc, vimRcPath}
};
return Column {
useFakeVim,
Group {
title(Tr::tr("Vim Behavior")),
Column {
bools,
ints,
strings
}
},
Group {
title(Tr::tr("Plugin Emulation")),
Column {
emulateVimCommentary,
emulateReplaceWithRegister,
emulateArgTextObj,
emulateExchange,
emulateSurround
}
},
Row {
PushButton {
text(Tr::tr("Copy Text Editor Settings")),
onClicked([this] {
TabSettings ts = TextEditorSettings::codeStyle()->tabSettings();
TypingSettings tps = TextEditorSettings::typingSettings();
expandTab.setValue(ts.m_tabPolicy != TabSettings::TabsOnlyTabPolicy);
tabStop.setValue(ts.m_tabSize);
shiftWidth.setValue(ts.m_indentSize);
smartTab.setValue(tps.m_smartBackspaceBehavior
== TypingSettings::BackspaceFollowsPreviousIndents);
autoIndent.setValue(true);
smartIndent.setValue(tps.m_autoIndent);
incSearch.setValue(true);
}),
},
PushButton {
text(Tr::tr("Set Qt Style")),
onClicked([this] {
expandTab.setVolatileValue(true);
tabStop.setVolatileValue(4);
shiftWidth.setVolatileValue(4);
smartTab.setVolatileValue(true);
autoIndent.setVolatileValue(true);
smartIndent.setVolatileValue(true);
incSearch.setVolatileValue(true);
backspace.setVolatileValue(QString("indent,eol,start"));
passKeys.setVolatileValue(true);
}),
},
PushButton {
text(Tr::tr("Set Plain Style")),
onClicked([this] {
expandTab.setVolatileValue(false);
tabStop.setVolatileValue(8);
shiftWidth.setVolatileValue(8);
smartTab.setVolatileValue(false);
autoIndent.setVolatileValue(false);
smartIndent.setVolatileValue(false);
incSearch.setVolatileValue(false);
backspace.setVolatileValue(QString());
passKeys.setVolatileValue(false);
}),
},
st
},
st
};
});
readSettings();
#endif #endif
} }
@@ -187,11 +326,4 @@ void FakeVimSettings::setup(FvBaseAspect *aspect,
m_nameToAspect[shortName] = aspect; m_nameToAspect[shortName] = aspect;
} }
FakeVimSettings *fakeVimSettings() } // FakeVim::Internal
{
static FakeVimSettings s;
return &s;
}
} // namespace Internal
} // namespace FakeVim

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef FAKEVIM_STANDALONE #ifndef FAKEVIM_STANDALONE
# include <utils/aspects.h> # include <coreplugin/dialogs/ioptionspage.h>
#endif #endif
#include <QCoreApplication> #include <QCoreApplication>
@@ -13,8 +13,7 @@
#include <QString> #include <QString>
#include <QVariant> #include <QVariant>
namespace FakeVim { namespace FakeVim::Internal {
namespace Internal {
#ifdef FAKEVIM_STANDALONE #ifdef FAKEVIM_STANDALONE
class FvBaseAspect class FvBaseAspect
@@ -68,7 +67,7 @@ public:
#else #else
using FvAspectContainer = Utils::AspectContainer; using FvAspectContainer = Core::PagedSettings;
using FvBaseAspect = Utils::BaseAspect; using FvBaseAspect = Utils::BaseAspect;
using FvBoolAspect = Utils::BoolAspect; using FvBoolAspect = Utils::BoolAspect;
using FvIntegerAspect = Utils::IntegerAspect; using FvIntegerAspect = Utils::IntegerAspect;
@@ -145,7 +144,6 @@ private:
QHash<FvBaseAspect *, QString> m_aspectToName; QHash<FvBaseAspect *, QString> m_aspectToName;
}; };
FakeVimSettings *fakeVimSettings(); FakeVimSettings &settings();
} // namespace Internal } // FakeVim::Internal
} // namespace FakeVim

View File

@@ -408,9 +408,8 @@ static QRegularExpression vimPatternToQtPattern(const QString &needle)
*/ */
// FIXME: Option smartcase should be used only if search was typed by user. // FIXME: Option smartcase should be used only if search was typed by user.
const bool ignoreCaseOption = fakeVimSettings()->ignoreCase.value(); const bool smartCaseOption = settings().smartCase();
const bool smartCaseOption = fakeVimSettings()->smartCase.value(); const bool initialIgnoreCase = settings().ignoreCase()
const bool initialIgnoreCase = ignoreCaseOption
&& !(smartCaseOption && needle.contains(QRegularExpression("[A-Z]"))); && !(smartCaseOption && needle.contains(QRegularExpression("[A-Z]")));
bool ignorecase = initialIgnoreCase; bool ignorecase = initialIgnoreCase;
@@ -2373,7 +2372,7 @@ public:
QString surroundFunction; // Used for storing the function name provided to ys{motion}f QString surroundFunction; // Used for storing the function name provided to ys{motion}f
} g; } g;
FakeVimSettings &s = *fakeVimSettings(); FakeVimSettings &s = settings();
}; };
static void initSingleShotTimer(QTimer *timer, static void initSingleShotTimer(QTimer *timer,
@@ -2527,7 +2526,7 @@ void FakeVimHandler::Private::leaveFakeVim(bool needUpdate)
// The command might have destroyed the editor. // The command might have destroyed the editor.
if (m_textedit || m_plaintextedit) { if (m_textedit || m_plaintextedit) {
if (s.showMarks.value()) if (s.showMarks())
updateSelection(); updateSelection();
updateMiniBuffer(); updateMiniBuffer();
@@ -2576,7 +2575,7 @@ bool FakeVimHandler::Private::wantsOverride(QKeyEvent *ev)
// We are interested in overriding most Ctrl key combinations. // We are interested in overriding most Ctrl key combinations.
if (isOnlyControlModifier(mods) if (isOnlyControlModifier(mods)
&& !s.passControlKey.value() && !s.passControlKey()
&& ((key >= Key_A && key <= Key_Z && key != Key_K) && ((key >= Key_A && key <= Key_Z && key != Key_K)
|| key == Key_BracketLeft || key == Key_BracketRight)) { || key == Key_BracketLeft || key == Key_BracketRight)) {
// Ctrl-K is special as it is the Core's default notion of Locator // Ctrl-K is special as it is the Core's default notion of Locator
@@ -3059,7 +3058,7 @@ void FakeVimHandler::Private::stopIncrementalFind()
void FakeVimHandler::Private::updateFind(bool isComplete) void FakeVimHandler::Private::updateFind(bool isComplete)
{ {
if (!isComplete && !s.incSearch.value()) if (!isComplete && !s.incSearch())
return; return;
g.currentMessage.clear(); g.currentMessage.clear();
@@ -3161,7 +3160,7 @@ void FakeVimHandler::Private::pushUndoState(bool overwrite)
pos = firstPositionInLine(lineForPosition(pos)); pos = firstPositionInLine(lineForPosition(pos));
else if (isVisualBlockMode()) else if (isVisualBlockMode())
pos = blockAt(pos).position() + qMin(columnAt(anchor()), columnAt(position())); pos = blockAt(pos).position() + qMin(columnAt(anchor()), columnAt(position()));
} else if (g.movetype == MoveLineWise && s.startOfLine.value()) { } else if (g.movetype == MoveLineWise && s.startOfLine()) {
QTextCursor tc = m_cursor; QTextCursor tc = m_cursor;
if (g.submode == ShiftLeftSubMode || g.submode == ShiftRightSubMode if (g.submode == ShiftLeftSubMode || g.submode == ShiftRightSubMode
|| g.submode == IndentSubMode) { || g.submode == IndentSubMode) {
@@ -3621,7 +3620,7 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommandMovement)
return; return;
} else if (g.submode == ExchangeSubMode) { } else if (g.submode == ExchangeSubMode) {
exchangeRange(currentRange()); exchangeRange(currentRange());
} else if (g.submode == ReplaceWithRegisterSubMode && s.emulateReplaceWithRegister.value()) { } else if (g.submode == ReplaceWithRegisterSubMode && s.emulateReplaceWithRegister()) {
pushUndoState(false); pushUndoState(false);
beginEditBlock(); beginEditBlock();
replaceWithRegister(currentRange()); replaceWithRegister(currentRange());
@@ -3734,7 +3733,7 @@ void FakeVimHandler::Private::clearCurrentMode()
void FakeVimHandler::Private::updateSelection() void FakeVimHandler::Private::updateSelection()
{ {
QList<QTextEdit::ExtraSelection> selections = m_extraSelections; QList<QTextEdit::ExtraSelection> selections = m_extraSelections;
if (s.showMarks.value()) { if (s.showMarks()) {
for (auto it = m_buffer->marks.cbegin(), end = m_buffer->marks.cend(); it != end; ++it) { for (auto it = m_buffer->marks.cbegin(), end = m_buffer->marks.cend(); it != end; ++it) {
QTextEdit::ExtraSelection sel; QTextEdit::ExtraSelection sel;
sel.cursor = m_cursor; sel.cursor = m_cursor;
@@ -3753,7 +3752,7 @@ void FakeVimHandler::Private::updateSelection()
void FakeVimHandler::Private::updateHighlights() void FakeVimHandler::Private::updateHighlights()
{ {
if (s.useCoreSearch.value() || !s.hlSearch.value() || g.highlightsCleared) { if (s.useCoreSearch() || !s.hlSearch() || g.highlightsCleared) {
if (m_highlighted.isEmpty()) if (m_highlighted.isEmpty())
return; return;
m_highlighted.clear(); m_highlighted.clear();
@@ -3800,7 +3799,7 @@ void FakeVimHandler::Private::updateMiniBuffer()
} else if (!g.mapStates.isEmpty() && !g.mapStates.last().silent) { } else if (!g.mapStates.isEmpty() && !g.mapStates.last().silent) {
// Do not reset previous message when after running a mapped command. // Do not reset previous message when after running a mapped command.
return; return;
} else if (g.mode == CommandMode && !g.currentCommand.isEmpty() && s.showCmd.value()) { } else if (g.mode == CommandMode && !g.currentCommand.isEmpty() && s.showCmd()) {
msg = g.currentCommand; msg = g.currentCommand;
messageLevel = MessageShowCmd; messageLevel = MessageShowCmd;
} else if (g.mode == CommandMode && isVisualMode()) { } else if (g.mode == CommandMode && isVisualMode()) {
@@ -3907,7 +3906,7 @@ bool FakeVimHandler::Private::handleCommandSubSubMode(const Input &input)
handled = selectBlockTextObject(g.subsubdata.is('i'), '{', '}'); handled = selectBlockTextObject(g.subsubdata.is('i'), '{', '}');
else if (input.is('"') || input.is('\'') || input.is('`')) else if (input.is('"') || input.is('\'') || input.is('`'))
handled = selectQuotedStringTextObject(g.subsubdata.is('i'), input.asChar()); handled = selectQuotedStringTextObject(g.subsubdata.is('i'), input.asChar());
else if (input.is('a') && s.emulateArgTextObj.value()) else if (input.is('a') && s.emulateArgTextObj())
handled = selectArgumentTextObject(g.subsubdata.is('i')); handled = selectArgumentTextObject(g.subsubdata.is('i'));
else else
handled = false; handled = false;
@@ -4050,7 +4049,7 @@ bool FakeVimHandler::Private::handleMovement(const Input &input)
g.subsubmode = NoSubSubMode; g.subsubmode = NoSubSubMode;
} else if (input.is('/') || input.is('?')) { } else if (input.is('/') || input.is('?')) {
g.lastSearchForward = input.is('/'); g.lastSearchForward = input.is('/');
if (s.useCoreSearch.value()) { if (s.useCoreSearch()) {
// re-use the core dialog. // re-use the core dialog.
g.findPending = true; g.findPending = true;
m_findStartPosition = position(); m_findStartPosition = position();
@@ -4225,7 +4224,7 @@ bool FakeVimHandler::Private::handleMovement(const Input &input)
m_cursor = EDITOR(cursorForPosition(QPoint(0, EDITOR(height()) / 2))); m_cursor = EDITOR(cursorForPosition(QPoint(0, EDITOR(height()) / 2)));
handleStartOfLine(); handleStartOfLine();
} else if (input.is('n') || input.is('N')) { } else if (input.is('n') || input.is('N')) {
if (s.useCoreSearch.value()) { if (s.useCoreSearch()) {
bool forward = (input.is('n')) ? g.lastSearchForward : !g.lastSearchForward; bool forward = (input.is('n')) ? g.lastSearchForward : !g.lastSearchForward;
int pos = position(); int pos = position();
q->findNextRequested(!forward); q->findNextRequested(!forward);
@@ -4314,7 +4313,7 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
handled = handleNoSubMode(input); handled = handleNoSubMode(input);
} else if (g.submode == ExchangeSubMode) { } else if (g.submode == ExchangeSubMode) {
handled = handleExchangeSubMode(input); handled = handleExchangeSubMode(input);
} else if (g.submode == ChangeSubMode && input.is('x') && s.emulateExchange.value()) { } else if (g.submode == ChangeSubMode && input.is('x') && s.emulateExchange()) {
// Exchange submode is "cx", so we need to switch over from ChangeSubMode here // Exchange submode is "cx", so we need to switch over from ChangeSubMode here
g.submode = ExchangeSubMode; g.submode = ExchangeSubMode;
handled = true; handled = true;
@@ -4324,15 +4323,15 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
} else if (g.submode == AddSurroundingSubMode) { } else if (g.submode == AddSurroundingSubMode) {
handled = handleAddSurroundingSubMode(input); handled = handleAddSurroundingSubMode(input);
} else if (g.submode == ChangeSubMode && (input.is('s') || input.is('S')) } else if (g.submode == ChangeSubMode && (input.is('s') || input.is('S'))
&& s.emulateSurround.value()) { && s.emulateSurround()) {
g.submode = ChangeSurroundingSubMode; g.submode = ChangeSurroundingSubMode;
g.surroundUpperCaseS = input.is('S'); g.surroundUpperCaseS = input.is('S');
handled = true; handled = true;
} else if (g.submode == DeleteSubMode && input.is('s') && s.emulateSurround.value()) { } else if (g.submode == DeleteSubMode && input.is('s') && s.emulateSurround()) {
g.submode = DeleteSurroundingSubMode; g.submode = DeleteSurroundingSubMode;
handled = true; handled = true;
} else if (g.submode == YankSubMode && (input.is('s') || input.is('S')) } else if (g.submode == YankSubMode && (input.is('s') || input.is('S'))
&& s.emulateSurround.value()) { && s.emulateSurround()) {
g.submode = AddSurroundingSubMode; g.submode = AddSurroundingSubMode;
g.movetype = MoveInclusive; g.movetype = MoveInclusive;
g.surroundUpperCaseS = input.is('S'); g.surroundUpperCaseS = input.is('S');
@@ -4341,10 +4340,9 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
|| g.submode == DeleteSubMode || g.submode == DeleteSubMode
|| g.submode == YankSubMode) { || g.submode == YankSubMode) {
handled = handleChangeDeleteYankSubModes(input); handled = handleChangeDeleteYankSubModes(input);
} else if (g.submode == CommentSubMode && s.emulateVimCommentary.value()) { } else if (g.submode == CommentSubMode && s.emulateVimCommentary()) {
handled = handleCommentSubMode(input); handled = handleCommentSubMode(input);
} else if (g.submode == ReplaceWithRegisterSubMode } else if (g.submode == ReplaceWithRegisterSubMode && s.emulateReplaceWithRegister()) {
&& s.emulateReplaceWithRegister.value()) {
handled = handleReplaceWithRegisterSubMode(input); handled = handleReplaceWithRegisterSubMode(input);
} else if (g.submode == ReplaceSubMode) { } else if (g.submode == ReplaceSubMode) {
handled = handleReplaceSubMode(input); handled = handleReplaceSubMode(input);
@@ -4487,7 +4485,7 @@ bool FakeVimHandler::Private::handleNoSubMode(const Input &input)
setTargetColumn(); setTargetColumn();
} else if (input.isControl('a')) { } else if (input.isControl('a')) {
changeNumberTextObject(count()); changeNumberTextObject(count());
} else if (g.gflag && input.is('c') && s.emulateVimCommentary.value()) { } else if (g.gflag && input.is('c') && s.emulateVimCommentary()) {
if (isVisualMode()) { if (isVisualMode()) {
pushUndoState(); pushUndoState();
@@ -4512,7 +4510,7 @@ bool FakeVimHandler::Private::handleNoSubMode(const Input &input)
pushUndoState(); pushUndoState();
setAnchor(); setAnchor();
} }
} else if (g.gflag && input.is('r') && s.emulateReplaceWithRegister.value()) { } else if (g.gflag && input.is('r') && s.emulateReplaceWithRegister()) {
g.submode = ReplaceWithRegisterSubMode; g.submode = ReplaceWithRegisterSubMode;
if (isVisualMode()) { if (isVisualMode()) {
dotCommand = visualDotCommand() + QString::number(count()) + "gr"; dotCommand = visualDotCommand() + QString::number(count()) + "gr";
@@ -4671,7 +4669,7 @@ bool FakeVimHandler::Private::handleNoSubMode(const Input &input)
int repeat = count(); int repeat = count();
while (--repeat >= 0) while (--repeat >= 0)
redo(); redo();
} else if (input.is('S') && isVisualMode() && s.emulateSurround.value()) { } else if (input.is('S') && isVisualMode() && s.emulateSurround()) {
g.submode = AddSurroundingSubMode; g.submode = AddSurroundingSubMode;
g.subsubmode = SurroundSubSubMode; g.subsubmode = SurroundSubSubMode;
} else if (input.is('s')) { } else if (input.is('s')) {
@@ -4756,7 +4754,7 @@ bool FakeVimHandler::Private::handleNoSubMode(const Input &input)
if (isVisualMode()) { if (isVisualMode()) {
leaveVisualMode(); leaveVisualMode();
finishMovement(); finishMovement();
} else if (g.gflag || (g.submode == InvertCaseSubMode && s.tildeOp.value())) { } else if (g.gflag || (g.submode == InvertCaseSubMode && s.tildeOp())) {
if (atEndOfLine()) if (atEndOfLine())
moveLeft(); moveLeft();
setAnchor(); setAnchor();
@@ -5404,8 +5402,8 @@ void FakeVimHandler::Private::handleInsertMode(const Input &input)
if (!handleInsertInEditor(Input(Qt::Key_Backspace, Qt::NoModifier))) { if (!handleInsertInEditor(Input(Qt::Key_Backspace, Qt::NoModifier))) {
joinPreviousEditBlock(); joinPreviousEditBlock();
if (!m_buffer->lastInsertion.isEmpty() if (!m_buffer->lastInsertion.isEmpty()
|| s.backspace.value().contains("start") || s.backspace().contains("start")
|| s.backspace.value().contains("2")) { || s.backspace().contains("2")) {
const int line = cursorLine() + 1; const int line = cursorLine() + 1;
const Column col = cursorColumn(); const Column col = cursorColumn();
QString data = lineContents(line); QString data = lineContents(line);
@@ -5438,7 +5436,7 @@ void FakeVimHandler::Private::handleInsertMode(const Input &input)
} else if (input.isKey(Key_Tab)) { } else if (input.isKey(Key_Tab)) {
if (q->tabPressedInInsertMode()) { if (q->tabPressedInInsertMode()) {
m_buffer->insertState.insertingSpaces = true; m_buffer->insertState.insertingSpaces = true;
if (s.expandTab.value()) { if (s.expandTab()) {
const int ts = s.tabStop(); const int ts = s.tabStop();
const int col = logicalCursorColumn(); const int col = logicalCursorColumn();
QString str = QString(ts - col % ts, ' '); QString str = QString(ts - col % ts, ' ');
@@ -5494,7 +5492,7 @@ void FakeVimHandler::Private::insertInInsertMode(const QString &text)
{ {
joinPreviousEditBlock(); joinPreviousEditBlock();
insertText(text); insertText(text);
if (s.smartIndent.value() && isElectricCharacter(text.at(0))) { if (s.smartIndent() && isElectricCharacter(text.at(0))) {
const QString leftText = block().text() const QString leftText = block().text()
.left(position() - 1 - block().position()); .left(position() - 1 - block().position());
if (leftText.simplified().isEmpty()) { if (leftText.simplified().isEmpty()) {
@@ -6265,7 +6263,7 @@ bool FakeVimHandler::Private::handleExMoveCommand(const ExCommand &cmd)
if (!insertAtEnd) if (!insertAtEnd)
moveUp(1); moveUp(1);
if (s.startOfLine.value()) if (s.startOfLine())
moveToFirstNonBlankOnLine(); moveToFirstNonBlankOnLine();
if (lastAnchor.line >= startLine && lastAnchor.line <= endLine) if (lastAnchor.line >= startLine && lastAnchor.line <= endLine)
@@ -6794,7 +6792,7 @@ QTextCursor FakeVimHandler::Private::search(const SearchData &sd, int startPos,
} }
if (tc.isNull()) { if (tc.isNull()) {
if (s.wrapScan.value()) { if (s.wrapScan()) {
tc = QTextCursor(document()); tc = QTextCursor(document());
tc.movePosition(sd.forward ? StartOfDocument : EndOfDocument); tc.movePosition(sd.forward ? StartOfDocument : EndOfDocument);
if (sd.forward) if (sd.forward)
@@ -6954,7 +6952,7 @@ void FakeVimHandler::Private::shiftRegionRight(int repeat)
std::swap(beginLine, endLine); std::swap(beginLine, endLine);
targetPos = position(); targetPos = position();
} }
if (s.startOfLine.value()) if (s.startOfLine())
targetPos = firstPositionInLine(beginLine); targetPos = firstPositionInLine(beginLine);
const int sw = s.shiftWidth(); const int sw = s.shiftWidth();
@@ -7106,7 +7104,7 @@ void FakeVimHandler::Private::setupCharClass()
const QChar c = QLatin1Char(i); const QChar c = QLatin1Char(i);
m_charClass[i] = c.isSpace() ? 0 : 1; m_charClass[i] = c.isSpace() ? 0 : 1;
} }
const QString conf = s.isKeyword.value(); const QString conf = s.isKeyword();
for (const QString &part : conf.split(',')) { for (const QString &part : conf.split(',')) {
if (part.contains('-')) { if (part.contains('-')) {
const int from = someInt(part.section('-', 0, 0)); const int from = someInt(part.section('-', 0, 0));
@@ -7594,7 +7592,7 @@ void FakeVimHandler::Private::transformText(const Range &range, const Transforma
void FakeVimHandler::Private::insertText(QTextCursor &tc, const QString &text) void FakeVimHandler::Private::insertText(QTextCursor &tc, const QString &text)
{ {
if (s.passKeys.value()) { if (s.passKeys()) {
if (tc.hasSelection() && text.isEmpty()) { if (tc.hasSelection() && text.isEmpty()) {
QKeyEvent event(QEvent::KeyPress, Qt::Key_Delete, Qt::NoModifier, QString()); QKeyEvent event(QEvent::KeyPress, Qt::Key_Delete, Qt::NoModifier, QString());
passEventToEditor(event, tc); passEventToEditor(event, tc);
@@ -7937,7 +7935,7 @@ void FakeVimHandler::Private::joinLines(int count, bool preserveSpace)
moveRight(); moveRight();
// If the line we started from is a comment, remove the comment string from the next line // If the line we started from is a comment, remove the comment string from the next line
if (startingLineIsComment && s.formatOptions.value().contains('f')) { if (startingLineIsComment && s.formatOptions().contains('f')) {
if (characterAtCursor() == '/' && characterAt(position() + 1) == '/') if (characterAtCursor() == '/' && characterAt(position() + 1) == '/')
moveRight(2); moveRight(2);
else if (characterAtCursor() == '*' || characterAtCursor() == '#') else if (characterAtCursor() == '*' || characterAtCursor() == '#')
@@ -7955,7 +7953,7 @@ void FakeVimHandler::Private::joinLines(int count, bool preserveSpace)
void FakeVimHandler::Private::insertNewLine() void FakeVimHandler::Private::insertNewLine()
{ {
if (m_buffer->editBlockLevel <= 1 && s.passKeys.value()) { if (m_buffer->editBlockLevel <= 1 && s.passKeys()) {
QKeyEvent event(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier, "\n"); QKeyEvent event(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier, "\n");
if (passEventToEditor(event, m_cursor)) if (passEventToEditor(event, m_cursor))
return; return;
@@ -7967,7 +7965,7 @@ void FakeVimHandler::Private::insertNewLine()
bool FakeVimHandler::Private::handleInsertInEditor(const Input &input) bool FakeVimHandler::Private::handleInsertInEditor(const Input &input)
{ {
if (m_buffer->editBlockLevel > 0 || !s.passKeys.value()) if (m_buffer->editBlockLevel > 0 || !s.passKeys())
return false; return false;
joinPreviousEditBlock(); joinPreviousEditBlock();
@@ -8712,10 +8710,10 @@ QString FakeVimHandler::Private::tabExpand(int n) const
void FakeVimHandler::Private::insertAutomaticIndentation(bool goingDown, bool forceAutoIndent) void FakeVimHandler::Private::insertAutomaticIndentation(bool goingDown, bool forceAutoIndent)
{ {
if (!forceAutoIndent && !s.autoIndent.value() && !s.smartIndent.value()) if (!forceAutoIndent && !s.autoIndent() && !s.smartIndent())
return; return;
if (s.smartIndent.value()) { if (s.smartIndent()) {
QTextBlock bl = block(); QTextBlock bl = block();
Range range(bl.position(), bl.position()); Range range(bl.position(), bl.position());
indentText(range, '\n'); indentText(range, '\n');
@@ -8734,7 +8732,7 @@ void FakeVimHandler::Private::insertAutomaticIndentation(bool goingDown, bool fo
void FakeVimHandler::Private::handleStartOfLine() void FakeVimHandler::Private::handleStartOfLine()
{ {
if (s.startOfLine.value()) if (s.startOfLine())
moveToFirstNonBlankOnLine(); moveToFirstNonBlankOnLine();
} }
@@ -9331,7 +9329,7 @@ void FakeVimHandler::Private::getRegisterType(int *reg, bool *isClipboard, bool
*reg = c.toLower().unicode(); *reg = c.toLower().unicode();
if (c == '"') { if (c == '"') {
QStringList list = s.clipboard.value().split(','); QStringList list = s.clipboard().split(',');
clipboard = list.contains("unnamedplus"); clipboard = list.contains("unnamedplus");
selection = list.contains("unnamed"); selection = list.contains("unnamed");
} else if (c == '+') { } else if (c == '+') {
@@ -9385,7 +9383,7 @@ void FakeVimHandler::updateGlobalMarksFilenames(const QString &oldFileName, cons
bool FakeVimHandler::eventFilter(QObject *ob, QEvent *ev) bool FakeVimHandler::eventFilter(QObject *ob, QEvent *ev)
{ {
#ifndef FAKEVIM_STANDALONE #ifndef FAKEVIM_STANDALONE
if (!fakeVimSettings()->useFakeVim.value()) if (!settings().useFakeVim())
return QObject::eventFilter(ob, ev); return QObject::eventFilter(ob, ev);
#endif #endif

View File

@@ -340,134 +340,6 @@ private:
using ExCommandMap = QMap<QString, QRegularExpression>; using ExCommandMap = QMap<QString, QRegularExpression>;
using UserCommandMap = QMap<int, QString>; using UserCommandMap = QMap<int, QString>;
static void layoutPage(QWidget *widget)
{
using namespace Layouting;
FakeVimSettings &s = *fakeVimSettings();
Row bools {
Column {
s.autoIndent,
s.smartIndent,
s.expandTab,
s.smartTab,
s.hlSearch,
s.showCmd,
s.startOfLine,
s.passKeys,
s.blinkingCursor
},
Column {
s.incSearch,
s.useCoreSearch,
s.ignoreCase,
s.smartCase,
s.wrapScan,
s.showMarks,
s.passControlKey,
s.relativeNumber,
s.tildeOp
}
};
Row ints { s.shiftWidth, s.tabStop, s.scrollOff, st };
Column strings {
s.backspace,
s.isKeyword,
Row {s.readVimRc, s.vimRcPath}
};
Column {
s.useFakeVim,
Group {
title(Tr::tr("Vim Behavior")),
Column {
bools,
ints,
strings
}
},
Group {
title(Tr::tr("Plugin Emulation")),
Column {
s.emulateVimCommentary,
s.emulateReplaceWithRegister,
s.emulateArgTextObj,
s.emulateExchange,
s.emulateSurround
}
},
Row {
PushButton {
text(Tr::tr("Copy Text Editor Settings")),
onClicked([&s] {
TabSettings ts = TextEditorSettings::codeStyle()->tabSettings();
TypingSettings tps = TextEditorSettings::typingSettings();
s.expandTab.setValue(ts.m_tabPolicy != TabSettings::TabsOnlyTabPolicy);
s.tabStop.setValue(ts.m_tabSize);
s.shiftWidth.setValue(ts.m_indentSize);
s.smartTab.setValue(tps.m_smartBackspaceBehavior
== TypingSettings::BackspaceFollowsPreviousIndents);
s.autoIndent.setValue(true);
s.smartIndent.setValue(tps.m_autoIndent);
s.incSearch.setValue(true);
}),
},
PushButton {
text(Tr::tr("Set Qt Style")),
onClicked([&s] {
s.expandTab.setVolatileValue(true);
s.tabStop.setVolatileValue(4);
s.shiftWidth.setVolatileValue(4);
s.smartTab.setVolatileValue(true);
s.autoIndent.setVolatileValue(true);
s.smartIndent.setVolatileValue(true);
s.incSearch.setVolatileValue(true);
s.backspace.setVolatileValue(QString("indent,eol,start"));
s.passKeys.setVolatileValue(true);
}),
},
PushButton {
text(Tr::tr("Set Plain Style")),
onClicked([&s] {
s.expandTab.setVolatileValue(false);
s.tabStop.setVolatileValue(8);
s.shiftWidth.setVolatileValue(8);
s.smartTab.setVolatileValue(false);
s.autoIndent.setVolatileValue(false);
s.smartIndent.setVolatileValue(false);
s.incSearch.setVolatileValue(false);
s.backspace.setVolatileValue(QString());
s.passKeys.setVolatileValue(false);
}),
},
st
},
st
}.attachTo(widget);
s.vimRcPath.setEnabler(&s.readVimRc);
}
class FakeVimOptionPage : public IOptionsPage
{
public:
FakeVimOptionPage()
{
setId(SETTINGS_ID);
setDisplayName(Tr::tr("General"));
setCategory(SETTINGS_CATEGORY);
setDisplayCategory(Tr::tr("FakeVim"));
setCategoryIconPath(":/fakevim/images/settingscategory_fakevim.png");
setLayouter(&layoutPage);
setSettings(fakeVimSettings());
}
};
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
// //
@@ -507,7 +379,6 @@ public:
int cursorPos, int anchorPos, int messageLevel); int cursorPos, int anchorPos, int messageLevel);
void handleExCommand(FakeVimHandler *handler, bool *handled, const ExCommand &cmd); void handleExCommand(FakeVimHandler *handler, bool *handled, const ExCommand &cmd);
void writeSettings();
void readSettings(); void readSettings();
void handleDelayedQuitAll(bool forced); void handleDelayedQuitAll(bool forced);
@@ -1107,7 +978,7 @@ IAssistProcessor *FakeVimCompletionAssistProvider::createProcessor(const AssistI
class FakeVimPluginRunData class FakeVimPluginRunData
{ {
public: public:
FakeVimOptionPage optionsPage; FakeVimSettings settings;
FakeVimExCommandsPage exCommandsPage; FakeVimExCommandsPage exCommandsPage;
FakeVimUserCommandsPage userCommandsPage; FakeVimUserCommandsPage userCommandsPage;
@@ -1179,7 +1050,7 @@ void FakeVimPluginPrivate::initialize()
Command *cmd = nullptr; Command *cmd = nullptr;
cmd = ActionManager::registerAction(fakeVimSettings()->useFakeVim.action(), cmd = ActionManager::registerAction(settings().useFakeVim.action(),
INSTALL_HANDLER, Context(Core::Constants::C_GLOBAL), true); INSTALL_HANDLER, Context(Core::Constants::C_GLOBAL), true);
cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+Shift+Y,Meta+Shift+Y") cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+Shift+Y,Meta+Shift+Y")
: Tr::tr("Alt+Y,Alt+Y"))); : Tr::tr("Alt+Y,Alt+Y")));
@@ -1217,7 +1088,7 @@ void FakeVimPluginPrivate::initialize()
connect(DocumentManager::instance(), &DocumentManager::documentRenamed, connect(DocumentManager::instance(), &DocumentManager::documentRenamed,
this, &FakeVimPluginPrivate::documentRenamed); this, &FakeVimPluginPrivate::documentRenamed);
FakeVimSettings &s = *fakeVimSettings(); FakeVimSettings &s = settings();
connect(&s.useFakeVim, &FvBoolAspect::valueChanged, connect(&s.useFakeVim, &FvBoolAspect::valueChanged,
this, &FakeVimPluginPrivate::setUseFakeVim); this, &FakeVimPluginPrivate::setUseFakeVim);
connect(&s.readVimRc, &FvBaseAspect::changed, connect(&s.readVimRc, &FvBaseAspect::changed,
@@ -1235,7 +1106,7 @@ void FakeVimPluginPrivate::initialize()
connect(this, &FakeVimPluginPrivate::delayedQuitAllRequested, connect(this, &FakeVimPluginPrivate::delayedQuitAllRequested,
this, &FakeVimPluginPrivate::handleDelayedQuitAll, Qt::QueuedConnection); this, &FakeVimPluginPrivate::handleDelayedQuitAll, Qt::QueuedConnection);
setCursorBlinking(s.blinkingCursor.value()); setCursorBlinking(s.blinkingCursor());
} }
void FakeVimPluginPrivate::userActionTriggered(int key) void FakeVimPluginPrivate::userActionTriggered(int key)
@@ -1244,7 +1115,7 @@ void FakeVimPluginPrivate::userActionTriggered(int key)
FakeVimHandler *handler = m_editorToHandler[editor].handler; FakeVimHandler *handler = m_editorToHandler[editor].handler;
if (handler) { if (handler) {
// If disabled, enable FakeVim mode just for single user command. // If disabled, enable FakeVim mode just for single user command.
bool enableFakeVim = !fakeVimSettings()->useFakeVim.value(); bool enableFakeVim = !settings().useFakeVim();
if (enableFakeVim) if (enableFakeVim)
setUseFakeVimInternal(true); setUseFakeVimInternal(true);
@@ -1270,26 +1141,18 @@ void FakeVimPluginPrivate::createRelativeNumberWidget(IEditor *editor)
{ {
if (auto textEditor = TextEditorWidget::fromEditor(editor)) { if (auto textEditor = TextEditorWidget::fromEditor(editor)) {
auto relativeNumbers = new RelativeNumbersColumn(textEditor); auto relativeNumbers = new RelativeNumbersColumn(textEditor);
connect(&fakeVimSettings()->relativeNumber, &FvBaseAspect::changed, connect(&settings().relativeNumber, &FvBaseAspect::changed,
relativeNumbers, &QObject::deleteLater); relativeNumbers, &QObject::deleteLater);
connect(&fakeVimSettings()->useFakeVim, &FvBaseAspect::changed, connect(&settings().useFakeVim, &FvBaseAspect::changed,
relativeNumbers, &QObject::deleteLater); relativeNumbers, &QObject::deleteLater);
relativeNumbers->show(); relativeNumbers->show();
} }
} }
void FakeVimPluginPrivate::writeSettings()
{
QSettings *settings = ICore::settings();
fakeVimSettings()->writeSettings(settings);
}
void FakeVimPluginPrivate::readSettings() void FakeVimPluginPrivate::readSettings()
{ {
QSettings *settings = ICore::settings(); QSettings *settings = ICore::settings();
fakeVimSettings()->readSettings(settings);
m_exCommandMap = m_defaultExCommandMap; m_exCommandMap = m_defaultExCommandMap;
int size = settings->beginReadArray(exCommandMapGroup); int size = settings->beginReadArray(exCommandMapGroup);
for (int i = 0; i < size; ++i) { for (int i = 0; i < size; ++i) {
@@ -1318,9 +1181,9 @@ void FakeVimPluginPrivate::maybeReadVimRc()
//qDebug() << theFakeVimSetting(ConfigReadVimRc) //qDebug() << theFakeVimSetting(ConfigReadVimRc)
// << theFakeVimSetting(ConfigReadVimRc)->value(); // << theFakeVimSetting(ConfigReadVimRc)->value();
//qDebug() << theFakeVimSetting(ConfigShiftWidth)->value(); //qDebug() << theFakeVimSetting(ConfigShiftWidth)->value();
if (!fakeVimSettings()->readVimRc.value()) if (!settings().readVimRc())
return; return;
QString fileName = fakeVimSettings()->vimRcPath.value(); QString fileName = settings().vimRcPath();
if (fileName.isEmpty()) { if (fileName.isEmpty()) {
fileName = QStandardPaths::writableLocation(QStandardPaths::HomeLocation) fileName = QStandardPaths::writableLocation(QStandardPaths::HomeLocation)
+ QLatin1String(HostOsInfo::isWindowsHost() ? "/_vimrc" : "/.vimrc"); + QLatin1String(HostOsInfo::isWindowsHost() ? "/_vimrc" : "/.vimrc");
@@ -1330,7 +1193,6 @@ void FakeVimPluginPrivate::maybeReadVimRc()
QPlainTextEdit editor; QPlainTextEdit editor;
FakeVimHandler handler(&editor); FakeVimHandler handler(&editor);
handler.handleCommand("source " + fileName); handler.handleCommand("source " + fileName);
//writeSettings();
//qDebug() << theFakeVimSetting(ConfigShiftWidth)->value(); //qDebug() << theFakeVimSetting(ConfigShiftWidth)->value();
} }
@@ -1659,9 +1521,9 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
return; return;
TabSettings tabSettings; TabSettings tabSettings;
tabSettings.m_indentSize = fakeVimSettings()->shiftWidth(); tabSettings.m_indentSize = settings().shiftWidth();
tabSettings.m_tabSize = fakeVimSettings()->tabStop(); tabSettings.m_tabSize = settings().tabStop();
tabSettings.m_tabPolicy = fakeVimSettings()->expandTab() tabSettings.m_tabPolicy = settings().expandTab()
? TabSettings::SpacesOnlyTabPolicy : TabSettings::TabsOnlyTabPolicy; ? TabSettings::SpacesOnlyTabPolicy : TabSettings::TabsOnlyTabPolicy;
tabSettings.m_continuationAlignBehavior = tabSettings.m_continuationAlignBehavior =
tew->textDocument()->tabSettings().m_continuationAlignBehavior; tew->textDocument()->tabSettings().m_continuationAlignBehavior;
@@ -1885,19 +1747,15 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
*output = proc.cleanedStdOut(); *output = proc.cleanedStdOut();
}); });
connect(ICore::instance(), &ICore::saveSettingsRequested,
this, &FakeVimPluginPrivate::writeSettings);
handler->setCurrentFileName(editor->document()->filePath().toString()); handler->setCurrentFileName(editor->document()->filePath().toString());
handler->installEventFilter(); handler->installEventFilter();
// pop up the bar // pop up the bar
if (fakeVimSettings()->useFakeVim.value()) { if (settings().useFakeVim()) {
resetCommandBuffer(); resetCommandBuffer();
handler->setupWidget(); handler->setupWidget();
if (fakeVimSettings()->relativeNumber.value()) if (settings().relativeNumber())
createRelativeNumberWidget(editor); createRelativeNumberWidget(editor);
} }
} }
@@ -1939,8 +1797,8 @@ void FakeVimPluginPrivate::setUseFakeVim(bool on)
//qDebug() << "SET USE FAKEVIM" << on; //qDebug() << "SET USE FAKEVIM" << on;
Find::setUseFakeVim(on); Find::setUseFakeVim(on);
setUseFakeVimInternal(on); setUseFakeVimInternal(on);
setShowRelativeLineNumbers(fakeVimSettings()->relativeNumber.value()); setShowRelativeLineNumbers(settings().relativeNumber());
setCursorBlinking(fakeVimSettings()->blinkingCursor.value()); setCursorBlinking(settings().blinkingCursor());
} }
void FakeVimPluginPrivate::setUseFakeVimInternal(bool on) void FakeVimPluginPrivate::setUseFakeVimInternal(bool on)
@@ -1968,7 +1826,7 @@ void FakeVimPluginPrivate::setUseFakeVimInternal(bool on)
void FakeVimPluginPrivate::setShowRelativeLineNumbers(bool on) void FakeVimPluginPrivate::setShowRelativeLineNumbers(bool on)
{ {
if (on && fakeVimSettings()->useFakeVim.value()) { if (on && settings().useFakeVim()) {
for (auto it = m_editorToHandler.constBegin(); it != m_editorToHandler.constEnd(); ++it) for (auto it = m_editorToHandler.constBegin(); it != m_editorToHandler.constEnd(); ++it)
createRelativeNumberWidget(it.key()); createRelativeNumberWidget(it.key());
} }
@@ -1979,7 +1837,7 @@ void FakeVimPluginPrivate::setCursorBlinking(bool on)
if (m_savedCursorFlashTime == 0) if (m_savedCursorFlashTime == 0)
m_savedCursorFlashTime = QGuiApplication::styleHints()->cursorFlashTime(); m_savedCursorFlashTime = QGuiApplication::styleHints()->cursorFlashTime();
const bool blink = on || !fakeVimSettings()->useFakeVim.value(); const bool blink = on || !settings().useFakeVim();
QGuiApplication::styleHints()->setCursorFlashTime(blink ? m_savedCursorFlashTime : 0); QGuiApplication::styleHints()->setCursorFlashTime(blink ? m_savedCursorFlashTime : 0);
} }
@@ -2123,7 +1981,7 @@ void FakeVimPluginPrivate::handleDelayedQuitAll(bool forced)
void FakeVimPluginPrivate::quitFakeVim() void FakeVimPluginPrivate::quitFakeVim()
{ {
fakeVimSettings()->useFakeVim.setValue(false); settings().useFakeVim.setValue(false);
} }
void FakeVimPluginPrivate::resetCommandBuffer() void FakeVimPluginPrivate::resetCommandBuffer()

View File

@@ -93,8 +93,8 @@ FossilCommitWidget::FossilCommitWidget() : m_commitPanel(new QWidget)
m_invalidBranchLabel->setType(InfoLabel::Error); m_invalidBranchLabel->setType(InfoLabel::Error);
m_isPrivateCheckBox = new QCheckBox(Tr::tr("Private")); m_isPrivateCheckBox = new QCheckBox(Tr::tr("Private"));
m_isPrivateCheckBox->setToolTip(Tr::tr("Create a private check-in that is never synced.\n" m_isPrivateCheckBox->setToolTip("<html>" + Tr::tr("Create a private check-in that is never synced. "
"Children of private check-ins are automatically private.\n" "Children of private check-ins are automatically private. "
"Private check-ins are not pushed to the remote repository by default.")); "Private check-ins are not pushed to the remote repository by default."));
m_tagsLineEdit = new QLineEdit; m_tagsLineEdit = new QLineEdit;

View File

@@ -88,9 +88,9 @@ FossilSettings::FossilSettings()
logCount.setToolTip(Tr::tr("The number of recent commit log entries to show. " logCount.setToolTip(Tr::tr("The number of recent commit log entries to show. "
"Choose 0 to see all entries.")); "Choose 0 to see all entries."));
setLayouter([this](QWidget *widget) { setLayouter([this] {
using namespace Layouting; using namespace Layouting;
Column { return Column {
Group { Group {
title(Tr::tr("Configuration")), title(Tr::tr("Configuration")),
Row { binaryPath } Row { binaryPath }
@@ -117,7 +117,7 @@ FossilSettings::FossilSettings()
}, },
}, },
st st
}.attachTo(widget); };
}); });
} }

View File

@@ -34,7 +34,7 @@ PullOrPushDialog::PullOrPushDialog(Mode mode, QWidget *parent)
m_localPathChooser->setPromptDialogFilter(Tr::tr(Constants::FOSSIL_FILE_FILTER)); m_localPathChooser->setPromptDialogFilter(Tr::tr(Constants::FOSSIL_FILE_FILTER));
m_urlButton = new QRadioButton(Tr::tr("Specify URL:")); m_urlButton = new QRadioButton(Tr::tr("Specify URL:"));
m_urlButton->setToolTip(Tr::tr("For example: https://[user[:pass]@]host[:port]/[path]")); m_urlButton->setToolTip(Tr::tr("For example: \"https://[user[:pass]@]host[:port]/[path]\"."));
m_urlLineEdit = new QLineEdit; m_urlLineEdit = new QLineEdit;
m_urlLineEdit->setEnabled(false); m_urlLineEdit->setEnabled(false);

View File

@@ -86,7 +86,7 @@
{ {
"name": "Repo", "name": "Repo",
"trDisplayName": "Remote repository:", "trDisplayName": "Remote repository:",
"trToolTip": "For example: https://[user[:pass]@]host[:port]/[path]", "trToolTip": "For example: \"https://[user[:pass]@]host[:port]/[path]\".",
"type": "LineEdit", "type": "LineEdit",
"enabled": "%{isCloneRepo}", "enabled": "%{isCloneRepo}",
"mandatory": false "mandatory": false

View File

@@ -43,8 +43,8 @@ public:
curlChooser->setCommandVersionArguments({"-V"}); curlChooser->setCommandVersionArguments({"-V"});
auto portSpinBox = new QSpinBox(this); auto portSpinBox = new QSpinBox(this);
portSpinBox->setValue(p->server.port);
portSpinBox->setRange(1, 65535); portSpinBox->setRange(1, 65535);
portSpinBox->setValue(p->server.port);
auto httpsCheckBox = new QCheckBox(Git::Tr::tr("HTTPS")); auto httpsCheckBox = new QCheckBox(Git::Tr::tr("HTTPS"));
httpsCheckBox->setChecked(p->https); httpsCheckBox->setChecked(p->https);

View File

@@ -441,7 +441,7 @@ ShowController::ShowController(IDocument *document, const QString &id)
}; };
using namespace std::placeholders; using namespace std::placeholders;
QList<TaskItem> tasks {parallel, continueOnDone, onGroupError(onFollowsError)}; QList<GroupItem> tasks {parallel, continueOnDone, onGroupError(onFollowsError)};
for (int i = 0, total = parents.size(); i < total; ++i) { for (int i = 0, total = parents.size(); i < total; ++i) {
tasks.append(ProcessTask(std::bind(setupFollow, _1, parents.at(i)), tasks.append(ProcessTask(std::bind(setupFollow, _1, parents.at(i)),
std::bind(onFollowDone, _1, i))); std::bind(onFollowDone, _1, i)));
@@ -465,11 +465,11 @@ ShowController::ShowController(IDocument *document, const QString &id)
parallel, parallel,
onGroupSetup([this] { setStartupFile(VcsBase::source(this->document()).toString()); }), onGroupSetup([this] { setStartupFile(VcsBase::source(this->document()).toString()); }),
Group { Group {
optional, finishAllAndDone,
ProcessTask(setupDescription, onDescriptionDone), ProcessTask(setupDescription, onDescriptionDone),
Group { Group {
parallel, parallel,
optional, finishAllAndDone,
onGroupSetup(desciptionDetailsSetup), onGroupSetup(desciptionDetailsSetup),
ProcessTask(setupBranches, onBranchesDone, onBranchesError), ProcessTask(setupBranches, onBranchesDone, onBranchesError),
ProcessTask(setupPrecedes, onPrecedesDone, onPrecedesError), ProcessTask(setupPrecedes, onPrecedesDone, onPrecedesError),
@@ -2873,7 +2873,7 @@ bool GitClient::addAndCommit(const FilePath &repositoryDirectory,
GitPlugin::updateCurrentBranch(); GitPlugin::updateCurrentBranch();
return true; return true;
} }
VcsOutputWindow::appendError(Tr::tr("Cannot commit %n files", nullptr, commitCount) + "\n"); VcsOutputWindow::appendError(Tr::tr("Cannot commit %n file(s)", nullptr, commitCount) + "\n");
return false; return false;
} }

View File

@@ -21,13 +21,12 @@
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <QCheckBox> #include <QCheckBox>
#include <QFuture>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QRegularExpressionValidator> #include <QRegularExpressionValidator>
#include <QSettings> #include <QSettings>
#include <QTextStream>
using namespace Core; using namespace Core;
using namespace TextEditor;
using namespace Utils; using namespace Utils;
using namespace VcsBase; using namespace VcsBase;
@@ -43,91 +42,106 @@ public:
QString id() const { return recurseSubmodules ? ref + ".Rec" : ref; } QString id() const { return recurseSubmodules ? ref + ".Rec" : ref; }
}; };
class GitGrepRunner static QStringView nextLine(QStringView *remainingInput)
{ {
using PromiseType = QPromise<SearchResultItems>; const int newLinePos = remainingInput->indexOf('\n');
if (newLinePos < 0) {
public: QStringView ret = *remainingInput;
GitGrepRunner(const TextEditor::FileFindParameters &parameters) *remainingInput = QStringView();
: m_parameters(parameters) return ret;
{
m_directory = FilePath::fromString(parameters.additionalParameters.toString());
m_vcsBinary = GitClient::instance()->vcsBinary();
m_environment = GitClient::instance()->processEnvironment();
} }
QStringView ret = remainingInput->left(newLinePos);
*remainingInput = remainingInput->mid(newLinePos + 1);
return ret;
}
struct Match struct Match
{ {
Match() = default; Match() = default;
Match(int start, int length) : Match(int start, int length) :
matchStart(start), matchLength(length) {} matchStart(start), matchLength(length) {}
int matchStart = 0; int matchStart = 0;
int matchLength = 0; int matchLength = 0;
QStringList regexpCapturedTexts; QStringList regexpCapturedTexts;
}; };
void processLine(const QString &line, SearchResultItems *resultList) const static void processLine(QStringView line, SearchResultItems *resultList,
{ const std::optional<QRegularExpression> &regExp, const QString &ref,
const FilePath &directory)
{
if (line.isEmpty())
return;
static const QLatin1String boldRed("\x1b[1;31m");
static const QLatin1String resetColor("\x1b[m");
SearchResultItem result;
const int lineSeparator = line.indexOf(QChar::Null);
QStringView filePath = line.left(lineSeparator);
if (!ref.isEmpty() && filePath.startsWith(ref))
filePath = filePath.mid(ref.length());
result.setFilePath(directory.pathAppended(filePath.toString()));
const int textSeparator = line.indexOf(QChar::Null, lineSeparator + 1);
const int lineNumber = line.mid(lineSeparator + 1, textSeparator - lineSeparator - 1).toInt();
QString text = line.mid(textSeparator + 1).toString();
QList<Match> matches;
while (true) {
const int matchStart = text.indexOf(boldRed);
if (matchStart == -1)
break;
const int matchTextStart = matchStart + boldRed.size();
const int matchEnd = text.indexOf(resetColor, matchTextStart);
QTC_ASSERT(matchEnd != -1, break);
const int matchLength = matchEnd - matchTextStart;
Match match(matchStart, matchLength);
const QString matchText = text.mid(matchTextStart, matchLength);
if (regExp)
match.regexpCapturedTexts = regExp->match(matchText).capturedTexts();
matches.append(match);
text = text.left(matchStart) + matchText + text.mid(matchEnd + resetColor.size());
}
result.setDisplayText(text);
for (const auto &match : std::as_const(matches)) {
result.setMainRange(lineNumber, match.matchStart, match.matchLength);
result.setUserData(match.regexpCapturedTexts);
result.setUseTextEditorFont(true);
resultList->append(result);
}
}
static SearchResultItems parse(const QFuture<void> &future, const QString &input,
const std::optional<QRegularExpression> &regExp, const QString &ref,
const FilePath &directory)
{
SearchResultItems items;
QStringView remainingInput(input);
while (true) {
if (future.isCanceled())
return {};
if (remainingInput.isEmpty())
break;
const QStringView line = nextLine(&remainingInput);
if (line.isEmpty()) if (line.isEmpty())
return; continue;
static const QLatin1String boldRed("\x1b[1;31m");
static const QLatin1String resetColor("\x1b[m");
SearchResultItem result;
const int lineSeparator = line.indexOf(QChar::Null);
QString filePath = line.left(lineSeparator);
if (!m_ref.isEmpty() && filePath.startsWith(m_ref))
filePath.remove(0, m_ref.length());
result.setFilePath(m_directory.pathAppended(filePath));
const int textSeparator = line.indexOf(QChar::Null, lineSeparator + 1);
const int lineNumber = line.mid(lineSeparator + 1, textSeparator - lineSeparator - 1).toInt();
QString text = line.mid(textSeparator + 1);
QRegularExpression regexp;
QList<Match> matches;
if (m_parameters.flags & FindRegularExpression) {
const QRegularExpression::PatternOptions patternOptions =
(m_parameters.flags & FindCaseSensitively)
? QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption;
regexp.setPattern(m_parameters.text);
regexp.setPatternOptions(patternOptions);
}
for (;;) {
const int matchStart = text.indexOf(boldRed);
if (matchStart == -1)
break;
const int matchTextStart = matchStart + boldRed.size();
const int matchEnd = text.indexOf(resetColor, matchTextStart);
QTC_ASSERT(matchEnd != -1, break);
const int matchLength = matchEnd - matchTextStart;
Match match(matchStart, matchLength);
const QString matchText = text.mid(matchTextStart, matchLength);
if (m_parameters.flags & FindRegularExpression)
match.regexpCapturedTexts = regexp.match(matchText).capturedTexts();
matches.append(match);
text = text.left(matchStart) + matchText + text.mid(matchEnd + resetColor.size());
}
result.setDisplayText(text);
for (const auto &match : std::as_const(matches)) { processLine(line, &items, regExp, ref, directory);
result.setMainRange(lineNumber, match.matchStart, match.matchLength);
result.setUserData(match.regexpCapturedTexts);
resultList->append(result);
}
} }
return items;
}
void read(PromiseType &fi, const QString &text) static void runGitGrep(QPromise<SearchResultItems> &promise, const FileFindParameters &parameters)
{ {
SearchResultItems resultList; const FilePath directory = FilePath::fromString(parameters.additionalParameters.toString());
QString t = text; const GitGrepParameters gitParameters
QTextStream stream(&t); = parameters.searchEngineParameters.value<GitGrepParameters>();
while (!stream.atEnd() && !fi.isCanceled()) const QString ref = gitParameters.ref.isEmpty() ? QString() : gitParameters.ref + ':';
processLine(stream.readLine(), &resultList);
if (!resultList.isEmpty() && !fi.isCanceled()) const auto setupProcess = [&](Process &process) {
fi.addResult(resultList); const FilePath vcsBinary = GitClient::instance()->vcsBinary();
} const Environment environment = GitClient::instance()->processEnvironment();
void operator()(PromiseType &promise)
{
QStringList arguments = { QStringList arguments = {
"-c", "color.grep.match=bold red", "-c", "color.grep.match=bold red",
"-c", "color.grep=always", "-c", "color.grep=always",
@@ -135,60 +149,41 @@ public:
"-c", "color.grep.lineNumber=", "-c", "color.grep.lineNumber=",
"grep", "-zn", "--no-full-name" "grep", "-zn", "--no-full-name"
}; };
if (!(m_parameters.flags & FindCaseSensitively)) if (!(parameters.flags & FindCaseSensitively))
arguments << "-i"; arguments << "-i";
if (m_parameters.flags & FindWholeWords) if (parameters.flags & FindWholeWords)
arguments << "-w"; arguments << "-w";
if (m_parameters.flags & FindRegularExpression) if (parameters.flags & FindRegularExpression)
arguments << "-P"; arguments << "-P";
else else
arguments << "-F"; arguments << "-F";
arguments << "-e" << m_parameters.text; arguments << "-e" << parameters.text;
GitGrepParameters params = m_parameters.searchEngineParameters.value<GitGrepParameters>(); if (gitParameters.recurseSubmodules)
if (params.recurseSubmodules)
arguments << "--recurse-submodules"; arguments << "--recurse-submodules";
if (!params.ref.isEmpty()) { if (!gitParameters.ref.isEmpty()) {
arguments << params.ref; arguments << gitParameters.ref;
m_ref = params.ref + ':';
} }
const QStringList filterArgs = const QStringList filterArgs =
m_parameters.nameFilters.isEmpty() ? QStringList("*") // needed for exclusion filters parameters.nameFilters.isEmpty() ? QStringList("*") // needed for exclusion filters
: m_parameters.nameFilters; : parameters.nameFilters;
const QStringList exclusionArgs = const QStringList exclusionArgs =
Utils::transform(m_parameters.exclusionFilters, [](const QString &filter) { Utils::transform(parameters.exclusionFilters, [](const QString &filter) {
return QString(":!" + filter); return QString(":!" + filter);
}); });
arguments << "--" << filterArgs << exclusionArgs; arguments << "--" << filterArgs << exclusionArgs;
Process process; process.setEnvironment(environment);
process.setEnvironment(m_environment); process.setCommand({vcsBinary, arguments});
process.setCommand({m_vcsBinary, arguments}); process.setWorkingDirectory(directory);
process.setWorkingDirectory(m_directory); };
process.setStdOutCallback([this, &promise](const QString &text) { read(promise, text); });
process.start();
process.waitForFinished();
switch (process.result()) { const auto outputParser = [&ref, &directory](const QFuture<void> &future, const QString &input,
case ProcessResult::TerminatedAbnormally: const std::optional<QRegularExpression> &regExp) {
case ProcessResult::StartFailed: return parse(future, input, regExp, ref, directory);
case ProcessResult::Hang: };
promise.future().cancel();
break;
case ProcessResult::FinishedWithSuccess:
case ProcessResult::FinishedWithError:
// When no results are found, git-grep exits with non-zero status.
// Do not consider this as an error.
break;
}
}
private: TextEditor::searchInProcessOutput(promise, parameters, setupProcess, outputParser);
FilePath m_vcsBinary; }
FilePath m_directory;
QString m_ref;
TextEditor::FileFindParameters m_parameters;
Environment m_environment;
};
static bool isGitDirectory(const FilePath &path) static bool isGitDirectory(const FilePath &path)
{ {
@@ -211,18 +206,16 @@ GitGrep::GitGrep(GitClient *client)
m_treeLineEdit->setValidator(new QRegularExpressionValidator(refExpression, this)); m_treeLineEdit->setValidator(new QRegularExpressionValidator(refExpression, this));
layout->addWidget(m_treeLineEdit); layout->addWidget(m_treeLineEdit);
// asynchronously check git version, add "recurse submodules" option if available // asynchronously check git version, add "recurse submodules" option if available
Utils::onResultReady(client->gitVersion(), Utils::onResultReady(client->gitVersion(), this,
this,
[this, pLayout = QPointer<QHBoxLayout>(layout)](unsigned version) { [this, pLayout = QPointer<QHBoxLayout>(layout)](unsigned version) {
if (version >= 0x021300 && pLayout) { if (version >= 0x021300 && pLayout) {
m_recurseSubmodules = new QCheckBox(Tr::tr("Recurse submodules")); m_recurseSubmodules = new QCheckBox(Tr::tr("Recurse submodules"));
pLayout->addWidget(m_recurseSubmodules); pLayout->addWidget(m_recurseSubmodules);
} }
}); });
TextEditor::FindInFiles *findInFiles = TextEditor::FindInFiles::instance(); FindInFiles *findInFiles = FindInFiles::instance();
QTC_ASSERT(findInFiles, return); QTC_ASSERT(findInFiles, return);
connect(findInFiles, &TextEditor::FindInFiles::pathChanged, connect(findInFiles, &FindInFiles::pathChanged, m_widget, [this](const FilePath &path) {
m_widget, [this](const FilePath &path) {
setEnabled(isGitDirectory(path)); setEnabled(isGitDirectory(path));
}); });
connect(this, &SearchEngine::enabledChanged, m_widget, &QWidget::setEnabled); connect(this, &SearchEngine::enabledChanged, m_widget, &QWidget::setEnabled);
@@ -271,14 +264,14 @@ void GitGrep::writeSettings(QSettings *settings) const
settings->setValue(GitGrepRef, m_treeLineEdit->text()); settings->setValue(GitGrepRef, m_treeLineEdit->text());
} }
QFuture<SearchResultItems> GitGrep::executeSearch(const TextEditor::FileFindParameters &parameters, QFuture<SearchResultItems> GitGrep::executeSearch(const FileFindParameters &parameters,
TextEditor::BaseFileFind * /*baseFileFind*/) BaseFileFind *)
{ {
return Utils::asyncRun(GitGrepRunner(parameters)); return Utils::asyncRun(runGitGrep, parameters);
} }
IEditor *GitGrep::openEditor(const SearchResultItem &item, IEditor *GitGrep::openEditor(const SearchResultItem &item,
const TextEditor::FileFindParameters &parameters) const FileFindParameters &parameters)
{ {
const GitGrepParameters params = parameters.searchEngineParameters.value<GitGrepParameters>(); const GitGrepParameters params = parameters.searchEngineParameters.value<GitGrepParameters>();
const QStringList &itemPath = item.path(); const QStringList &itemPath = item.path();

View File

@@ -9,10 +9,7 @@ QT_BEGIN_NAMESPACE
class QCheckBox; class QCheckBox;
QT_END_NAMESPACE QT_END_NAMESPACE
namespace Utils { namespace Utils { class FancyLineEdit; }
class FancyLineEdit;
class SearchResultItem;
}
namespace Git::Internal { namespace Git::Internal {

View File

@@ -90,8 +90,8 @@ GitSettings::GitSettings()
instantBlame.setSettingsKey("Git Instant"); instantBlame.setSettingsKey("Git Instant");
instantBlame.setDefaultValue(true); instantBlame.setDefaultValue(true);
instantBlame.setLabelText(Tr::tr("Add instant blame annotations to editor")); instantBlame.setLabelText(Tr::tr("Add instant blame annotations to editor"));
instantBlame.setToolTip(Tr::tr("Directly annotate each line in the editor " instantBlame.setToolTip(
"when scrolling through the document.")); Tr::tr("Annotate the current line in the editor with Git \"blame\" output."));
graphLog.setSettingsKey("GraphLog"); graphLog.setSettingsKey("GraphLog");

View File

@@ -356,6 +356,7 @@ void Client::setName(const QString &name)
QString Client::name() const QString Client::name() const
{ {
if (d->m_project && !d->m_project->displayName().isEmpty()) if (d->m_project && !d->m_project->displayName().isEmpty())
//: <language client> for <project>
return Tr::tr("%1 for %2").arg(d->m_displayName, d->m_project->displayName()); return Tr::tr("%1 for %2").arg(d->m_displayName, d->m_project->displayName());
return d->m_displayName; return d->m_displayName;
} }
@@ -555,11 +556,17 @@ Client::State Client::state() const
QString Client::stateString() const QString Client::stateString() const
{ {
switch (d->m_state){ switch (d->m_state){
//: language client state
case Uninitialized: return Tr::tr("uninitialized"); case Uninitialized: return Tr::tr("uninitialized");
//: language client state
case InitializeRequested: return Tr::tr("initialize requested"); case InitializeRequested: return Tr::tr("initialize requested");
//: language client state
case Initialized: return Tr::tr("initialized"); case Initialized: return Tr::tr("initialized");
//: language client state
case ShutdownRequested: return Tr::tr("shutdown requested"); case ShutdownRequested: return Tr::tr("shutdown requested");
//: language client state
case Shutdown: return Tr::tr("shut down"); case Shutdown: return Tr::tr("shut down");
//: language client state
case Error: return Tr::tr("error"); case Error: return Tr::tr("error");
} }
return {}; return {};
@@ -1970,7 +1977,7 @@ void ClientPrivate::initializeCallback(const InitializeRequest::Response &initRe
if (std::optional<ResponseError<InitializeError>> error = initResponse.error()) { if (std::optional<ResponseError<InitializeError>> error = initResponse.error()) {
if (std::optional<InitializeError> data = error->data()) { if (std::optional<InitializeError> data = error->data()) {
if (data->retry()) { if (data->retry()) {
const QString title(Tr::tr("Language Server \"%1\" Initialize Error").arg(m_displayName)); const QString title(Tr::tr("Language Server \"%1\" Initialization Error").arg(m_displayName));
auto result = QMessageBox::warning(Core::ICore::dialogParent(), auto result = QMessageBox::warning(Core::ICore::dialogParent(),
title, title,
error->message(), error->message(),
@@ -1983,7 +1990,7 @@ void ClientPrivate::initializeCallback(const InitializeRequest::Response &initRe
} }
} }
} }
q->setError(Tr::tr("Initialize error: ") + error->message()); q->setError(Tr::tr("Initialization error: %1.").arg(error->message()));
emit q->finished(); emit q->finished();
return; return;
} }

View File

@@ -8,6 +8,7 @@
#include <languageserverprotocol/progresssupport.h> #include <languageserverprotocol/progresssupport.h>
#include <QTime> #include <QTime>
#include <QTimer>
using namespace LanguageServerProtocol; using namespace LanguageServerProtocol;
@@ -81,9 +82,28 @@ void ProgressManager::beginProgress(const ProgressToken &token, const WorkDonePr
auto interface = new QFutureInterface<void>(); auto interface = new QFutureInterface<void>();
interface->reportStarted(); interface->reportStarted();
interface->setProgressRange(0, 100); // LSP always reports percentage of the task interface->setProgressRange(0, 100); // LSP always reports percentage of the task
const QString title = m_titles.value(token, begin.title()); ProgressItem progressItem;
Core::FutureProgress *progress = Core::ProgressManager::addTask( progressItem.futureInterface = interface;
interface->future(), title, languageClientProgressId(token)); progressItem.title = m_titles.value(token, begin.title());
if (LOGPROGRESS().isDebugEnabled())
progressItem.timer.start();
progressItem.showBarTimer = new QTimer();
progressItem.showBarTimer->setSingleShot(true);
progressItem.showBarTimer->setInterval(750);
progressItem.showBarTimer->callOnTimeout([this, token]() { spawnProgressBar(token); });
progressItem.showBarTimer->start();
m_progress[token] = progressItem;
reportProgress(token, begin);
}
void ProgressManager::spawnProgressBar(const LanguageServerProtocol::ProgressToken &token)
{
ProgressItem &progressItem = m_progress[token];
QTC_ASSERT(progressItem.futureInterface, return);
Core::FutureProgress *progress
= Core::ProgressManager::addTask(progressItem.futureInterface->future(),
progressItem.title,
languageClientProgressId(token));
const std::function<void()> clickHandler = m_clickHandlers.value(token); const std::function<void()> clickHandler = m_clickHandlers.value(token);
if (clickHandler) if (clickHandler)
QObject::connect(progress, &Core::FutureProgress::clicked, clickHandler); QObject::connect(progress, &Core::FutureProgress::clicked, clickHandler);
@@ -92,23 +112,26 @@ void ProgressManager::beginProgress(const ProgressToken &token, const WorkDonePr
QObject::connect(progress, &Core::FutureProgress::canceled, cancelHandler); QObject::connect(progress, &Core::FutureProgress::canceled, cancelHandler);
else else
progress->setCancelEnabled(false); progress->setCancelEnabled(false);
m_progress[token] = {progress, interface}; if (!progressItem.message.isEmpty()) {
if (LOGPROGRESS().isDebugEnabled()) progress->setSubtitle(progressItem.message);
m_timer[token].start(); progress->setSubtitleVisibleInStatusBar(true);
reportProgress(token, begin); }
progressItem.progressInterface = progress;
} }
void ProgressManager::reportProgress(const ProgressToken &token, void ProgressManager::reportProgress(const ProgressToken &token,
const WorkDoneProgressReport &report) const WorkDoneProgressReport &report)
{ {
const LanguageClientProgress &progress = m_progress.value(token); ProgressItem &progress = m_progress[token];
const std::optional<QString> &message = report.message();
if (progress.progressInterface) { if (progress.progressInterface) {
const std::optional<QString> &message = report.message();
if (message.has_value()) { if (message.has_value()) {
progress.progressInterface->setSubtitle(*message); progress.progressInterface->setSubtitle(*message);
const bool showSubtitle = !message->isEmpty(); const bool showSubtitle = !message->isEmpty();
progress.progressInterface->setSubtitleVisibleInStatusBar(showSubtitle); progress.progressInterface->setSubtitleVisibleInStatusBar(showSubtitle);
} }
} else if (message.has_value()) {
progress.message = *message;
} }
if (progress.futureInterface) { if (progress.futureInterface) {
if (const std::optional<double> &percentage = report.percentage(); percentage.has_value()) if (const std::optional<double> &percentage = report.percentage(); percentage.has_value())
@@ -118,7 +141,7 @@ void ProgressManager::reportProgress(const ProgressToken &token,
void ProgressManager::endProgress(const ProgressToken &token, const WorkDoneProgressEnd &end) void ProgressManager::endProgress(const ProgressToken &token, const WorkDoneProgressEnd &end)
{ {
const LanguageClientProgress &progress = m_progress.value(token); const ProgressItem &progress = m_progress.value(token);
const QString &message = end.message().value_or(QString()); const QString &message = end.message().value_or(QString());
if (progress.progressInterface) { if (progress.progressInterface) {
if (!message.isEmpty()) { if (!message.isEmpty()) {
@@ -127,11 +150,11 @@ void ProgressManager::endProgress(const ProgressToken &token, const WorkDoneProg
} }
progress.progressInterface->setSubtitle(message); progress.progressInterface->setSubtitle(message);
progress.progressInterface->setSubtitleVisibleInStatusBar(!message.isEmpty()); progress.progressInterface->setSubtitleVisibleInStatusBar(!message.isEmpty());
auto timer = m_timer.take(token); if (progress.timer.isValid()) {
if (timer.isValid()) {
qCDebug(LOGPROGRESS) << QString("%1 took %2") qCDebug(LOGPROGRESS) << QString("%1 took %2")
.arg(progress.progressInterface->title()) .arg(progress.progressInterface->title())
.arg(QTime::fromMSecsSinceStartOfDay(timer.elapsed()) .arg(QTime::fromMSecsSinceStartOfDay(
progress.timer.elapsed())
.toString(Qt::ISODateWithMs)); .toString(Qt::ISODateWithMs));
} }
} }
@@ -140,7 +163,8 @@ void ProgressManager::endProgress(const ProgressToken &token, const WorkDoneProg
void ProgressManager::endProgressReport(const ProgressToken &token) void ProgressManager::endProgressReport(const ProgressToken &token)
{ {
const LanguageClientProgress &progress = m_progress.take(token); ProgressItem progress = m_progress.take(token);
delete progress.showBarTimer;
if (progress.futureInterface) if (progress.futureInterface)
progress.futureInterface->reportFinished(); progress.futureInterface->reportFinished();
delete progress.futureInterface; delete progress.futureInterface;

View File

@@ -11,6 +11,10 @@
#include <QFutureInterface> #include <QFutureInterface>
#include <QPointer> #include <QPointer>
QT_BEGIN_NAMESPACE
class QTimer;
QT_END_NAMESPACE
namespace LanguageServerProtocol { namespace LanguageServerProtocol {
class ProgressParams; class ProgressParams;
class ProgressToken; class ProgressToken;
@@ -46,15 +50,20 @@ private:
const LanguageServerProtocol::WorkDoneProgressReport &report); const LanguageServerProtocol::WorkDoneProgressReport &report);
void endProgress(const LanguageServerProtocol::ProgressToken &token, void endProgress(const LanguageServerProtocol::ProgressToken &token,
const LanguageServerProtocol::WorkDoneProgressEnd &end); const LanguageServerProtocol::WorkDoneProgressEnd &end);
void spawnProgressBar(const LanguageServerProtocol::ProgressToken &token);
struct LanguageClientProgress { struct ProgressItem
{
QPointer<Core::FutureProgress> progressInterface = nullptr; QPointer<Core::FutureProgress> progressInterface = nullptr;
QFutureInterface<void> *futureInterface = nullptr; QFutureInterface<void> *futureInterface = nullptr;
QElapsedTimer timer;
QTimer *showBarTimer = nullptr;
QString message;
QString title;
}; };
QMap<LanguageServerProtocol::ProgressToken, LanguageClientProgress> m_progress; QMap<LanguageServerProtocol::ProgressToken, ProgressItem> m_progress;
QMap<LanguageServerProtocol::ProgressToken, QString> m_titles; QMap<LanguageServerProtocol::ProgressToken, QString> m_titles;
QMap<LanguageServerProtocol::ProgressToken, QElapsedTimer> m_timer;
QMap<LanguageServerProtocol::ProgressToken, std::function<void()>> m_clickHandlers; QMap<LanguageServerProtocol::ProgressToken, std::function<void()>> m_clickHandlers;
QMap<LanguageServerProtocol::ProgressToken, std::function<void()>> m_cancelHandlers; QMap<LanguageServerProtocol::ProgressToken, std::function<void()>> m_cancelHandlers;
}; };

View File

@@ -9,7 +9,6 @@ add_qtc_plugin(Macros
macrolocatorfilter.cpp macrolocatorfilter.h macrolocatorfilter.cpp macrolocatorfilter.h
macromanager.cpp macromanager.h macromanager.cpp macromanager.h
macrooptionspage.cpp macrooptionspage.h macrooptionspage.cpp macrooptionspage.h
macrooptionswidget.cpp macrooptionswidget.h
macros.qrc macros.qrc
macrosconstants.h macrosconstants.h
macrosplugin.cpp macrosplugin.h macrosplugin.cpp macrosplugin.h

View File

@@ -3,15 +3,195 @@
#include "macrooptionspage.h" #include "macrooptionspage.h"
#include "macro.h"
#include "macromanager.h" #include "macromanager.h"
#include "macrooptionswidget.h"
#include "macrosconstants.h" #include "macrosconstants.h"
#include "macrostr.h" #include "macrostr.h"
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/icore.h>
#include <texteditor/texteditorconstants.h> #include <texteditor/texteditorconstants.h>
namespace Macros { #include <utils/layoutbuilder.h>
namespace Internal {
#include <QAction>
#include <QDir>
#include <QFileInfo>
#include <QGroupBox>
#include <QHeaderView>
#include <QLabel>
#include <QLineEdit>
#include <QMap>
#include <QPushButton>
#include <QStringList>
#include <QTreeWidget>
#include <QTreeWidgetItem>
namespace Macros::Internal {
const int NAME_ROLE = Qt::UserRole;
const int WRITE_ROLE = Qt::UserRole + 1;
class MacroOptionsWidget final : public Core::IOptionsPageWidget
{
public:
MacroOptionsWidget();
void initialize();
void apply() final;
private:
void remove();
void changeCurrentItem(QTreeWidgetItem *current);
void createTable();
void changeDescription(const QString &description);
QStringList m_macroToRemove;
bool m_changingCurrent = false;
QMap<QString, QString> m_macroToChange;
QTreeWidget *m_treeWidget;
QPushButton *m_removeButton;
QGroupBox *m_macroGroup;
QLineEdit *m_description;
};
MacroOptionsWidget::MacroOptionsWidget()
{
m_treeWidget = new QTreeWidget;
m_treeWidget->setTextElideMode(Qt::ElideLeft);
m_treeWidget->setUniformRowHeights(true);
m_treeWidget->setSortingEnabled(true);
m_treeWidget->setColumnCount(3);
m_treeWidget->header()->setSortIndicatorShown(true);
m_treeWidget->header()->setStretchLastSection(true);
m_treeWidget->header()->setSortIndicator(0, Qt::AscendingOrder);
m_treeWidget->setHeaderLabels({Tr::tr("Name"), Tr::tr("Description"), Tr::tr("Shortcut")});
m_description = new QLineEdit;
m_removeButton = new QPushButton(Tr::tr("Remove"));
m_macroGroup = new QGroupBox(Tr::tr("Macro"), this);
using namespace Layouting;
Row {
Tr::tr("Description:"), m_description
}.attachTo(m_macroGroup);
Column {
Group {
title(Tr::tr("Preferences")),
Row {
m_treeWidget,
Column { m_removeButton, st },
}
},
m_macroGroup
}.attachTo(this);
connect(m_treeWidget, &QTreeWidget::currentItemChanged,
this, &MacroOptionsWidget::changeCurrentItem);
connect(m_removeButton, &QPushButton::clicked,
this, &MacroOptionsWidget::remove);
connect(m_description, &QLineEdit::textChanged,
this, &MacroOptionsWidget::changeDescription);
initialize();
}
void MacroOptionsWidget::initialize()
{
m_macroToRemove.clear();
m_macroToChange.clear();
m_treeWidget->clear();
changeCurrentItem(nullptr);
// Create the treeview
createTable();
}
void MacroOptionsWidget::createTable()
{
QDir dir(MacroManager::macrosDirectory());
const Utils::Id base = Utils::Id(Constants::PREFIX_MACRO);
for (Macro *macro : MacroManager::macros()) {
QFileInfo fileInfo(macro->fileName());
if (fileInfo.absoluteDir() == dir.absolutePath()) {
auto macroItem = new QTreeWidgetItem(m_treeWidget);
macroItem->setText(0, macro->displayName());
macroItem->setText(1, macro->description());
macroItem->setData(0, NAME_ROLE, macro->displayName());
macroItem->setData(0, WRITE_ROLE, macro->isWritable());
Core::Command *command =
Core::ActionManager::command(base.withSuffix(macro->displayName()));
if (command && command->action()) {
macroItem->setText(2,
command->action()->shortcut().toString(QKeySequence::NativeText));
}
}
}
}
void MacroOptionsWidget::changeCurrentItem(QTreeWidgetItem *current)
{
m_changingCurrent = true;
m_removeButton->setEnabled(current);
m_macroGroup->setEnabled(current);
if (!current) {
m_description->clear();
} else {
m_description->setText(current->text(1));
m_description->setEnabled(current->data(0, WRITE_ROLE).toBool());
}
m_changingCurrent = false;
}
void MacroOptionsWidget::remove()
{
QTreeWidgetItem *current = m_treeWidget->currentItem();
m_macroToRemove.append(current->data(0, NAME_ROLE).toString());
delete current;
}
void MacroOptionsWidget::apply()
{
// Remove macro
for (const QString &name : std::as_const(m_macroToRemove)) {
MacroManager::instance()->deleteMacro(name);
m_macroToChange.remove(name);
}
// Change macro
for (auto it = m_macroToChange.cbegin(), end = m_macroToChange.cend(); it != end; ++it)
MacroManager::instance()->changeMacro(it.key(), it.value());
// Reinitialize the page
initialize();
}
void MacroOptionsWidget::changeDescription(const QString &description)
{
QTreeWidgetItem *current = m_treeWidget->currentItem();
if (m_changingCurrent || !current)
return;
QString macroName = current->data(0, NAME_ROLE).toString();
m_macroToChange[macroName] = description;
current->setText(1, description);
QFont font = current->font(1);
font.setItalic(true);
current->setFont(1, font);
}
MacroOptionsPage::MacroOptionsPage() MacroOptionsPage::MacroOptionsPage()
{ {
@@ -21,5 +201,4 @@ MacroOptionsPage::MacroOptionsPage()
setWidgetCreator([] { return new MacroOptionsWidget; }); setWidgetCreator([] { return new MacroOptionsWidget; });
} }
} // Internal } // Macros::Internal
} // Macros

View File

@@ -5,8 +5,7 @@
#include <coreplugin/dialogs/ioptionspage.h> #include <coreplugin/dialogs/ioptionspage.h>
namespace Macros { namespace Macros::Internal {
namespace Internal {
class MacroOptionsPage final : public Core::IOptionsPage class MacroOptionsPage final : public Core::IOptionsPage
{ {
@@ -14,5 +13,4 @@ public:
MacroOptionsPage(); MacroOptionsPage();
}; };
} // namespace Internal } // Macros::Internal
} // namespace Macros

View File

@@ -1,166 +0,0 @@
// Copyright (C) 2016 Nicolas Arnaud-Cormos
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "macrooptionswidget.h"
#include "macro.h"
#include "macromanager.h"
#include "macrosconstants.h"
#include "macrostr.h"
#include <coreplugin/icore.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
#include <utils/layoutbuilder.h>
#include <QAction>
#include <QDir>
#include <QFileInfo>
#include <QGroupBox>
#include <QHeaderView>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QTreeWidget>
#include <QTreeWidgetItem>
namespace Macros::Internal {
const int NAME_ROLE = Qt::UserRole;
const int WRITE_ROLE = Qt::UserRole + 1;
MacroOptionsWidget::MacroOptionsWidget()
{
m_treeWidget = new QTreeWidget;
m_treeWidget->setTextElideMode(Qt::ElideLeft);
m_treeWidget->setUniformRowHeights(true);
m_treeWidget->setSortingEnabled(true);
m_treeWidget->setColumnCount(3);
m_treeWidget->header()->setSortIndicatorShown(true);
m_treeWidget->header()->setStretchLastSection(true);
m_treeWidget->header()->setSortIndicator(0, Qt::AscendingOrder);
m_treeWidget->setHeaderLabels({Tr::tr("Name"), Tr::tr("Description"), Tr::tr("Shortcut")});
m_description = new QLineEdit;
m_removeButton = new QPushButton(Tr::tr("Remove"));
m_macroGroup = new QGroupBox(Tr::tr("Macro"), this);
using namespace Layouting;
Row {
Tr::tr("Description:"), m_description
}.attachTo(m_macroGroup);
Column {
Group {
title(Tr::tr("Preferences")),
Row {
m_treeWidget,
Column { m_removeButton, st },
}
},
m_macroGroup
}.attachTo(this);
connect(m_treeWidget, &QTreeWidget::currentItemChanged,
this, &MacroOptionsWidget::changeCurrentItem);
connect(m_removeButton, &QPushButton::clicked,
this, &MacroOptionsWidget::remove);
connect(m_description, &QLineEdit::textChanged,
this, &MacroOptionsWidget::changeDescription);
initialize();
}
MacroOptionsWidget::~MacroOptionsWidget() = default;
void MacroOptionsWidget::initialize()
{
m_macroToRemove.clear();
m_macroToChange.clear();
m_treeWidget->clear();
changeCurrentItem(nullptr);
// Create the treeview
createTable();
}
void MacroOptionsWidget::createTable()
{
QDir dir(MacroManager::macrosDirectory());
const Utils::Id base = Utils::Id(Constants::PREFIX_MACRO);
for (Macro *macro : MacroManager::macros()) {
QFileInfo fileInfo(macro->fileName());
if (fileInfo.absoluteDir() == dir.absolutePath()) {
auto macroItem = new QTreeWidgetItem(m_treeWidget);
macroItem->setText(0, macro->displayName());
macroItem->setText(1, macro->description());
macroItem->setData(0, NAME_ROLE, macro->displayName());
macroItem->setData(0, WRITE_ROLE, macro->isWritable());
Core::Command *command =
Core::ActionManager::command(base.withSuffix(macro->displayName()));
if (command && command->action()) {
macroItem->setText(2,
command->action()->shortcut().toString(QKeySequence::NativeText));
}
}
}
}
void MacroOptionsWidget::changeCurrentItem(QTreeWidgetItem *current)
{
m_changingCurrent = true;
m_removeButton->setEnabled(current);
m_macroGroup->setEnabled(current);
if (!current) {
m_description->clear();
} else {
m_description->setText(current->text(1));
m_description->setEnabled(current->data(0, WRITE_ROLE).toBool());
}
m_changingCurrent = false;
}
void MacroOptionsWidget::remove()
{
QTreeWidgetItem *current = m_treeWidget->currentItem();
m_macroToRemove.append(current->data(0, NAME_ROLE).toString());
delete current;
}
void MacroOptionsWidget::apply()
{
// Remove macro
for (const QString &name : std::as_const(m_macroToRemove)) {
MacroManager::instance()->deleteMacro(name);
m_macroToChange.remove(name);
}
// Change macro
for (auto it = m_macroToChange.cbegin(), end = m_macroToChange.cend(); it != end; ++it)
MacroManager::instance()->changeMacro(it.key(), it.value());
// Reinitialize the page
initialize();
}
void MacroOptionsWidget::changeDescription(const QString &description)
{
QTreeWidgetItem *current = m_treeWidget->currentItem();
if (m_changingCurrent || !current)
return;
QString macroName = current->data(0, NAME_ROLE).toString();
m_macroToChange[macroName] = description;
current->setText(1, description);
QFont font = current->font(1);
font.setItalic(true);
current->setFont(1, font);
}
} // Macros::Internal

View File

@@ -1,55 +0,0 @@
// Copyright (C) 2016 Nicolas Arnaud-Cormos
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include <coreplugin/dialogs/ioptionspage.h>
#include <QStringList>
#include <QMap>
QT_BEGIN_NAMESPACE
class QGroupBox;
class QLineEdit;
class QPushButton;
class QTreeWidget;
class QTreeWidgetItem;
QT_END_NAMESPACE
namespace Macros {
namespace Internal {
class MacroOptionsWidget final : public Core::IOptionsPageWidget
{
Q_OBJECT
public:
MacroOptionsWidget();
~MacroOptionsWidget() final;
void initialize();
void apply() final;
private:
void remove();
void changeCurrentItem(QTreeWidgetItem *current);
void createTable();
void changeDescription(const QString &description);
private:
QStringList m_macroToRemove;
bool m_changingCurrent = false;
QMap<QString, QString> m_macroToChange;
QTreeWidget *m_treeWidget;
QPushButton *m_removeButton;
QGroupBox *m_macroGroup;
QLineEdit *m_description;
};
} // namespace Internal
} // namespace Macros

View File

@@ -29,8 +29,6 @@ QtcPlugin {
"macromanager.h", "macromanager.h",
"macrooptionspage.cpp", "macrooptionspage.cpp",
"macrooptionspage.h", "macrooptionspage.h",
"macrooptionswidget.cpp",
"macrooptionswidget.h",
"macros.qrc", "macros.qrc",
"macrosconstants.h", "macrosconstants.h",
"macrosplugin.cpp", "macrosplugin.cpp",

View File

@@ -24,7 +24,7 @@ add_qtc_plugin(McuSupport
settingshandler.cpp settingshandler.h settingshandler.cpp settingshandler.h
mcuqmlprojectnode.cpp mcuqmlprojectnode.h mcuqmlprojectnode.cpp mcuqmlprojectnode.h
mcubuildstep.cpp mcubuildstep.h mcubuildstep.cpp mcubuildstep.h
dialogs/mcukitcreationdialog.h dialogs/mcukitcreationdialog.cpp dialogs/mcukitcreationdialog.ui dialogs/mcukitcreationdialog.cpp dialogs/mcukitcreationdialog.h
) )
add_subdirectory(test) add_subdirectory(test)

Some files were not shown because too many files have changed in this diff Show More