forked from qt-creator/qt-creator
Merge remote-tracking branch 'origin/11.0'
Conflicts: tests/manual/subdirfileiterator/tst_subdirfileiterator.cpp Change-Id: I6fa8fbed152efc4033fa69e1ab67ced7e2ad35bc
This commit is contained in:
223
dist/changelog/changes-11.0.0.md
vendored
Normal file
223
dist/changelog/changes-11.0.0.md
vendored
Normal 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
|
||||
@@ -482,8 +482,9 @@
|
||||
\row
|
||||
\li Add Class Member
|
||||
\li Adds a member declaration for the class member being
|
||||
initialized if it is not yet declared. You must enter
|
||||
the data type of the member.
|
||||
initialized if it is not yet declared. If \QC cannot
|
||||
automatically detect the data type of the member, you
|
||||
must add it.
|
||||
\li Identifier
|
||||
\row
|
||||
\li Create Implementations for Member Functions
|
||||
|
||||
@@ -99,4 +99,7 @@
|
||||
|
||||
\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.
|
||||
*/
|
||||
|
||||
@@ -18199,16 +18199,16 @@ Möchten Sie sie jetzt auschecken?</translation>
|
||||
<translation>Bytes</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>KB</source>
|
||||
<translation>KB</translation>
|
||||
<source>KiB</source>
|
||||
<translation>KiB</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>GB</source>
|
||||
<translation>GB</translation>
|
||||
<source>GiB</source>
|
||||
<translation>GiB</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>TB</source>
|
||||
<translation>TB</translation>
|
||||
<source>TiB</source>
|
||||
<translation>TiB</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enable crash reporting</source>
|
||||
@@ -27194,12 +27194,8 @@ zu deaktivieren, deaktiviert auch die folgenden Plugins:
|
||||
<translation>Privat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<source>Tag names to apply; comma-separated.</source>
|
||||
@@ -39112,7 +39108,7 @@ Sie werden erhalten.</numerusform>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Configure Project</source>
|
||||
<translation>Projekt &Konfigurieren</translation>
|
||||
<translation>Projekt &konfigurieren</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enable Kit for Project "%1"</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>
|
||||
<translation>Hinweis: Die zweite Zeile der Beschreibung sollte leer sein.</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source><p>Writing good commit messages</p><ul><li>Avoid very short commit messages.</li><li>Consider the first line as subject (like in email) and keep it shorter than %n characters.</li><li>After an empty second line, a longer description can be added.</li><li>Describe why the change was done, not how it was done.</li></ul></source>
|
||||
<translation>
|
||||
<numerusform><p>Gute Beschreibungen für Commits schreiben</p><ul><li>Vermeiden Sie sehr kurze Beschreibungen.</li><li>Betrachten Sie die erste Zeile als Betreff (wie in einer E-Mail) und halten Sie sie kürzer als ein Zeichen.</li><li>Eine längere Beschreibung kann nach einer leeren zweiten Zeile folgen.</li><li>Beschreiben Sie, weshalb die Änderung vorgenommen wurde, nicht wie sie vorgenommen wurde.</li></ul></numerusform>
|
||||
<numerusform><p>Gute Beschreibungen für Commits schreiben</p><ul><li>Vermeiden Sie sehr kurze Beschreibungen.</li><li>Betrachten Sie die erste Zeile als Betreff (wie in einer E-Mail) und halten Sie sie kürzer als %n Zeichen.</li><li>Eine längere Beschreibung kann nach einer leeren zweiten Zeile folgen.</li><li>Beschreiben Sie, weshalb die Änderung vorgenommen wurde, nicht wie sie vorgenommen wurde.</li></ul></numerusform>
|
||||
</translation>
|
||||
<message>
|
||||
<source><p>Writing good commit messages</p><ul><li>Avoid very short commit messages.</li><li>Consider the first line as a subject (like in emails) and keep it shorter than 72 characters.</li><li>After an empty second line, a longer description can be added.</li><li>Describe why the change was done, not how it was done.</li></ul></source>
|
||||
<translation><p>Gute Beschreibungen für Commits schreiben</p><ul><li>Vermeiden Sie sehr kurze Beschreibungen.</li><li>Betrachten Sie die erste Zeile als Betreff (wie in einer E-Mail) und halten Sie sie kürzer als 72 Zeichen.</li><li>Eine längere Beschreibung kann nach einer leeren zweiten Zeile folgen.</li><li>Beschreiben Sie, weshalb die Änderung vorgenommen wurde, nicht wie sie vorgenommen wurde.</li></ul></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Update in progress</source>
|
||||
|
||||
@@ -369,61 +369,62 @@ LibraryInfo::LibraryInfo(const QmlDirParser &parser, const QByteArray &fingerpri
|
||||
QByteArray LibraryInfo::calculateFingerprint() const
|
||||
{
|
||||
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();
|
||||
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
|
||||
addData(&len, sizeof(len));
|
||||
for (const QmlDirParser::Component &component : _components) {
|
||||
len = component.fileName.size();
|
||||
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
|
||||
hash.addData(reinterpret_cast<const char *>(component.fileName.constData()),
|
||||
len * sizeofQChar);
|
||||
hash.addData(reinterpret_cast<const char *>(&component.majorVersion), sizeof(component.majorVersion));
|
||||
hash.addData(reinterpret_cast<const char *>(&component.minorVersion), sizeof(component.minorVersion));
|
||||
addData(&len, sizeof(len));
|
||||
addData(component.fileName.constData(), len * sizeofQChar);
|
||||
addData(&component.majorVersion, sizeof(component.majorVersion));
|
||||
addData(&component.minorVersion, sizeof(component.minorVersion));
|
||||
len = component.typeName.size();
|
||||
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
|
||||
hash.addData(reinterpret_cast<const char *>(component.typeName.constData()),
|
||||
component.typeName.size() * sizeofQChar);
|
||||
addData(&len, sizeof(len));
|
||||
addData(component.typeName.constData(), component.typeName.size() * sizeofQChar);
|
||||
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();
|
||||
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
|
||||
addData(&len, sizeof(len));
|
||||
for (const QmlDirParser::Plugin &plugin : _plugins) {
|
||||
len = plugin.path.size();
|
||||
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
|
||||
hash.addData(reinterpret_cast<const char *>(plugin.path.constData()), len * sizeofQChar);
|
||||
addData(&len, sizeof(len));
|
||||
addData(plugin.path.constData(), len * sizeofQChar);
|
||||
len = plugin.name.size();
|
||||
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
|
||||
hash.addData(reinterpret_cast<const char *>(plugin.name.constData()), len * sizeofQChar);
|
||||
addData(&len, sizeof(len));
|
||||
addData(plugin.name.constData(), len * sizeofQChar);
|
||||
}
|
||||
len = _typeinfos.size();
|
||||
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
|
||||
addData(&len, sizeof(len));
|
||||
for (const QString &typeinfo : _typeinfos) {
|
||||
len = typeinfo.size();
|
||||
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
|
||||
hash.addData(reinterpret_cast<const char *>(typeinfo.constData()),
|
||||
len * sizeofQChar);
|
||||
addData(&len, sizeof(len));
|
||||
addData(typeinfo.constData(), len * sizeofQChar);
|
||||
}
|
||||
len = _metaObjects.size();
|
||||
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
|
||||
addData(&len, sizeof(len));
|
||||
QList<QByteArray> metaFingerprints;
|
||||
for (const LanguageUtils::FakeMetaObject::ConstPtr &metaObject : _metaObjects)
|
||||
metaFingerprints.append(metaObject->fingerprint());
|
||||
std::sort(metaFingerprints.begin(), metaFingerprints.end());
|
||||
for (const QByteArray &fp : std::as_const(metaFingerprints))
|
||||
hash.addData(fp);
|
||||
hash.addData(reinterpret_cast<const char *>(&_dumpStatus), sizeof(_dumpStatus));
|
||||
addData(&_dumpStatus, sizeof(_dumpStatus));
|
||||
len = _dumpError.size(); // localization dependent (avoid?)
|
||||
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
|
||||
hash.addData(reinterpret_cast<const char *>(_dumpError.constData()), len * sizeofQChar);
|
||||
addData(&len, sizeof(len));
|
||||
addData(_dumpError.constData(), len * sizeofQChar);
|
||||
|
||||
len = _moduleApis.size();
|
||||
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
|
||||
addData(&len, sizeof(len));
|
||||
for (const ModuleApiInfo &moduleInfo : _moduleApis)
|
||||
moduleInfo.addToHash(hash); // make it order independent?
|
||||
|
||||
len = _imports.size();
|
||||
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
|
||||
addData(&len, sizeof(len));
|
||||
for (const QmlDirParser::Import &import : _imports)
|
||||
hash.addData(import.module.toUtf8()); // import order matters, keep order-dependent
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
add_qtc_library(Tasking OBJECT
|
||||
# Never add dependencies to non-Qt libraries for this library
|
||||
DEPENDS Qt::Core
|
||||
DEPENDS Qt::Concurrent Qt::Core Qt::Network
|
||||
PUBLIC_DEFINES TASKING_LIBRARY
|
||||
SOURCES
|
||||
barrier.cpp barrier.h
|
||||
concurrentcall.h
|
||||
networkquery.cpp networkquery.h
|
||||
tasking_global.h
|
||||
tasktree.cpp tasktree.h
|
||||
)
|
||||
|
||||
@@ -34,7 +34,7 @@ private:
|
||||
int m_current = -1;
|
||||
};
|
||||
|
||||
class TASKING_EXPORT BarrierTaskAdapter : public Tasking::TaskAdapter<Barrier>
|
||||
class TASKING_EXPORT BarrierTaskAdapter : public TaskAdapter<Barrier>
|
||||
{
|
||||
public:
|
||||
BarrierTaskAdapter() { connect(task(), &Barrier::done, this, &TaskInterface::done); }
|
||||
|
||||
100
src/libs/solutions/tasking/concurrentcall.h
Normal file
100
src/libs/solutions/tasking/concurrentcall.h
Normal 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);
|
||||
38
src/libs/solutions/tasking/networkquery.cpp
Normal file
38
src/libs/solutions/tasking/networkquery.cpp
Normal 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
|
||||
55
src/libs/solutions/tasking/networkquery.h
Normal file
55
src/libs/solutions/tasking/networkquery.h
Normal 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);
|
||||
@@ -1,11 +1,14 @@
|
||||
QtcLibrary {
|
||||
name: "Tasking"
|
||||
Depends { name: "Qt"; submodules: ["core"] }
|
||||
Depends { name: "Qt"; submodules: ["concurrent", "core", "network"] }
|
||||
cpp.defines: base.concat("TASKING_LIBRARY")
|
||||
|
||||
files: [
|
||||
"barrier.cpp",
|
||||
"barrier.h",
|
||||
"concurrentcall.h",
|
||||
"networkquery.cpp",
|
||||
"networkquery.h",
|
||||
"tasking_global.h",
|
||||
"tasktree.cpp",
|
||||
"tasktree.h",
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#include <QSet>
|
||||
#include <QTimer>
|
||||
|
||||
using namespace std::chrono;
|
||||
|
||||
namespace Tasking {
|
||||
|
||||
// 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
|
||||
\inmodule QtCreator
|
||||
\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
|
||||
*/
|
||||
|
||||
/*!
|
||||
\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
|
||||
|
||||
@@ -99,7 +246,7 @@ private:
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typealias TaskItem::GroupSetupHandler
|
||||
\typealias GroupItem::GroupSetupHandler
|
||||
|
||||
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()\>.
|
||||
|
||||
@@ -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.
|
||||
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()>
|
||||
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.
|
||||
|
||||
@@ -157,14 +305,14 @@ private:
|
||||
after the storages are constructed, so that the \a handler may already
|
||||
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.
|
||||
The \a handler is invoked whenever the group finishes with success.
|
||||
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.
|
||||
|
||||
@@ -172,9 +320,9 @@ private:
|
||||
before the storages are destructed, so that the \a handler may still
|
||||
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);
|
||||
}
|
||||
@@ -191,9 +339,9 @@ TaskItem onGroupDone(const TaskItem::GroupEndHandler &handler)
|
||||
before the storages are destructed, so that the \a handler may still
|
||||
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);
|
||||
}
|
||||
@@ -239,24 +387,34 @@ TaskItem onGroupError(const TaskItem::GroupEndHandler &handler)
|
||||
|
||||
\sa sequential, parallel
|
||||
*/
|
||||
TaskItem parallelLimit(int limit)
|
||||
GroupItem parallelLimit(int limit)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
const TaskItem sequential = parallelLimit(1);
|
||||
const TaskItem parallel = parallelLimit(0);
|
||||
const TaskItem stopOnError = workflowPolicy(WorkflowPolicy::StopOnError);
|
||||
const TaskItem continueOnError = workflowPolicy(WorkflowPolicy::ContinueOnError);
|
||||
const TaskItem stopOnDone = workflowPolicy(WorkflowPolicy::StopOnDone);
|
||||
const TaskItem continueOnDone = workflowPolicy(WorkflowPolicy::ContinueOnDone);
|
||||
const TaskItem stopOnFinished = workflowPolicy(WorkflowPolicy::StopOnFinished);
|
||||
const TaskItem optional = workflowPolicy(WorkflowPolicy::Optional);
|
||||
const GroupItem sequential = parallelLimit(1);
|
||||
const GroupItem parallel = parallelLimit(0);
|
||||
|
||||
const GroupItem stopOnError = workflowPolicy(WorkflowPolicy::StopOnError);
|
||||
const GroupItem continueOnError = workflowPolicy(WorkflowPolicy::ContinueOnError);
|
||||
const GroupItem stopOnDone = workflowPolicy(WorkflowPolicy::StopOnDone);
|
||||
const GroupItem continueOnDone = workflowPolicy(WorkflowPolicy::ContinueOnDone);
|
||||
const GroupItem stopOnFinished = workflowPolicy(WorkflowPolicy::StopOnFinished);
|
||||
const GroupItem finishAllAndDone = workflowPolicy(WorkflowPolicy::FinishAllAndDone);
|
||||
const GroupItem finishAllAndError = workflowPolicy(WorkflowPolicy::FinishAllAndError);
|
||||
|
||||
static TaskAction toTaskAction(bool success)
|
||||
{
|
||||
@@ -326,11 +484,11 @@ void TreeStorageBase::activateStorage(int id) const
|
||||
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...");
|
||||
return);
|
||||
for (const TaskItem &child : children) {
|
||||
for (const GroupItem &child : children) {
|
||||
switch (child.m_type) {
|
||||
case Type::Group:
|
||||
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) {
|
||||
qWarning("Setting empty Setup Handler is no-op, skipping...");
|
||||
@@ -388,7 +546,7 @@ void TaskItem::setTaskSetupHandler(const TaskSetupHandler &handler)
|
||||
m_taskHandler.m_setupHandler = handler;
|
||||
}
|
||||
|
||||
void TaskItem::setTaskDoneHandler(const TaskEndHandler &handler)
|
||||
void GroupItem::setTaskDoneHandler(const TaskEndHandler &handler)
|
||||
{
|
||||
if (!handler) {
|
||||
qWarning("Setting empty Done Handler is no-op, skipping...");
|
||||
@@ -399,7 +557,7 @@ void TaskItem::setTaskDoneHandler(const TaskEndHandler &handler)
|
||||
m_taskHandler.m_doneHandler = handler;
|
||||
}
|
||||
|
||||
void TaskItem::setTaskErrorHandler(const TaskEndHandler &handler)
|
||||
void GroupItem::setTaskErrorHandler(const TaskEndHandler &handler)
|
||||
{
|
||||
if (!handler) {
|
||||
qWarning("Setting empty Error Handler is no-op, skipping...");
|
||||
@@ -410,6 +568,23 @@ void TaskItem::setTaskErrorHandler(const TaskEndHandler &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 TaskNode;
|
||||
|
||||
@@ -466,7 +641,7 @@ class TaskContainer
|
||||
Q_DISABLE_COPY_MOVE(TaskContainer)
|
||||
|
||||
public:
|
||||
TaskContainer(TaskTreePrivate *taskTreePrivate, const TaskItem &task,
|
||||
TaskContainer(TaskTreePrivate *taskTreePrivate, const GroupItem &task,
|
||||
TaskNode *parentNode, TaskContainer *parentContainer)
|
||||
: m_constData(taskTreePrivate, task, parentNode, parentContainer, this) {}
|
||||
TaskAction start();
|
||||
@@ -479,7 +654,7 @@ public:
|
||||
bool isStarting() const { return isRunning() && m_runtimeData->m_startGuard.isLocked(); }
|
||||
|
||||
struct ConstData {
|
||||
ConstData(TaskTreePrivate *taskTreePrivate, const TaskItem &task, TaskNode *parentNode,
|
||||
ConstData(TaskTreePrivate *taskTreePrivate, const GroupItem &task, TaskNode *parentNode,
|
||||
TaskContainer *parentContainer, TaskContainer *thisContainer);
|
||||
~ConstData() { qDeleteAll(m_children); }
|
||||
TaskTreePrivate * const m_taskTreePrivate = nullptr;
|
||||
@@ -488,7 +663,7 @@ public:
|
||||
|
||||
const int m_parallelLimit = 1;
|
||||
const WorkflowPolicy m_workflowPolicy = WorkflowPolicy::StopOnError;
|
||||
const TaskItem::GroupHandler m_groupHandler;
|
||||
const GroupItem::GroupHandler m_groupHandler;
|
||||
const QList<TreeStorageBase> m_storageList;
|
||||
const QList<TaskNode *> m_children;
|
||||
const int m_taskCount = 0;
|
||||
@@ -505,8 +680,8 @@ public:
|
||||
|
||||
const ConstData &m_constData;
|
||||
const QList<int> m_storageIdList;
|
||||
int m_doneCount = 0;
|
||||
bool m_successBit = true;
|
||||
int m_doneCount = 0;
|
||||
Guard m_startGuard;
|
||||
};
|
||||
|
||||
@@ -519,7 +694,7 @@ class TaskNode
|
||||
Q_DISABLE_COPY_MOVE(TaskNode)
|
||||
|
||||
public:
|
||||
TaskNode(TaskTreePrivate *taskTreePrivate, const TaskItem &task,
|
||||
TaskNode(TaskTreePrivate *taskTreePrivate, const GroupItem &task,
|
||||
TaskContainer *parentContainer)
|
||||
: m_taskHandler(task.taskHandler())
|
||||
, m_container(taskTreePrivate, task, this, parentContainer)
|
||||
@@ -537,7 +712,7 @@ public:
|
||||
TaskTree *taskTree() const { return m_container.m_constData.m_taskTreePrivate->q; }
|
||||
|
||||
private:
|
||||
const TaskItem::TaskHandler m_taskHandler;
|
||||
const GroupItem::TaskHandler m_taskHandler;
|
||||
TaskContainer m_container;
|
||||
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,
|
||||
const TaskItem &task)
|
||||
const GroupItem &task)
|
||||
{
|
||||
QList<TaskNode *> result;
|
||||
const QList<TaskItem> &children = task.children();
|
||||
for (const TaskItem &child : children)
|
||||
const QList<GroupItem> &children = task.children();
|
||||
for (const GroupItem &child : children)
|
||||
result.append(new TaskNode(taskTreePrivate, child, container));
|
||||
return result;
|
||||
}
|
||||
|
||||
TaskContainer::ConstData::ConstData(TaskTreePrivate *taskTreePrivate, const TaskItem &task,
|
||||
TaskContainer::ConstData::ConstData(TaskTreePrivate *taskTreePrivate, const GroupItem &task,
|
||||
TaskNode *parentNode, TaskContainer *parentContainer,
|
||||
TaskContainer *thisContainer)
|
||||
: 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)
|
||||
: m_constData(constData)
|
||||
, m_storageIdList(createStorages(constData))
|
||||
{
|
||||
m_successBit = m_constData.m_workflowPolicy != WorkflowPolicy::StopOnDone
|
||||
&& m_constData.m_workflowPolicy != WorkflowPolicy::ContinueOnDone;
|
||||
}
|
||||
, m_successBit(initialSuccessBit(m_constData.m_workflowPolicy))
|
||||
{}
|
||||
|
||||
TaskContainer::RuntimeData::~RuntimeData()
|
||||
{
|
||||
@@ -720,10 +910,11 @@ TaskContainer::RuntimeData::~RuntimeData()
|
||||
|
||||
bool TaskContainer::RuntimeData::updateSuccessBit(bool success)
|
||||
{
|
||||
if (m_constData.m_workflowPolicy == WorkflowPolicy::Optional)
|
||||
return m_successBit;
|
||||
if (m_constData.m_workflowPolicy == WorkflowPolicy::StopOnFinished) {
|
||||
m_successBit = success;
|
||||
if (m_constData.m_workflowPolicy == WorkflowPolicy::FinishAllAndDone
|
||||
|| m_constData.m_workflowPolicy == WorkflowPolicy::FinishAllAndError
|
||||
|| m_constData.m_workflowPolicy == WorkflowPolicy::StopOnFinished) {
|
||||
if (m_constData.m_workflowPolicy == WorkflowPolicy::StopOnFinished)
|
||||
m_successBit = success;
|
||||
return m_successBit;
|
||||
}
|
||||
|
||||
@@ -753,7 +944,7 @@ TaskAction TaskContainer::start()
|
||||
}
|
||||
if (startAction == TaskAction::Continue) {
|
||||
if (m_constData.m_children.isEmpty())
|
||||
startAction = TaskAction::StopWithDone;
|
||||
startAction = toTaskAction(m_runtimeData->m_successBit);
|
||||
}
|
||||
return continueStart(startAction, 0);
|
||||
}
|
||||
@@ -845,7 +1036,7 @@ void TaskContainer::stop()
|
||||
|
||||
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)
|
||||
invokeHandler(this, groupHandler.m_doneHandler);
|
||||
else if (!m_runtimeData->m_successBit && groupHandler.m_errorHandler)
|
||||
@@ -893,6 +1084,7 @@ void TaskNode::stop()
|
||||
|
||||
if (!m_task) {
|
||||
m_container.stop();
|
||||
m_container.m_runtimeData->updateSuccessBit(false);
|
||||
m_container.invokeEndHandler();
|
||||
return;
|
||||
}
|
||||
@@ -1331,77 +1523,11 @@ void TaskNode::invokeEndHandler(bool success)
|
||||
\section2 Workflow Policy
|
||||
|
||||
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
|
||||
\header
|
||||
\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.
|
||||
If a child of a group is also a group, the child group runs its tasks
|
||||
according to its own workflow policy.
|
||||
|
||||
\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:
|
||||
|
||||
\code
|
||||
static QByteArray load(const FilePath &fileName) { ... }
|
||||
static void save(const FilePath &fileName, const QByteArray &array) { ... }
|
||||
static QByteArray load(const QString &fileName) { ... }
|
||||
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
|
||||
QByteArray content; // [2] custom inter-task data
|
||||
@@ -1448,6 +1574,13 @@ void TaskNode::invokeEndHandler(bool success)
|
||||
};
|
||||
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
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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::errorOccurred, &loop, [finalize] { finalize(false); });
|
||||
start();
|
||||
if (!isRunning())
|
||||
return ok;
|
||||
|
||||
QTimer timer;
|
||||
if (timeoutMs) {
|
||||
timer.setSingleShot(true);
|
||||
timer.setInterval(timeoutMs);
|
||||
connect(&timer, &QTimer::timeout, this, &TaskTree::stop);
|
||||
timer.start();
|
||||
}
|
||||
QTimer::singleShot(0, this, &TaskTree::start);
|
||||
|
||||
loop.exec(QEventLoop::ExcludeUserInputEvents);
|
||||
if (!ok) {
|
||||
@@ -1700,11 +1830,19 @@ bool TaskTree::runBlocking(const QFuture<void> &future, int timeoutMs)
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool TaskTree::runBlocking(int timeoutMs)
|
||||
bool TaskTree::runBlocking(const Group &recipe, milliseconds timeout)
|
||||
{
|
||||
QPromise<void> dummy;
|
||||
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
|
||||
@@ -1749,4 +1887,95 @@ void TaskTreeTaskAdapter::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
|
||||
|
||||
@@ -16,6 +16,8 @@ QT_END_NAMESPACE
|
||||
|
||||
namespace Tasking {
|
||||
|
||||
Q_NAMESPACE_EXPORT(TASKING_EXPORT)
|
||||
|
||||
class ExecutionContextActivator;
|
||||
class TaskContainer;
|
||||
class TaskTreePrivate;
|
||||
@@ -99,16 +101,19 @@ private:
|
||||
// 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.
|
||||
// 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 {
|
||||
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.
|
||||
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.
|
||||
StopOnFinished, // 3 - Stops on first finished child and report its result.
|
||||
Optional // 4 - Reports done after all children finished.
|
||||
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.
|
||||
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.
|
||||
StopOnFinished, // 3 - Stops on first finished child and report its result.
|
||||
FinishAllAndDone, // 4 - Reports done after all children finished.
|
||||
FinishAllAndError // 5 - Reports error after all children finished.
|
||||
};
|
||||
Q_ENUM_NS(WorkflowPolicy);
|
||||
|
||||
enum class TaskAction
|
||||
{
|
||||
@@ -116,8 +121,9 @@ enum class TaskAction
|
||||
StopWithDone,
|
||||
StopWithError
|
||||
};
|
||||
Q_ENUM_NS(TaskAction);
|
||||
|
||||
class TASKING_EXPORT TaskItem
|
||||
class TASKING_EXPORT GroupItem
|
||||
{
|
||||
public:
|
||||
// Internal, provided by QTC_DECLARE_CUSTOM_TASK
|
||||
@@ -150,7 +156,7 @@ public:
|
||||
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; }
|
||||
QList<TreeStorageBase> storageList() const { return m_storageList; }
|
||||
TaskHandler taskHandler() const { return m_taskHandler; }
|
||||
@@ -163,52 +169,59 @@ protected:
|
||||
TaskHandler
|
||||
};
|
||||
|
||||
TaskItem() = default;
|
||||
TaskItem(const GroupData &data)
|
||||
GroupItem() = default;
|
||||
GroupItem(const GroupData &data)
|
||||
: m_type(Type::GroupData)
|
||||
, m_groupData(data) {}
|
||||
TaskItem(const TreeStorageBase &storage)
|
||||
GroupItem(const TreeStorageBase &storage)
|
||||
: m_type(Type::Storage)
|
||||
, m_storageList{storage} {}
|
||||
TaskItem(const TaskHandler &handler)
|
||||
GroupItem(const TaskHandler &handler)
|
||||
: m_type(Type::TaskHandler)
|
||||
, m_taskHandler(handler) {}
|
||||
void addChildren(const QList<TaskItem> &children);
|
||||
void addChildren(const QList<GroupItem> &children);
|
||||
|
||||
void setTaskSetupHandler(const TaskSetupHandler &handler);
|
||||
void setTaskDoneHandler(const TaskEndHandler &handler);
|
||||
void setTaskErrorHandler(const TaskEndHandler &handler);
|
||||
static TaskItem groupHandler(const GroupHandler &handler) { return TaskItem({handler}); }
|
||||
static TaskItem parallelLimit(int limit) { return TaskItem({{}, limit}); }
|
||||
static TaskItem workflowPolicy(WorkflowPolicy policy) { return TaskItem({{}, {}, policy}); }
|
||||
static GroupItem groupHandler(const GroupHandler &handler) { return GroupItem({handler}); }
|
||||
static GroupItem parallelLimit(int limit) { return GroupItem({{}, limit}); }
|
||||
static GroupItem workflowPolicy(WorkflowPolicy policy) { return GroupItem({{}, {}, policy}); }
|
||||
static GroupItem withTimeout(const GroupItem &item, std::chrono::milliseconds timeout,
|
||||
const GroupEndHandler &handler = {});
|
||||
|
||||
private:
|
||||
Type m_type = Type::Group;
|
||||
QList<TaskItem> m_children;
|
||||
QList<GroupItem> m_children;
|
||||
GroupData m_groupData;
|
||||
QList<TreeStorageBase> m_storageList;
|
||||
TaskHandler m_taskHandler;
|
||||
};
|
||||
|
||||
class TASKING_EXPORT Group : public TaskItem
|
||||
class TASKING_EXPORT Group : public GroupItem
|
||||
{
|
||||
public:
|
||||
Group(const QList<TaskItem> &children) { addChildren(children); }
|
||||
Group(std::initializer_list<TaskItem> children) { addChildren(children); }
|
||||
Group(const QList<GroupItem> &children) { addChildren(children); }
|
||||
Group(std::initializer_list<GroupItem> children) { addChildren(children); }
|
||||
|
||||
// GroupData related:
|
||||
template <typename SetupHandler>
|
||||
static TaskItem onGroupSetup(SetupHandler &&handler) {
|
||||
static GroupItem onGroupSetup(SetupHandler &&handler) {
|
||||
return groupHandler({wrapGroupSetup(std::forward<SetupHandler>(handler))});
|
||||
}
|
||||
static TaskItem onGroupDone(const GroupEndHandler &handler) {
|
||||
static GroupItem onGroupDone(const GroupEndHandler &handler) {
|
||||
return groupHandler({{}, handler});
|
||||
}
|
||||
static TaskItem onGroupError(const GroupEndHandler &handler) {
|
||||
static GroupItem onGroupError(const GroupEndHandler &handler) {
|
||||
return groupHandler({{}, {}, handler});
|
||||
}
|
||||
using TaskItem::parallelLimit; // Default: 1 (sequential). 0 means unlimited (parallel).
|
||||
using TaskItem::workflowPolicy; // Default: WorkflowPolicy::StopOnError.
|
||||
using GroupItem::parallelLimit; // Default: 1 (sequential). 0 means unlimited (parallel).
|
||||
using GroupItem::workflowPolicy; // Default: WorkflowPolicy::StopOnError.
|
||||
|
||||
GroupItem withTimeout(std::chrono::milliseconds timeout,
|
||||
const GroupEndHandler &handler = {}) const {
|
||||
return GroupItem::withTimeout(*this, timeout, handler);
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename SetupHandler>
|
||||
@@ -231,29 +244,31 @@ private:
|
||||
};
|
||||
|
||||
template <typename SetupHandler>
|
||||
static TaskItem onGroupSetup(SetupHandler &&handler)
|
||||
static GroupItem onGroupSetup(SetupHandler &&handler)
|
||||
{
|
||||
return Group::onGroupSetup(std::forward<SetupHandler>(handler));
|
||||
}
|
||||
|
||||
TASKING_EXPORT TaskItem onGroupDone(const TaskItem::GroupEndHandler &handler);
|
||||
TASKING_EXPORT TaskItem onGroupError(const TaskItem::GroupEndHandler &handler);
|
||||
TASKING_EXPORT TaskItem parallelLimit(int limit);
|
||||
TASKING_EXPORT TaskItem workflowPolicy(WorkflowPolicy policy);
|
||||
TASKING_EXPORT GroupItem onGroupDone(const GroupItem::GroupEndHandler &handler);
|
||||
TASKING_EXPORT GroupItem onGroupError(const GroupItem::GroupEndHandler &handler);
|
||||
TASKING_EXPORT GroupItem parallelLimit(int limit);
|
||||
TASKING_EXPORT GroupItem workflowPolicy(WorkflowPolicy policy);
|
||||
|
||||
TASKING_EXPORT extern const TaskItem sequential;
|
||||
TASKING_EXPORT extern const TaskItem 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;
|
||||
TASKING_EXPORT extern const GroupItem sequential;
|
||||
TASKING_EXPORT extern const GroupItem parallel;
|
||||
|
||||
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:
|
||||
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()
|
||||
@@ -266,7 +281,7 @@ public:
|
||||
|
||||
private:
|
||||
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>>;
|
||||
static_assert(isInvocable,
|
||||
"Sync element: The synchronous function can't take any arguments.");
|
||||
@@ -295,17 +310,17 @@ private:
|
||||
};
|
||||
|
||||
template <typename Adapter>
|
||||
class CustomTask : public TaskItem
|
||||
class CustomTask : public GroupItem
|
||||
{
|
||||
public:
|
||||
using Task = typename Adapter::Type;
|
||||
using EndHandler = std::function<void(const Task &)>;
|
||||
static Adapter *createAdapter() { return new Adapter; }
|
||||
CustomTask() : TaskItem({&createAdapter}) {}
|
||||
CustomTask() : GroupItem({&createAdapter}) {}
|
||||
template <typename SetupFunction>
|
||||
CustomTask(SetupFunction &&function, const EndHandler &done = {}, const EndHandler &error = {})
|
||||
: TaskItem({&createAdapter, wrapSetup(std::forward<SetupFunction>(function)),
|
||||
wrapEnd(done), wrapEnd(error)}) {}
|
||||
: GroupItem({&createAdapter, wrapSetup(std::forward<SetupFunction>(function)),
|
||||
wrapEnd(done), wrapEnd(error)}) {}
|
||||
|
||||
template <typename SetupFunction>
|
||||
CustomTask &onSetup(SetupFunction &&function) {
|
||||
@@ -321,9 +336,14 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
GroupItem withTimeout(std::chrono::milliseconds timeout,
|
||||
const GroupEndHandler &handler = {}) const {
|
||||
return GroupItem::withTimeout(*this, timeout, handler);
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename SetupFunction>
|
||||
static TaskItem::TaskSetupHandler wrapSetup(SetupFunction &&function) {
|
||||
static GroupItem::TaskSetupHandler wrapSetup(SetupFunction &&function) {
|
||||
static constexpr bool isDynamic = std::is_same_v<TaskAction,
|
||||
std::invoke_result_t<std::decay_t<SetupFunction>, typename Adapter::Type &>>;
|
||||
constexpr bool isVoid = std::is_same_v<void,
|
||||
@@ -370,8 +390,12 @@ public:
|
||||
// Helper methods. They execute a local event loop with ExcludeUserInputEvents.
|
||||
// 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.
|
||||
bool runBlocking(const QFuture<void> &future, int timeoutMs = 0);
|
||||
bool runBlocking(int timeoutMs = 0);
|
||||
bool runBlocking();
|
||||
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 progressMaximum() const { return taskCount(); }
|
||||
@@ -418,6 +442,17 @@ public:
|
||||
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
|
||||
|
||||
#define TASKING_DECLARE_TASK(CustomTaskName, TaskAdapterClass)\
|
||||
@@ -430,3 +465,4 @@ using CustomTaskName = CustomTask<TaskAdapterClass<Args...>>;\
|
||||
} // namespace Tasking
|
||||
|
||||
TASKING_DECLARE_TASK(TaskTreeTask, TaskTreeTaskAdapter);
|
||||
TASKING_DECLARE_TASK(TimeoutTask, TimeoutTaskAdapter);
|
||||
|
||||
@@ -670,6 +670,7 @@ public:
|
||||
// Used to block recursive editingFinished signals for example when return is pressed, and
|
||||
// the validation changes focus by opening a dialog
|
||||
bool m_blockAutoApply = false;
|
||||
bool m_allowPathFromDevice = true;
|
||||
|
||||
template<class Widget> void updateWidgetFromCheckStatus(StringAspect *aspect, Widget *w)
|
||||
{
|
||||
@@ -977,6 +978,13 @@ void StringAspect::setCommandVersionArguments(const QStringList &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.
|
||||
*/
|
||||
@@ -1122,6 +1130,7 @@ void StringAspect::addToLayout(LayoutItem &parent)
|
||||
d->m_pathChooserDisplay->setPromptDialogFilter(d->m_prompDialogFilter);
|
||||
d->m_pathChooserDisplay->setPromptDialogTitle(d->m_prompDialogTitle);
|
||||
d->m_pathChooserDisplay->setCommandVersionArguments(d->m_commandVersionArguments);
|
||||
d->m_pathChooserDisplay->setAllowPathFromDevice(d->m_allowPathFromDevice);
|
||||
if (defaultValue() == value())
|
||||
d->m_pathChooserDisplay->setDefaultValue(defaultValue());
|
||||
else
|
||||
@@ -2153,8 +2162,11 @@ void DoubleAspect::setSingleStep(double step)
|
||||
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)
|
||||
: SelectionAspect(container)
|
||||
{
|
||||
setDisplayStyle(DisplayStyle::ComboBox);
|
||||
setDefaultValue(TriState::Default);
|
||||
@@ -2308,7 +2320,7 @@ QList<int> IntegersAspect::value() const
|
||||
|
||||
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
|
||||
@@ -2319,7 +2331,7 @@ QList<int> IntegersAspect::defaultValue() const
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
TextDisplay::TextDisplay(AspectContainer *container)
|
||||
: BaseAspect(container)
|
||||
{}
|
||||
|
||||
/*!
|
||||
Constructs a text display showing the \a message with an icon representing
|
||||
type \a type.
|
||||
|
||||
@@ -406,6 +406,7 @@ public:
|
||||
void setOpenTerminalHandler(const std::function<void()> &openTerminal);
|
||||
void setAutoApplyOnEditingFinished(bool applyOnEditingFinished);
|
||||
void setElideMode(Qt::TextElideMode elideMode);
|
||||
void setAllowPathFromDevice(bool allowPathFromDevice);
|
||||
|
||||
void validateInput();
|
||||
|
||||
@@ -548,7 +549,8 @@ class QTCREATOR_UTILS_EXPORT TriStateAspect : public SelectionAspect
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TriStateAspect(const QString &onString = {},
|
||||
TriStateAspect(AspectContainer *container = nullptr,
|
||||
const QString &onString = {},
|
||||
const QString &offString = {},
|
||||
const QString &defaultString = {});
|
||||
|
||||
@@ -608,6 +610,7 @@ class QTCREATOR_UTILS_EXPORT TextDisplay : public BaseAspect
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TextDisplay(AspectContainer *container);
|
||||
TextDisplay(const QString &message = {},
|
||||
InfoLabel::InfoType type = InfoLabel::None);
|
||||
~TextDisplay() override;
|
||||
|
||||
@@ -493,6 +493,20 @@ bool DesktopDeviceFileAccess::hasHardLinks(const FilePath &filePath) const
|
||||
if (s.st_nlink > 1)
|
||||
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
|
||||
Q_UNUSED(filePath)
|
||||
#endif
|
||||
|
||||
@@ -104,7 +104,7 @@ RunResult DeviceShell::run(const CommandLine &cmd, const QByteArray &stdInData)
|
||||
|
||||
QWaitCondition waiter;
|
||||
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] {
|
||||
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);
|
||||
|
||||
const auto it = m_commandOutput.constFind(id);
|
||||
const RunResult result = *it;
|
||||
m_commandOutput.erase(it);
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#include "fileutils.h"
|
||||
|
||||
#include <QMap>
|
||||
#include <QHash>
|
||||
#include <QMutex>
|
||||
#include <QProcess>
|
||||
#include <QThread>
|
||||
@@ -78,8 +78,7 @@ private:
|
||||
int m_currentId{0};
|
||||
|
||||
QMutex m_commandMutex;
|
||||
// QMap is used here to preserve iterators
|
||||
QMap<quint64, CommandRun> m_commandOutput;
|
||||
QHash<quint64, CommandRun> m_commandOutput;
|
||||
QByteArray m_commandBuffer;
|
||||
|
||||
State m_shellScriptState = State::Unknown;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "utilsicons.h"
|
||||
#include "utilstr.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QKeyEvent>
|
||||
#include <QKeySequence>
|
||||
#include <QMenu>
|
||||
@@ -126,7 +127,7 @@ FancyLineEditPrivate::FancyLineEditPrivate(FancyLineEdit *parent) :
|
||||
m_completionShortcut(completionShortcut()->key(), parent),
|
||||
m_okTextColor(creatorTheme()->color(Theme::TextColorNormal)),
|
||||
m_errorTextColor(creatorTheme()->color(Theme::TextColorError)),
|
||||
m_placeholderTextColor(creatorTheme()->color(Theme::PalettePlaceholderText))
|
||||
m_placeholderTextColor(QApplication::palette().color(QPalette::PlaceholderText))
|
||||
|
||||
{
|
||||
m_completionShortcut.setContext(Qt::WidgetShortcut);
|
||||
|
||||
@@ -1914,7 +1914,7 @@ FilePath FilePath::canonicalPath() const
|
||||
return *this;
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
#ifdef Q_OS_WIN
|
||||
DWORD flagsAndAttrs = FILE_ATTRIBUTE_NORMAL;
|
||||
if (isDir())
|
||||
flagsAndAttrs |= FILE_FLAG_BACKUP_SEMANTICS;
|
||||
|
||||
@@ -30,7 +30,7 @@ public:
|
||||
void start() {
|
||||
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}));
|
||||
const auto finalize = [this](bool success) {
|
||||
m_taskTree.release()->deleteLater();
|
||||
@@ -49,8 +49,8 @@ protected:
|
||||
std::unique_ptr<TaskTree> m_taskTree;
|
||||
|
||||
private:
|
||||
virtual TaskItem remoteTask() = 0;
|
||||
virtual TaskItem localTask() = 0;
|
||||
virtual GroupItem remoteTask() = 0;
|
||||
virtual GroupItem localTask() = 0;
|
||||
};
|
||||
|
||||
static void localRead(QPromise<QByteArray> &promise, const FilePath &filePath)
|
||||
@@ -84,7 +84,7 @@ signals:
|
||||
void readyRead(const QByteArray &newData);
|
||||
|
||||
private:
|
||||
TaskItem remoteTask() final {
|
||||
GroupItem remoteTask() final {
|
||||
const auto setup = [this](Process &process) {
|
||||
const QStringList args = {"if=" + m_filePath.path()};
|
||||
const FilePath dd = m_filePath.withNewPath("dd");
|
||||
@@ -96,7 +96,7 @@ private:
|
||||
};
|
||||
return ProcessTask(setup);
|
||||
}
|
||||
TaskItem localTask() final {
|
||||
GroupItem localTask() final {
|
||||
const auto setup = [this](Async<QByteArray> &async) {
|
||||
async.setConcurrentCallData(localRead, m_filePath);
|
||||
Async<QByteArray> *asyncPtr = &async;
|
||||
@@ -251,7 +251,7 @@ signals:
|
||||
void started();
|
||||
|
||||
private:
|
||||
TaskItem remoteTask() final {
|
||||
GroupItem remoteTask() final {
|
||||
const auto setup = [this](Process &process) {
|
||||
m_writeBuffer = new WriteBuffer(false, &process);
|
||||
connect(m_writeBuffer, &WriteBuffer::writeRequested, &process, &Process::writeRaw);
|
||||
@@ -272,7 +272,7 @@ private:
|
||||
};
|
||||
return ProcessTask(setup, finalize, finalize);
|
||||
}
|
||||
TaskItem localTask() final {
|
||||
GroupItem localTask() final {
|
||||
const auto setup = [this](Async<void> &async) {
|
||||
m_writeBuffer = new WriteBuffer(isBuffered(), &async);
|
||||
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())
|
||||
return;
|
||||
|
||||
TaskTree taskTree(transferTask(source, destination));
|
||||
if (!taskTree.runBlocking(promise.future()))
|
||||
if (!TaskTree::runBlocking(transferTask(source, destination), promise.future()))
|
||||
promise.future().cancel();
|
||||
}
|
||||
|
||||
@@ -391,7 +390,7 @@ public:
|
||||
StreamResult m_streamResult = StreamResult::FinishedWithError;
|
||||
std::unique_ptr<TaskTree> m_taskTree;
|
||||
|
||||
TaskItem task() {
|
||||
GroupItem task() {
|
||||
if (m_streamerMode == StreamMode::Reader)
|
||||
return readerTask();
|
||||
if (m_streamerMode == StreamMode::Writer)
|
||||
@@ -400,7 +399,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
TaskItem readerTask() {
|
||||
GroupItem readerTask() {
|
||||
const auto setup = [this](FileStreamReader &reader) {
|
||||
m_readBuffer.clear();
|
||||
reader.setFilePath(m_source);
|
||||
@@ -410,14 +409,14 @@ private:
|
||||
};
|
||||
return FileStreamReaderTask(setup);
|
||||
}
|
||||
TaskItem writerTask() {
|
||||
GroupItem writerTask() {
|
||||
const auto setup = [this](FileStreamWriter &writer) {
|
||||
writer.setFilePath(m_destination);
|
||||
writer.setWriteData(m_writeBuffer);
|
||||
};
|
||||
return FileStreamWriterTask(setup);
|
||||
}
|
||||
TaskItem transferTask() {
|
||||
GroupItem transferTask() {
|
||||
const auto setup = [this](Async<void> &async) {
|
||||
async.setConcurrentCallData(transfer, m_source, m_destination);
|
||||
};
|
||||
|
||||
@@ -629,12 +629,18 @@ FilePathInfo::FileFlags fileInfoFlagsfromStatMode(const QString &hexString, int
|
||||
|
||||
FilePathInfo::FileFlags result;
|
||||
|
||||
if (mode & IRUSR)
|
||||
if (mode & IRUSR) {
|
||||
result |= FilePathInfo::ReadOwnerPerm;
|
||||
if (mode & IWUSR)
|
||||
result |= FilePathInfo::ReadUserPerm;
|
||||
}
|
||||
if (mode & IWUSR) {
|
||||
result |= FilePathInfo::WriteOwnerPerm;
|
||||
if (mode & IXUSR)
|
||||
result |= FilePathInfo::WriteUserPerm;
|
||||
}
|
||||
if (mode & IXUSR) {
|
||||
result |= FilePathInfo::ExeOwnerPerm;
|
||||
result |= FilePathInfo::ExeUserPerm;
|
||||
}
|
||||
if (mode & IRGRP)
|
||||
result |= FilePathInfo::ReadGroupPerm;
|
||||
if (mode & IWGRP)
|
||||
|
||||
@@ -676,24 +676,22 @@ LayoutItem st()
|
||||
|
||||
LayoutItem noMargin()
|
||||
{
|
||||
LayoutItem item;
|
||||
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;
|
||||
return customMargin({});
|
||||
}
|
||||
|
||||
LayoutItem normalMargin()
|
||||
{
|
||||
return customMargin({9, 9, 9, 9});
|
||||
}
|
||||
|
||||
LayoutItem customMargin(const QMargins &margin)
|
||||
{
|
||||
LayoutItem item;
|
||||
item.onAdd = [](LayoutBuilder &builder) {
|
||||
item.onAdd = [margin](LayoutBuilder &builder) {
|
||||
if (auto layout = builder.stack.last().layout)
|
||||
layout->setContentsMargins(9, 9, 9, 9);
|
||||
layout->setContentsMargins(margin);
|
||||
else if (auto widget = builder.stack.last().widget)
|
||||
widget->setContentsMargins(9, 9, 9, 9);
|
||||
widget->setContentsMargins(margin);
|
||||
};
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -11,12 +11,15 @@
|
||||
|
||||
#if defined(UTILS_LIBRARY)
|
||||
# define QTCREATOR_UTILS_EXPORT Q_DECL_EXPORT
|
||||
#elif defined(UTILS_STATIC_LIBRARY)
|
||||
# define QTCREATOR_UTILS_EXPORT
|
||||
#else
|
||||
# define QTCREATOR_UTILS_EXPORT Q_DECL_IMPORT
|
||||
#endif
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QLayout;
|
||||
class QMargins;
|
||||
class QObject;
|
||||
class QWidget;
|
||||
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 noMargin();
|
||||
QTCREATOR_UTILS_EXPORT LayoutItem normalMargin();
|
||||
QTCREATOR_UTILS_EXPORT LayoutItem customMargin(const QMargins &margin);
|
||||
QTCREATOR_UTILS_EXPORT LayoutItem withFormAlignment();
|
||||
|
||||
// "Setters"
|
||||
|
||||
@@ -133,9 +133,13 @@ TextTip::TextTip(QWidget *parent) : TipLabel(parent)
|
||||
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)
|
||||
@@ -148,13 +152,13 @@ void TextTip::setContent(const QVariant &content)
|
||||
m_format = item.second;
|
||||
}
|
||||
|
||||
bool containsLink = likelyContainsLink(m_text);
|
||||
bool containsLink = likelyContainsLink(m_text, m_format);
|
||||
setOpenExternalLinks(containsLink);
|
||||
}
|
||||
|
||||
bool TextTip::isInteractive() const
|
||||
{
|
||||
return likelyContainsLink(m_text);
|
||||
return likelyContainsLink(m_text, m_format);
|
||||
}
|
||||
|
||||
void TextTip::configure(const QPoint &pos)
|
||||
|
||||
@@ -122,7 +122,7 @@ private:
|
||||
QMap<QString, FilePath> m_filesToPull;
|
||||
|
||||
QStringList m_androidABIs;
|
||||
BoolAspect *m_uninstallPreviousPackage = nullptr;
|
||||
BoolAspect m_uninstallPreviousPackage{this};
|
||||
bool m_uninstallPreviousPackageRun = false;
|
||||
bool m_useAndroiddeployqt = false;
|
||||
bool m_askForUninstall = false;
|
||||
@@ -143,17 +143,16 @@ AndroidDeployQtStep::AndroidDeployQtStep(BuildStepList *parent, Id id)
|
||||
setImmutable(true);
|
||||
setUserExpanded(true);
|
||||
|
||||
m_uninstallPreviousPackage = addAspect<BoolAspect>();
|
||||
m_uninstallPreviousPackage->setSettingsKey(UninstallPreviousPackageKey);
|
||||
m_uninstallPreviousPackage->setLabel(Tr::tr("Uninstall the existing app before deployment"),
|
||||
m_uninstallPreviousPackage.setSettingsKey(UninstallPreviousPackageKey);
|
||||
m_uninstallPreviousPackage.setLabel(Tr::tr("Uninstall the existing app before deployment"),
|
||||
BoolAspect::LabelPlacement::AtCheckBox);
|
||||
m_uninstallPreviousPackage->setValue(false);
|
||||
m_uninstallPreviousPackage.setValue(false);
|
||||
|
||||
const QtSupport::QtVersion * const qt = QtSupport::QtKitAspect::qtVersion(kit());
|
||||
const bool forced = qt && qt->qtVersion() < QVersionNumber(5, 4, 0);
|
||||
if (forced) {
|
||||
m_uninstallPreviousPackage->setValue(true);
|
||||
m_uninstallPreviousPackage->setEnabled(false);
|
||||
m_uninstallPreviousPackage.setValue(true);
|
||||
m_uninstallPreviousPackage.setEnabled(false);
|
||||
}
|
||||
|
||||
connect(this, &AndroidDeployQtStep::askForUninstall,
|
||||
@@ -274,7 +273,7 @@ bool AndroidDeployQtStep::init()
|
||||
|
||||
emit addOutput(Tr::tr("Deploying to %1").arg(m_serialNumber), OutputFormat::NormalMessage);
|
||||
|
||||
m_uninstallPreviousPackageRun = m_uninstallPreviousPackage->value();
|
||||
m_uninstallPreviousPackageRun = m_uninstallPreviousPackage();
|
||||
if (m_uninstallPreviousPackageRun)
|
||||
m_manifestName = AndroidManager::manifestPath(target());
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@ CTestSettings::CTestSettings(Id settingsId)
|
||||
setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY);
|
||||
setDisplayName(Tr::tr("CTest"));
|
||||
|
||||
setLayouter([this](QWidget *w) {
|
||||
Row { Form {
|
||||
setLayouter([this] {
|
||||
return Row { Form {
|
||||
outputOnFail, br,
|
||||
scheduleRandom, br,
|
||||
stopOnFailure, br,
|
||||
@@ -39,7 +39,7 @@ CTestSettings::CTestSettings(Id settingsId)
|
||||
Row { testLoad, threshold}
|
||||
}
|
||||
}
|
||||
}, st }.attachTo(w);
|
||||
}, st };
|
||||
});
|
||||
|
||||
outputOnFail.setSettingsKey("OutputOnFail");
|
||||
|
||||
@@ -35,7 +35,7 @@ using namespace ProjectExplorer;
|
||||
static bool isProjectParsing()
|
||||
{
|
||||
const BuildSystem *bs = ProjectManager::startupBuildSystem();
|
||||
return bs && bs->isParsing();
|
||||
return bs && (bs->isParsing() || bs->isWaitingForParse());
|
||||
}
|
||||
|
||||
TestCodeParser::TestCodeParser()
|
||||
@@ -360,7 +360,7 @@ void TestCodeParser::scanForTests(const QSet<FilePath> &filePaths,
|
||||
|
||||
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) {
|
||||
const auto setup = [this, codeParsers, file](Async<TestParseResultPtr> &async) {
|
||||
async.setConcurrentCallData(parseFileForTests, codeParsers, file);
|
||||
|
||||
@@ -347,7 +347,7 @@ void TestRunner::runTestsHelper()
|
||||
std::unique_ptr<TestOutputReader> m_outputReader;
|
||||
};
|
||||
|
||||
QList<TaskItem> tasks{optional};
|
||||
QList<GroupItem> tasks{finishAllAndDone};
|
||||
|
||||
for (ITestConfiguration *config : m_selectedTests) {
|
||||
QTC_ASSERT(config, continue);
|
||||
@@ -442,7 +442,7 @@ void TestRunner::runTestsHelper()
|
||||
}
|
||||
};
|
||||
const Group group {
|
||||
optional,
|
||||
finishAllAndDone,
|
||||
Storage(storage),
|
||||
onGroupSetup(onSetup),
|
||||
ProcessTask(onProcessSetup, onProcessDone, onProcessDone)
|
||||
|
||||
@@ -42,23 +42,23 @@ private:
|
||||
void doRun() final;
|
||||
|
||||
bool m_runAutogen = false;
|
||||
StringAspect m_arguments{this};
|
||||
};
|
||||
|
||||
AutogenStep::AutogenStep(BuildStepList *bsl, Id id) : AbstractProcessStep(bsl, id)
|
||||
{
|
||||
auto arguments = addAspect<StringAspect>();
|
||||
arguments->setSettingsKey("AutotoolsProjectManager.AutogenStep.AdditionalArguments");
|
||||
arguments->setLabelText(Tr::tr("Arguments:"));
|
||||
arguments->setDisplayStyle(StringAspect::LineEditDisplay);
|
||||
arguments->setHistoryCompleter("AutotoolsPM.History.AutogenStepArgs");
|
||||
m_arguments.setSettingsKey("AutotoolsProjectManager.AutogenStep.AdditionalArguments");
|
||||
m_arguments.setLabelText(Tr::tr("Arguments:"));
|
||||
m_arguments.setDisplayStyle(StringAspect::LineEditDisplay);
|
||||
m_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(); });
|
||||
|
||||
setCommandLineProvider([this, arguments] {
|
||||
setCommandLineProvider([this] {
|
||||
return CommandLine(project()->projectDirectory() / "autogen.sh",
|
||||
arguments->value(),
|
||||
m_arguments(),
|
||||
CommandLine::Raw);
|
||||
});
|
||||
|
||||
|
||||
@@ -66,10 +66,10 @@ BazaarSettings::BazaarSettings()
|
||||
timeout.setLabelText(Tr::tr("Timeout:"));
|
||||
timeout.setSuffix(Tr::tr("s"));
|
||||
|
||||
setLayouter([this](QWidget *widget) {
|
||||
setLayouter([this] {
|
||||
using namespace Layouting;
|
||||
|
||||
Column {
|
||||
return Column {
|
||||
Group {
|
||||
title(Tr::tr("Configuration")),
|
||||
Row { binaryPath }
|
||||
@@ -88,7 +88,7 @@ BazaarSettings::BazaarSettings()
|
||||
Row { logCount, timeout, st }
|
||||
},
|
||||
st
|
||||
}.attachTo(widget);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -297,7 +297,7 @@ void AbstractSettings::read()
|
||||
|
||||
void AbstractSettings::readDocumentation()
|
||||
{
|
||||
const FilePath filename = documentationFilePath();
|
||||
const FilePath filename = documentationFilePath;
|
||||
if (filename.isEmpty()) {
|
||||
BeautifierPlugin::showError(Tr::tr("No documentation file specified."));
|
||||
return;
|
||||
|
||||
@@ -51,7 +51,8 @@ public:
|
||||
|
||||
Utils::FilePathAspect command{this};
|
||||
Utils::StringAspect supportedMimeTypes{this};
|
||||
Utils::FilePathAspect documentationFilePath; // Intentionally not saved.
|
||||
|
||||
Utils::FilePath documentationFilePath;
|
||||
|
||||
QVersionNumber version() const;
|
||||
|
||||
|
||||
@@ -59,11 +59,11 @@ ArtisticStyleSettings::ArtisticStyleSettings()
|
||||
|
||||
customStyle.setSettingsKey("customStyle");
|
||||
|
||||
documentationFilePath.setFilePath(
|
||||
documentationFilePath =
|
||||
Core::ICore::userResourcePath(Beautifier::Constants::SETTINGS_DIRNAME)
|
||||
.pathAppended(Beautifier::Constants::DOCUMENTATION_DIRNAME)
|
||||
.pathAppended(SETTINGS_NAME)
|
||||
.stringAppended(".xml"));
|
||||
.stringAppended(".xml");
|
||||
|
||||
read();
|
||||
}
|
||||
@@ -77,7 +77,7 @@ void ArtisticStyleSettings::createDocumentationFile() const
|
||||
if (process.result() != ProcessResult::FinishedWithSuccess)
|
||||
return;
|
||||
|
||||
QFile file(documentationFilePath().toFSPathString());
|
||||
QFile file(documentationFilePath.toFSPathString());
|
||||
const QFileInfo fi(file);
|
||||
if (!fi.exists())
|
||||
fi.dir().mkpath(fi.absolutePath());
|
||||
|
||||
@@ -63,16 +63,16 @@ ClangFormatSettings::ClangFormatSettings()
|
||||
|
||||
customStyle.setSettingsKey("customStyle");
|
||||
|
||||
documentationFilePath.setFilePath(Core::ICore::userResourcePath(Constants::SETTINGS_DIRNAME)
|
||||
documentationFilePath = Core::ICore::userResourcePath(Constants::SETTINGS_DIRNAME)
|
||||
.pathAppended(Constants::DOCUMENTATION_DIRNAME)
|
||||
.pathAppended(SETTINGS_NAME).stringAppended(".xml"));
|
||||
.pathAppended(SETTINGS_NAME).stringAppended(".xml");
|
||||
|
||||
read();
|
||||
}
|
||||
|
||||
void ClangFormatSettings::createDocumentationFile() const
|
||||
{
|
||||
QFile file(documentationFilePath().toFSPathString());
|
||||
QFile file(documentationFilePath.toFSPathString());
|
||||
const QFileInfo fi(file);
|
||||
if (!fi.exists())
|
||||
fi.dir().mkpath(fi.absolutePath());
|
||||
|
||||
@@ -66,9 +66,9 @@ UncrustifySettings::UncrustifySettings()
|
||||
specificConfigFile.setExpectedKind(Utils::PathChooser::File);
|
||||
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(SETTINGS_NAME).stringAppended(".xml"));
|
||||
.pathAppended(SETTINGS_NAME).stringAppended(".xml");
|
||||
|
||||
read();
|
||||
}
|
||||
@@ -82,7 +82,7 @@ void UncrustifySettings::createDocumentationFile() const
|
||||
if (process.result() != ProcessResult::FinishedWithSuccess)
|
||||
return;
|
||||
|
||||
QFile file(documentationFilePath().toFSPathString());
|
||||
QFile file(documentationFilePath.toFSPathString());
|
||||
const QFileInfo fi(file);
|
||||
if (!fi.exists())
|
||||
fi.dir().mkpath(fi.absolutePath());
|
||||
|
||||
@@ -421,8 +421,12 @@ void doSemanticHighlighting(
|
||||
if (ClangdClient * const client = ClangModelManagerSupport::clientForFile(filePath))
|
||||
client->setVirtualRanges(filePath, virtualRanges, docRevision);
|
||||
}, Qt::QueuedConnection);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
|
||||
promise.addResults(results);
|
||||
#else
|
||||
for (const HighlightingResult &r : results)
|
||||
promise.addResult(r);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/messagemanager.h>
|
||||
#include <coreplugin/session.h>
|
||||
#include <coreplugin/vcsmanager.h>
|
||||
|
||||
#include <cppeditor/cppcodemodelsettings.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.
|
||||
// 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.
|
||||
// We also look for repository changes explicitly.
|
||||
void ClangModelManagerSupport::watchForExternalChanges()
|
||||
{
|
||||
connect(DocumentManager::instance(), &DocumentManager::filesChangedExternally,
|
||||
@@ -709,6 +711,23 @@ void ClangModelManagerSupport::watchForExternalChanges()
|
||||
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
|
||||
|
||||
@@ -183,7 +183,7 @@ void ClangToolRunWorker::start()
|
||||
m_filesAnalyzed.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)) {
|
||||
if (!m_diagnosticConfig.isEnabled(tool)
|
||||
&& !m_runSettings.hasConfigFileForSourceFile(unit.file)) {
|
||||
|
||||
@@ -101,9 +101,9 @@ static FilePath createOutputFilePath(const FilePath &dirPath, const FilePath &fi
|
||||
return {};
|
||||
}
|
||||
|
||||
TaskItem clangToolTask(const AnalyzeInputData &input,
|
||||
const AnalyzeSetupHandler &setupHandler,
|
||||
const AnalyzeOutputHandler &outputHandler)
|
||||
GroupItem clangToolTask(const AnalyzeInputData &input,
|
||||
const AnalyzeSetupHandler &setupHandler,
|
||||
const AnalyzeOutputHandler &outputHandler)
|
||||
{
|
||||
struct ClangToolStorage {
|
||||
QString name;
|
||||
@@ -186,7 +186,7 @@ TaskItem clangToolTask(const AnalyzeInputData &input,
|
||||
Storage(storage),
|
||||
onGroupSetup(onSetup),
|
||||
Group {
|
||||
optional,
|
||||
finishAllAndDone,
|
||||
ProcessTask(onProcessSetup, onProcessDone, onProcessError)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
#include <utils/environment.h>
|
||||
|
||||
namespace Tasking { class TaskItem; }
|
||||
namespace Tasking { class GroupItem; }
|
||||
|
||||
namespace ClangTools {
|
||||
namespace Internal {
|
||||
@@ -50,9 +50,9 @@ struct AnalyzeOutputData
|
||||
using AnalyzeSetupHandler = std::function<bool()>;
|
||||
using AnalyzeOutputHandler = std::function<void(const AnalyzeOutputData &)>;
|
||||
|
||||
Tasking::TaskItem clangToolTask(const AnalyzeInputData &input,
|
||||
const AnalyzeSetupHandler &setupHandler,
|
||||
const AnalyzeOutputHandler &outputHandler);
|
||||
Tasking::GroupItem clangToolTask(const AnalyzeInputData &input,
|
||||
const AnalyzeSetupHandler &setupHandler,
|
||||
const AnalyzeOutputHandler &outputHandler);
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangTools
|
||||
|
||||
@@ -190,7 +190,7 @@ void DocumentClangToolRunner::run()
|
||||
vfso().update();
|
||||
const ClangDiagnosticConfig config = diagnosticConfig(runSettings.diagnosticConfigId());
|
||||
const Environment env = projectBuildEnvironment(project);
|
||||
QList<TaskItem> tasks{parallel};
|
||||
QList<GroupItem> tasks{parallel};
|
||||
const auto addClangTool = [this, &runSettings, &config, &env, &tasks](ClangToolType tool) {
|
||||
if (!toolEnabled(tool, config, runSettings))
|
||||
return;
|
||||
@@ -209,7 +209,7 @@ void DocumentClangToolRunner::run()
|
||||
return !m_document->isModified() || isVFSOverlaySupported(executable);
|
||||
};
|
||||
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::Clazy);
|
||||
|
||||
@@ -378,7 +378,7 @@ void Manager::gotoLocations(const QList<QVariant> &list)
|
||||
int line;
|
||||
int 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) {
|
||||
// we already are at the symbol, cycle to next location
|
||||
++it;
|
||||
|
||||
@@ -227,6 +227,10 @@ private:
|
||||
Q_INVOKABLE void updateStatusActions();
|
||||
|
||||
QString commitDisplayName() const final;
|
||||
QString commitAbortTitle() const final;
|
||||
QString commitAbortMessage() const final;
|
||||
QString commitErrorMessage(const QString &error) const final;
|
||||
|
||||
void checkOutCurrentFile();
|
||||
void addCurrentFile();
|
||||
void undoCheckOutCurrent();
|
||||
@@ -948,9 +952,27 @@ void ClearCasePluginPrivate::updateActions(VcsBasePluginPrivate::ActionState as)
|
||||
|
||||
QString ClearCasePluginPrivate::commitDisplayName() const
|
||||
{
|
||||
//: Name of the "commit" action of the VCS
|
||||
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()
|
||||
{
|
||||
const VcsBasePluginState state = currentState();
|
||||
|
||||
@@ -157,7 +157,7 @@ void CMakeEditorWidget::findLinkAt(const QTextCursor &cursor,
|
||||
|
||||
// find the beginning of a filename
|
||||
QString buffer;
|
||||
int beginPos = column - 1;
|
||||
int beginPos = column;
|
||||
while (beginPos >= 0) {
|
||||
if (isValidFileNameChar(block, beginPos)) {
|
||||
buffer.prepend(block.at(beginPos));
|
||||
|
||||
@@ -65,9 +65,9 @@ public:
|
||||
autoFormatMime.setDefaultValue("text/x-cmake");
|
||||
autoFormatMime.setLabelText(Tr::tr("Restrict to MIME types:"));
|
||||
|
||||
setLayouter([this](QWidget *widget) {
|
||||
setLayouter([this] {
|
||||
using namespace Layouting;
|
||||
Column {
|
||||
return Column {
|
||||
Row { Tr::tr("CMakeFormat command:"), command },
|
||||
Space(10),
|
||||
Group {
|
||||
@@ -79,7 +79,7 @@ public:
|
||||
}
|
||||
},
|
||||
st
|
||||
}.attachTo(widget);
|
||||
};
|
||||
});
|
||||
|
||||
ActionContainer *menu = ActionManager::createMenu(Constants::CMAKEFORMATTER_MENU_ID);
|
||||
|
||||
@@ -112,7 +112,7 @@ ConanInstallStep::ConanInstallStep(BuildStepList *bsl, Id id)
|
||||
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) {
|
||||
connectTarget(project, target);
|
||||
});
|
||||
|
||||
@@ -93,7 +93,7 @@ void AuthWidget::updateClient(const Utils::FilePath &nodeJs, const Utils::FilePa
|
||||
m_client = nullptr;
|
||||
setState(Tr::tr("Sign in"), false);
|
||||
m_button->setEnabled(false);
|
||||
if (!nodeJs.exists() || !agent.exists()) {
|
||||
if (!nodeJs.isExecutableFile() || !agent.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ QtcPlugin {
|
||||
|
||||
Depends { name: "Core" }
|
||||
Depends { name: "LanguageClient" }
|
||||
Depends { name: "ProjectExplorer" }
|
||||
Depends { name: "TextEditor" }
|
||||
Depends { name: "Qt"; submodules: ["widgets", "xml", "network"] }
|
||||
|
||||
|
||||
@@ -139,6 +139,7 @@ void CopilotHoverHandler::identifyMatch(TextEditorWidget *editorWidget,
|
||||
|
||||
void CopilotHoverHandler::operateTooltip(TextEditorWidget *editorWidget, const QPoint &point)
|
||||
{
|
||||
Q_UNUSED(point)
|
||||
auto *suggestion = dynamic_cast<CopilotSuggestion *>(TextDocumentLayout::suggestion(m_block));
|
||||
|
||||
if (!suggestion)
|
||||
@@ -147,8 +148,12 @@ void CopilotHoverHandler::operateTooltip(TextEditorWidget *editorWidget, const Q
|
||||
auto tooltipWidget = new CopilotCompletionToolTip(suggestion->completions(),
|
||||
suggestion->currentCompletion(),
|
||||
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
|
||||
|
||||
@@ -120,6 +120,9 @@ void CopilotPlugin::extensionsInitialized()
|
||||
void CopilotPlugin::restartClient()
|
||||
{
|
||||
LanguageClient::LanguageClientManager::shutdownClient(m_client);
|
||||
|
||||
if (!CopilotSettings::instance().nodeJsPath().isExecutableFile())
|
||||
return;
|
||||
m_client = new CopilotClient(CopilotSettings::instance().nodeJsPath(),
|
||||
CopilotSettings::instance().distPath());
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ CopilotSettings::CopilotSettings()
|
||||
nodeJsPath.setHistoryCompleter("Copilot.NodePath.History");
|
||||
nodeJsPath.setDisplayName(Tr::tr("Node.js Path"));
|
||||
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."));
|
||||
|
||||
distPath.setExpectedKind(PathChooser::File);
|
||||
@@ -63,7 +63,7 @@ CopilotSettings::CopilotSettings()
|
||||
distPath.setHistoryCompleter("Copilot.DistPath.History");
|
||||
distPath.setDisplayName(Tr::tr("Agent.js path"));
|
||||
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."));
|
||||
|
||||
autoComplete.setDisplayName(Tr::tr("Auto Complete"));
|
||||
@@ -71,7 +71,7 @@ CopilotSettings::CopilotSettings()
|
||||
autoComplete.setLabelText(Tr::tr("Request completions automatically"));
|
||||
autoComplete.setDefaultValue(true);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -347,8 +347,10 @@ void Internal::CommandPrivate::setCurrentContext(const Context &context)
|
||||
m_context = context;
|
||||
|
||||
QAction *currentAction = nullptr;
|
||||
for (int i = 0; i < m_context.size(); ++i) {
|
||||
if (QAction *a = m_contextActionMap.value(m_context.at(i), nullptr)) {
|
||||
for (const Id &id : std::as_const(m_context)) {
|
||||
if (id == Constants::C_GLOBAL_CUTOFF)
|
||||
break;
|
||||
if (QAction *a = m_contextActionMap.value(id, nullptr)) {
|
||||
currentAction = a;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -6,18 +6,17 @@
|
||||
#include <coreplugin/coreplugintr.h>
|
||||
#include <coreplugin/dialogs/shortcutsettings.h>
|
||||
|
||||
#include <utils/headerviewstretcher.h>
|
||||
#include <utils/fancylineedit.h>
|
||||
#include <utils/headerviewstretcher.h>
|
||||
#include <utils/layoutbuilder.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QGroupBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QPointer>
|
||||
#include <QPushButton>
|
||||
#include <QTreeWidgetItem>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
Q_DECLARE_METATYPE(Core::Internal::ShortcutItem*)
|
||||
|
||||
@@ -32,13 +31,10 @@ public:
|
||||
CommandMappingsPrivate(CommandMappings *parent)
|
||||
: q(parent)
|
||||
{
|
||||
groupBox = new QGroupBox(parent);
|
||||
groupBox->setTitle(::Core::Tr::tr("Command Mappings"));
|
||||
|
||||
filterEdit = new FancyLineEdit(groupBox);
|
||||
filterEdit = new FancyLineEdit;
|
||||
filterEdit->setFiltering(true);
|
||||
|
||||
commandList = new QTreeWidget(groupBox);
|
||||
commandList = new QTreeWidget;
|
||||
commandList->setRootIsDecorated(false);
|
||||
commandList->setUniformRowHeights(true);
|
||||
commandList->setSortingEnabled(true);
|
||||
@@ -49,33 +45,28 @@ public:
|
||||
item->setText(1, ::Core::Tr::tr("Label"));
|
||||
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."));
|
||||
|
||||
resetButton = new QPushButton(::Core::Tr::tr("Reset"), groupBox);
|
||||
resetButton = new QPushButton(::Core::Tr::tr("Reset"));
|
||||
resetButton->setToolTip(::Core::Tr::tr("Reset to default."));
|
||||
resetButton->setVisible(false);
|
||||
|
||||
importButton = new QPushButton(::Core::Tr::tr("Import..."), groupBox);
|
||||
exportButton = new QPushButton(::Core::Tr::tr("Export..."), groupBox);
|
||||
importButton = new QPushButton(::Core::Tr::tr("Import..."));
|
||||
exportButton = new QPushButton(::Core::Tr::tr("Export..."));
|
||||
|
||||
auto hboxLayout1 = new QHBoxLayout();
|
||||
hboxLayout1->addWidget(defaultButton);
|
||||
hboxLayout1->addWidget(resetButton);
|
||||
hboxLayout1->addStretch();
|
||||
hboxLayout1->addWidget(importButton);
|
||||
hboxLayout1->addWidget(exportButton);
|
||||
|
||||
auto hboxLayout = new QHBoxLayout();
|
||||
hboxLayout->addWidget(filterEdit);
|
||||
|
||||
auto vboxLayout1 = new QVBoxLayout(groupBox);
|
||||
vboxLayout1->addLayout(hboxLayout);
|
||||
vboxLayout1->addWidget(commandList);
|
||||
vboxLayout1->addLayout(hboxLayout1);
|
||||
|
||||
auto vboxLayout = new QVBoxLayout(parent);
|
||||
vboxLayout->addWidget(groupBox);
|
||||
using namespace Layouting;
|
||||
Column {
|
||||
Group {
|
||||
title(::Core::Tr::tr("Command Mappings")),
|
||||
bindTo(&groupBox),
|
||||
Column {
|
||||
filterEdit,
|
||||
commandList,
|
||||
Row { defaultButton, resetButton, st, importButton, exportButton },
|
||||
},
|
||||
},
|
||||
}.attachTo(parent);
|
||||
|
||||
q->connect(exportButton, &QPushButton::clicked,
|
||||
q, &CommandMappings::exportAction);
|
||||
|
||||
@@ -46,6 +46,11 @@ const char C_EDITORMANAGER[] = "Core.EditorManager";
|
||||
const char C_NAVIGATION_PANE[] = "Core.NavigationPane";
|
||||
const char C_PROBLEM_PANE[] = "Core.ProblemPane";
|
||||
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
|
||||
const char K_DEFAULT_TEXT_EDITOR_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::Core", "Plain Text Editor");
|
||||
|
||||
@@ -199,15 +199,6 @@ void IOptionsPage::setSettings(AspectContainer *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)
|
||||
{
|
||||
m_widgetCreator = [layouter] {
|
||||
|
||||
@@ -74,7 +74,6 @@ protected:
|
||||
void setCategoryIcon(const Utils::Icon &categoryIcon) { m_categoryIcon = categoryIcon; }
|
||||
void setCategoryIconPath(const Utils::FilePath &categoryIconPath);
|
||||
void setSettings(Utils::AspectContainer *settings);
|
||||
void setLayouter(const std::function<void(QWidget *w)> &layouter);
|
||||
void setLayouter(const std::function<Layouting::LayoutItem()> &layouter);
|
||||
|
||||
// Used in FontSettingsPage. FIXME?
|
||||
|
||||
@@ -42,7 +42,6 @@
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/checkablemessagebox.h>
|
||||
#include <utils/environment.h>
|
||||
#include <utils/executeondestruction.h>
|
||||
#include <utils/filepath.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/infobar.h>
|
||||
@@ -70,6 +69,7 @@
|
||||
#include <QPushButton>
|
||||
#include <QRegularExpression>
|
||||
#include <QRegularExpressionMatch>
|
||||
#include <QScopeGuard>
|
||||
#include <QSet>
|
||||
#include <QSettings>
|
||||
#include <QSplitter>
|
||||
@@ -3325,9 +3325,7 @@ IEditor *EditorManager::openEditorWithContents(Id editorId,
|
||||
EditorManager::gotoOtherSplit();
|
||||
|
||||
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
|
||||
Utils::ExecuteOnDestruction appRestoreCursor(&QApplication::restoreOverrideCursor);
|
||||
Q_UNUSED(appRestoreCursor)
|
||||
|
||||
const auto cleanup = qScopeGuard(&QApplication::restoreOverrideCursor);
|
||||
|
||||
const QString title = makeTitleUnique(titlePattern);
|
||||
|
||||
|
||||
@@ -450,7 +450,7 @@ void LocatorMatcher::start()
|
||||
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,
|
||||
int index) {
|
||||
@@ -470,7 +470,7 @@ void LocatorMatcher::start()
|
||||
for (const LocatorMatcherTask &task : std::as_const(d->m_tasks)) {
|
||||
const auto storage = task.storage;
|
||||
const Group group {
|
||||
optional,
|
||||
finishAllAndDone,
|
||||
Storage(storage),
|
||||
onGroupSetup(onSetup(storage, index)),
|
||||
onGroupDone(onDone(storage)),
|
||||
@@ -597,7 +597,7 @@ QString ILocatorFilter::shortcutString() const
|
||||
\internal
|
||||
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;
|
||||
}
|
||||
@@ -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
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ class CORE_EXPORT LocatorMatcherTask final
|
||||
public:
|
||||
// The main task. Initial data (searchTerm) should be taken from storage.input().
|
||||
// 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.
|
||||
Tasking::TreeStorage<LocatorStorage> storage;
|
||||
@@ -270,8 +270,8 @@ protected:
|
||||
virtual void saveState(QJsonObject &object) const;
|
||||
virtual void restoreState(const QJsonObject &object);
|
||||
|
||||
void setRefreshRecipe(const std::optional<Tasking::TaskItem> &recipe);
|
||||
std::optional<Tasking::TaskItem> refreshRecipe() const;
|
||||
void setRefreshRecipe(const std::optional<Tasking::GroupItem> &recipe);
|
||||
std::optional<Tasking::GroupItem> refreshRecipe() const;
|
||||
|
||||
static bool isOldSetting(const QByteArray &state);
|
||||
|
||||
@@ -289,7 +289,7 @@ private:
|
||||
QString m_description;
|
||||
QString m_defaultShortcut;
|
||||
std::optional<QString> m_defaultSearchText;
|
||||
std::optional<Tasking::TaskItem> m_refreshRecipe;
|
||||
std::optional<Tasking::GroupItem> m_refreshRecipe;
|
||||
QKeySequence m_defaultKeySequence;
|
||||
bool m_defaultIncludedByDefault = false;
|
||||
bool m_includedByDefault = m_defaultIncludedByDefault;
|
||||
|
||||
@@ -381,14 +381,14 @@ void Locator::refresh(const QList<ILocatorFilter *> &filters)
|
||||
m_refreshingFilters = Utils::filteredUnique(m_refreshingFilters + filters);
|
||||
|
||||
using namespace Tasking;
|
||||
QList<TaskItem> tasks{parallel};
|
||||
QList<GroupItem> tasks{parallel};
|
||||
for (ILocatorFilter *filter : std::as_const(m_refreshingFilters)) {
|
||||
const auto task = filter->refreshRecipe();
|
||||
if (!task.has_value())
|
||||
continue;
|
||||
|
||||
const Group group {
|
||||
optional,
|
||||
finishAllAndDone,
|
||||
*task,
|
||||
onGroupDone([this, filter] { m_refreshingFilters.removeOne(filter); })
|
||||
};
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/basetreeview.h>
|
||||
#include <utils/executeondestruction.h>
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/listmodel.h>
|
||||
#include <utils/qtcassert.h>
|
||||
@@ -33,6 +32,7 @@
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QRegularExpression>
|
||||
#include <QScopeGuard>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QToolButton>
|
||||
@@ -591,7 +591,7 @@ void LoggingViewManagerWidget::saveLoggingsToFile() const
|
||||
{
|
||||
// should we just let it continue without temporarily disabling?
|
||||
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)
|
||||
m_manager->setEnabled(false);
|
||||
const Utils::FilePath fp = Utils::FileUtils::getSaveFilePath(ICore::dialogParent(),
|
||||
|
||||
@@ -17,14 +17,11 @@
|
||||
#include <extensionsystem/pluginview.h>
|
||||
|
||||
#include <utils/fancylineedit.h>
|
||||
#include <utils/layoutbuilder.h>
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QDialog>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
@@ -35,30 +32,27 @@ PluginDialog::PluginDialog(QWidget *parent)
|
||||
: QDialog(parent),
|
||||
m_view(new ExtensionSystem::PluginView(this))
|
||||
{
|
||||
auto vl = new QVBoxLayout(this);
|
||||
|
||||
auto filterLayout = new QHBoxLayout;
|
||||
vl->addLayout(filterLayout);
|
||||
auto filterEdit = new Utils::FancyLineEdit(this);
|
||||
filterEdit->setFocus();
|
||||
filterEdit->setFiltering(true);
|
||||
connect(filterEdit, &Utils::FancyLineEdit::filterChanged,
|
||||
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);
|
||||
buttonBox->addButton(m_detailsButton, QDialogButtonBox::ActionRole);
|
||||
buttonBox->addButton(m_errorDetailsButton, QDialogButtonBox::ActionRole);
|
||||
buttonBox->addButton(m_installButton, QDialogButtonBox::ActionRole);
|
||||
vl->addWidget(buttonBox);
|
||||
m_detailsButton = buttonBox->addButton(Tr::tr("Details"), QDialogButtonBox::ActionRole);
|
||||
m_detailsButton->setEnabled(false);
|
||||
m_errorDetailsButton = buttonBox->addButton(Tr::tr("Error Details"),
|
||||
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);
|
||||
setWindowTitle(Tr::tr("Installed Plugins"));
|
||||
@@ -116,13 +110,16 @@ void PluginDialog::openDetails(ExtensionSystem::PluginSpec *spec)
|
||||
return;
|
||||
QDialog dialog(this);
|
||||
dialog.setWindowTitle(Tr::tr("Plugin Details of %1").arg(spec->name()));
|
||||
auto layout = new QVBoxLayout;
|
||||
dialog.setLayout(layout);
|
||||
auto details = new ExtensionSystem::PluginDetailsView(&dialog);
|
||||
layout->addWidget(details);
|
||||
details->update(spec);
|
||||
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::rejected, &dialog, &QDialog::reject);
|
||||
dialog.resize(400, 500);
|
||||
@@ -136,13 +133,16 @@ void PluginDialog::openErrorDetails()
|
||||
return;
|
||||
QDialog dialog(this);
|
||||
dialog.setWindowTitle(Tr::tr("Plugin Errors of %1").arg(spec->name()));
|
||||
auto layout = new QVBoxLayout;
|
||||
dialog.setLayout(layout);
|
||||
auto errors = new ExtensionSystem::PluginErrorView(&dialog);
|
||||
layout->addWidget(errors);
|
||||
errors->update(spec);
|
||||
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::rejected, &dialog, &QDialog::reject);
|
||||
dialog.resize(500, 300);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/infolabel.h>
|
||||
#include <utils/layoutbuilder.h>
|
||||
#include <utils/pathchooser.h>
|
||||
#include <utils/process.h>
|
||||
#include <utils/qtcassert.h>
|
||||
@@ -33,7 +34,6 @@
|
||||
#include <QPushButton>
|
||||
#include <QRadioButton>
|
||||
#include <QTextEdit>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include <memory>
|
||||
|
||||
@@ -80,19 +80,15 @@ public:
|
||||
, m_data(data)
|
||||
{
|
||||
setTitle(Tr::tr("Source"));
|
||||
auto vlayout = new QVBoxLayout;
|
||||
setLayout(vlayout);
|
||||
|
||||
auto label = new QLabel(
|
||||
"<p>"
|
||||
+ Tr::tr("Choose source location. This can be a plugin library file or a zip file.")
|
||||
+ "</p>");
|
||||
label->setWordWrap(true);
|
||||
vlayout->addWidget(label);
|
||||
|
||||
auto chooser = new PathChooser;
|
||||
chooser->setExpectedKind(PathChooser::Any);
|
||||
vlayout->addWidget(chooser);
|
||||
connect(chooser, &PathChooser::textChanged, this, [this, chooser] {
|
||||
m_data->sourcePath = chooser->filePath();
|
||||
updateWarnings();
|
||||
@@ -101,7 +97,8 @@ public:
|
||||
m_info = new InfoLabel;
|
||||
m_info->setType(InfoLabel::Error);
|
||||
m_info->setVisible(false);
|
||||
vlayout->addWidget(m_info);
|
||||
|
||||
Layouting::Column { label, chooser, m_info }.attachTo(this);
|
||||
}
|
||||
|
||||
void updateWarnings()
|
||||
@@ -153,8 +150,6 @@ public:
|
||||
, m_data(data)
|
||||
{
|
||||
setTitle(Tr::tr("Check Archive"));
|
||||
auto vlayout = new QVBoxLayout;
|
||||
setLayout(vlayout);
|
||||
|
||||
m_label = new InfoLabel;
|
||||
m_label->setElideMode(Qt::ElideNone);
|
||||
@@ -163,13 +158,11 @@ public:
|
||||
m_output = new QTextEdit;
|
||||
m_output->setReadOnly(true);
|
||||
|
||||
auto hlayout = new QHBoxLayout;
|
||||
hlayout->addWidget(m_label, 1);
|
||||
hlayout->addStretch();
|
||||
hlayout->addWidget(m_cancelButton);
|
||||
|
||||
vlayout->addLayout(hlayout);
|
||||
vlayout->addWidget(m_output);
|
||||
using namespace Layouting;
|
||||
Column {
|
||||
Row { m_label, st, m_cancelButton },
|
||||
m_output,
|
||||
}.attachTo(this);
|
||||
}
|
||||
|
||||
void initializePage() final
|
||||
@@ -322,13 +315,9 @@ public:
|
||||
, m_data(data)
|
||||
{
|
||||
setTitle(Tr::tr("Install Location"));
|
||||
auto vlayout = new QVBoxLayout;
|
||||
setLayout(vlayout);
|
||||
|
||||
auto label = new QLabel("<p>" + Tr::tr("Choose install location.") + "</p>");
|
||||
label->setWordWrap(true);
|
||||
vlayout->addWidget(label);
|
||||
vlayout->addSpacing(10);
|
||||
|
||||
auto localInstall = new QRadioButton(Tr::tr("User plugins"));
|
||||
localInstall->setChecked(!m_data->installIntoApplication);
|
||||
@@ -338,10 +327,6 @@ public:
|
||||
localLabel->setWordWrap(true);
|
||||
localLabel->setAttribute(Qt::WA_MacSmallSize, true);
|
||||
|
||||
vlayout->addWidget(localInstall);
|
||||
vlayout->addWidget(localLabel);
|
||||
vlayout->addSpacing(10);
|
||||
|
||||
auto appInstall = new QRadioButton(
|
||||
Tr::tr("%1 installation").arg(Constants::IDE_DISPLAY_NAME));
|
||||
appInstall->setChecked(m_data->installIntoApplication);
|
||||
@@ -351,8 +336,11 @@ public:
|
||||
.arg(Constants::IDE_DISPLAY_NAME));
|
||||
appLabel->setWordWrap(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);
|
||||
group->addButton(localInstall);
|
||||
@@ -375,12 +363,9 @@ public:
|
||||
{
|
||||
setTitle(Tr::tr("Summary"));
|
||||
|
||||
auto vlayout = new QVBoxLayout;
|
||||
setLayout(vlayout);
|
||||
|
||||
m_summaryLabel = new QLabel(this);
|
||||
m_summaryLabel->setWordWrap(true);
|
||||
vlayout->addWidget(m_summaryLabel);
|
||||
Layouting::Column { m_summaryLabel }.attachTo(this);
|
||||
}
|
||||
|
||||
void initializePage() final
|
||||
|
||||
@@ -51,8 +51,7 @@ const char showCrashButtonKey[] = "ShowCrashButton";
|
||||
// TODO: move to somewhere in Utils
|
||||
static QString formatSize(qint64 size)
|
||||
{
|
||||
QStringList units {Tr::tr("Bytes"), Tr::tr("KB"), Tr::tr("MB"),
|
||||
Tr::tr("GB"), Tr::tr("TB")};
|
||||
QStringList units{Tr::tr("Bytes"), Tr::tr("KiB"), Tr::tr("MiB"), Tr::tr("GiB"), Tr::tr("TiB")};
|
||||
double outputSize = size;
|
||||
int i;
|
||||
for (i = 0; i < units.size() - 1; ++i) {
|
||||
|
||||
@@ -108,11 +108,11 @@ CppcheckOptions::CppcheckOptions()
|
||||
readSettings();
|
||||
}
|
||||
|
||||
std::function<void(QWidget *widget)> CppcheckOptions::layouter()
|
||||
std::function<Layouting::LayoutItem()> CppcheckOptions::layouter()
|
||||
{
|
||||
return [this](QWidget *widget) {
|
||||
return [this] {
|
||||
using namespace Layouting;
|
||||
Form {
|
||||
return Form {
|
||||
binary, br,
|
||||
Tr::tr("Checks:"), Flow {
|
||||
warning,
|
||||
@@ -132,7 +132,7 @@ std::function<void(QWidget *widget)> CppcheckOptions::layouter()
|
||||
addIncludePaths,
|
||||
guessArguments
|
||||
}
|
||||
}.attachTo(widget);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ class CppcheckOptions final : public Core::PagedSettings
|
||||
public:
|
||||
CppcheckOptions();
|
||||
|
||||
std::function<void(QWidget *widget)> layouter();
|
||||
std::function<Layouting::LayoutItem()> layouter();
|
||||
|
||||
Utils::FilePathAspect binary{this};
|
||||
Utils::BoolAspect warning{this};
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <debugger/analyzer/analyzerconstants.h>
|
||||
#include <debugger/debuggermainwindow.h>
|
||||
|
||||
#include <utils/layoutbuilder.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/utilsicons.h>
|
||||
|
||||
@@ -111,8 +112,7 @@ void CppcheckPluginPrivate::startManualRun()
|
||||
|
||||
manualRunTool.updateOptions();
|
||||
|
||||
auto optionsWidget = new QWidget;
|
||||
options.layouter()(optionsWidget);
|
||||
auto optionsWidget = options.layouter()().emerge();
|
||||
|
||||
ManualRunDialog dialog(optionsWidget, project);
|
||||
if (dialog.exec() == ManualRunDialog::Rejected)
|
||||
|
||||
@@ -64,7 +64,7 @@ void CppProjectUpdater::update(const ProjectUpdateInfo &projectUpdateInfo,
|
||||
if (async.isResultAvailable())
|
||||
storage->projectInfo = async.result();
|
||||
};
|
||||
QList<TaskItem> tasks{parallel};
|
||||
QList<GroupItem> tasks{parallel};
|
||||
tasks.append(AsyncTask<ProjectInfo::ConstPtr>(setupInfoGenerator, onInfoGeneratorDone));
|
||||
for (QPointer<ExtraCompiler> compiler : compilers) {
|
||||
if (compiler && compiler->isDirty())
|
||||
|
||||
@@ -414,6 +414,7 @@ F2TestCase::F2TestCase(CppEditorAction action,
|
||||
} else {
|
||||
currentTextEditor->convertPosition(targetTestFile->m_targetCursorPosition,
|
||||
&expectedLine, &expectedColumn);
|
||||
++expectedColumn;
|
||||
if (useClangd && (tag == "classDestructor" || tag == "fromDestructorDefinitionSymbol"
|
||||
|| tag == "fromDestructorBody")) {
|
||||
--expectedColumn; // clangd goes before the ~, built-in code model after
|
||||
|
||||
@@ -166,7 +166,7 @@ void ResourcePreviewHoverHandler::operateTooltip(TextEditorWidget *editorWidget,
|
||||
{
|
||||
const QString tt = makeTooltip();
|
||||
if (!tt.isEmpty())
|
||||
Utils::ToolTip::show(point, tt, editorWidget);
|
||||
Utils::ToolTip::show(point, tt, Qt::MarkdownText, editorWidget);
|
||||
else
|
||||
Utils::ToolTip::hide();
|
||||
}
|
||||
@@ -180,10 +180,8 @@ QString ResourcePreviewHoverHandler::makeTooltip() const
|
||||
|
||||
const Utils::MimeType mimeType = Utils::mimeTypeForFile(m_resPath);
|
||||
if (mimeType.name().startsWith("image", Qt::CaseInsensitive))
|
||||
ret += QString("<img src=\"file:///%1\" /><br/>").arg(m_resPath);
|
||||
|
||||
ret += QString("<a href=\"file:///%1\">%2</a>")
|
||||
.arg(m_resPath, QDir::toNativeSeparators(m_resPath));
|
||||
ret += QString(" \n").arg(m_resPath);
|
||||
ret += QString("[%1](%2)").arg(QDir::toNativeSeparators(m_resPath), m_resPath);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ void SemanticHighlighter::run()
|
||||
|
||||
m_revision = documentRevision();
|
||||
m_seenBlocks.clear();
|
||||
m_nextResultToHandle = m_resultCount = 0;
|
||||
qCDebug(log) << "starting runner for document revision" << m_revision;
|
||||
m_watcher->setFuture(m_highlightingRunner());
|
||||
}
|
||||
@@ -92,6 +93,21 @@ void SemanticHighlighter::onHighlighterResultAvailable(int from, int to)
|
||||
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;
|
||||
t.start();
|
||||
|
||||
@@ -177,6 +193,8 @@ void SemanticHighlighter::onHighlighterFinished()
|
||||
{
|
||||
QTC_ASSERT(m_watcher, return);
|
||||
|
||||
handleHighlighterResults();
|
||||
|
||||
QElapsedTimer t;
|
||||
t.start();
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ public:
|
||||
|
||||
private:
|
||||
void onHighlighterResultAvailable(int from, int to);
|
||||
void handleHighlighterResults();
|
||||
void onHighlighterFinished();
|
||||
|
||||
void connectWatcher();
|
||||
@@ -82,6 +83,8 @@ private:
|
||||
QScopedPointer<QFutureWatcher<TextEditor::HighlightingResult>> m_watcher;
|
||||
QHash<int, QTextCharFormat> m_formatMap;
|
||||
std::set<int> m_seenBlocks;
|
||||
int m_nextResultToHandle = 0;
|
||||
int m_resultCount = 0;
|
||||
|
||||
HighlightingRunner m_highlightingRunner;
|
||||
};
|
||||
|
||||
@@ -59,9 +59,9 @@ CvsSettings::CvsSettings()
|
||||
|
||||
diffIgnoreBlankLines.setSettingsKey("DiffIgnoreBlankLines");
|
||||
|
||||
setLayouter([this](QWidget *widget) {
|
||||
setLayouter([this] {
|
||||
using namespace Layouting;
|
||||
Column {
|
||||
return Column {
|
||||
Group {
|
||||
title(Tr::tr("Configuration")),
|
||||
Form {
|
||||
@@ -80,7 +80,7 @@ CvsSettings::CvsSettings()
|
||||
}
|
||||
},
|
||||
st
|
||||
}.attachTo(widget);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -101,11 +101,11 @@ DebuggerRunConfigurationAspect::DebuggerRunConfigurationAspect(Target *target)
|
||||
addDataExtractor(this, &DebuggerRunConfigurationAspect::useMultiProcess, &Data::useMultiProcess);
|
||||
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->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->setSettingsKey("RunConfiguration.UseQmlDebugger");
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ public:
|
||||
setCategory(Constants::DEBUGGER_SETTINGS_CATEGORY);
|
||||
setSettings(&debuggerSettings()->page2);
|
||||
|
||||
setLayouter([](QWidget *w) {
|
||||
setLayouter([] {
|
||||
using namespace Layouting;
|
||||
DebuggerSettings &s = *debuggerSettings();
|
||||
|
||||
@@ -84,7 +84,7 @@ public:
|
||||
Column { s.gdbPostAttachCommands },
|
||||
};
|
||||
|
||||
Grid { general, extended, br, startup, attach }.attachTo(w);
|
||||
return Grid { general, extended, br, startup, attach };
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -128,7 +128,7 @@ DiffFilesController::DiffFilesController(IDocument *document)
|
||||
outputList->resize(inputList.size());
|
||||
|
||||
using namespace std::placeholders;
|
||||
QList<TaskItem> tasks {parallel, optional};
|
||||
QList<GroupItem> tasks {parallel, finishAllAndDone};
|
||||
for (int i = 0; i < inputList.size(); ++i) {
|
||||
tasks.append(AsyncTask<FileData>(std::bind(setupDiff, _1, inputList.at(i)),
|
||||
std::bind(onDiffDone, _1, i)));
|
||||
|
||||
@@ -10,6 +10,13 @@
|
||||
// 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.
|
||||
|
||||
#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/layoutbuilder.h>
|
||||
#include <utils/qtcassert.h>
|
||||
@@ -18,8 +25,7 @@
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
namespace FakeVim {
|
||||
namespace Internal {
|
||||
namespace FakeVim::Internal {
|
||||
|
||||
#ifdef FAKEVIM_STANDALONE
|
||||
FvBaseAspect::FvBaseAspect()
|
||||
@@ -62,13 +68,31 @@ QString FvBaseAspect::settingsKey() const
|
||||
void setAutoApply(bool ) {}
|
||||
#endif
|
||||
|
||||
|
||||
static FakeVimSettings *s_settings;
|
||||
|
||||
FakeVimSettings &settings()
|
||||
{
|
||||
return *s_settings;
|
||||
}
|
||||
|
||||
FakeVimSettings::FakeVimSettings()
|
||||
{
|
||||
setAutoApply(false);
|
||||
s_settings = this;
|
||||
|
||||
#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"));
|
||||
#endif
|
||||
|
||||
// Specific FakeVim settings
|
||||
setup(&readVimRc, false, "ReadVimRc", {}, Tr::tr("Read .vimrc from location:"));
|
||||
setup(&vimRcPath, QString(), "VimRcPath", {}, {}); // Tr::tr("Path to .vimrc")
|
||||
@@ -135,6 +159,121 @@ FakeVimSettings::FakeVimSettings()
|
||||
"%USERPROFILE%\\_vimrc on Windows, ~/.vimrc otherwise."));
|
||||
vimRcPath.setPlaceHolderText(Tr::tr("Default: %1").arg(vimrcDefault));
|
||||
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
|
||||
}
|
||||
|
||||
@@ -187,11 +326,4 @@ void FakeVimSettings::setup(FvBaseAspect *aspect,
|
||||
m_nameToAspect[shortName] = aspect;
|
||||
}
|
||||
|
||||
FakeVimSettings *fakeVimSettings()
|
||||
{
|
||||
static FakeVimSettings s;
|
||||
return &s;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace FakeVim
|
||||
} // FakeVim::Internal
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef FAKEVIM_STANDALONE
|
||||
# include <utils/aspects.h>
|
||||
# include <coreplugin/dialogs/ioptionspage.h>
|
||||
#endif
|
||||
|
||||
#include <QCoreApplication>
|
||||
@@ -13,8 +13,7 @@
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
|
||||
namespace FakeVim {
|
||||
namespace Internal {
|
||||
namespace FakeVim::Internal {
|
||||
|
||||
#ifdef FAKEVIM_STANDALONE
|
||||
class FvBaseAspect
|
||||
@@ -68,7 +67,7 @@ public:
|
||||
|
||||
#else
|
||||
|
||||
using FvAspectContainer = Utils::AspectContainer;
|
||||
using FvAspectContainer = Core::PagedSettings;
|
||||
using FvBaseAspect = Utils::BaseAspect;
|
||||
using FvBoolAspect = Utils::BoolAspect;
|
||||
using FvIntegerAspect = Utils::IntegerAspect;
|
||||
@@ -145,7 +144,6 @@ private:
|
||||
QHash<FvBaseAspect *, QString> m_aspectToName;
|
||||
};
|
||||
|
||||
FakeVimSettings *fakeVimSettings();
|
||||
FakeVimSettings &settings();
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace FakeVim
|
||||
} // FakeVim::Internal
|
||||
|
||||
@@ -408,9 +408,8 @@ static QRegularExpression vimPatternToQtPattern(const QString &needle)
|
||||
*/
|
||||
|
||||
// FIXME: Option smartcase should be used only if search was typed by user.
|
||||
const bool ignoreCaseOption = fakeVimSettings()->ignoreCase.value();
|
||||
const bool smartCaseOption = fakeVimSettings()->smartCase.value();
|
||||
const bool initialIgnoreCase = ignoreCaseOption
|
||||
const bool smartCaseOption = settings().smartCase();
|
||||
const bool initialIgnoreCase = settings().ignoreCase()
|
||||
&& !(smartCaseOption && needle.contains(QRegularExpression("[A-Z]")));
|
||||
|
||||
bool ignorecase = initialIgnoreCase;
|
||||
@@ -2373,7 +2372,7 @@ public:
|
||||
QString surroundFunction; // Used for storing the function name provided to ys{motion}f
|
||||
} g;
|
||||
|
||||
FakeVimSettings &s = *fakeVimSettings();
|
||||
FakeVimSettings &s = settings();
|
||||
};
|
||||
|
||||
static void initSingleShotTimer(QTimer *timer,
|
||||
@@ -2527,7 +2526,7 @@ void FakeVimHandler::Private::leaveFakeVim(bool needUpdate)
|
||||
|
||||
// The command might have destroyed the editor.
|
||||
if (m_textedit || m_plaintextedit) {
|
||||
if (s.showMarks.value())
|
||||
if (s.showMarks())
|
||||
updateSelection();
|
||||
|
||||
updateMiniBuffer();
|
||||
@@ -2576,7 +2575,7 @@ bool FakeVimHandler::Private::wantsOverride(QKeyEvent *ev)
|
||||
|
||||
// We are interested in overriding most Ctrl key combinations.
|
||||
if (isOnlyControlModifier(mods)
|
||||
&& !s.passControlKey.value()
|
||||
&& !s.passControlKey()
|
||||
&& ((key >= Key_A && key <= Key_Z && key != Key_K)
|
||||
|| key == Key_BracketLeft || key == Key_BracketRight)) {
|
||||
// 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)
|
||||
{
|
||||
if (!isComplete && !s.incSearch.value())
|
||||
if (!isComplete && !s.incSearch())
|
||||
return;
|
||||
|
||||
g.currentMessage.clear();
|
||||
@@ -3161,7 +3160,7 @@ void FakeVimHandler::Private::pushUndoState(bool overwrite)
|
||||
pos = firstPositionInLine(lineForPosition(pos));
|
||||
else if (isVisualBlockMode())
|
||||
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;
|
||||
if (g.submode == ShiftLeftSubMode || g.submode == ShiftRightSubMode
|
||||
|| g.submode == IndentSubMode) {
|
||||
@@ -3621,7 +3620,7 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommandMovement)
|
||||
return;
|
||||
} else if (g.submode == ExchangeSubMode) {
|
||||
exchangeRange(currentRange());
|
||||
} else if (g.submode == ReplaceWithRegisterSubMode && s.emulateReplaceWithRegister.value()) {
|
||||
} else if (g.submode == ReplaceWithRegisterSubMode && s.emulateReplaceWithRegister()) {
|
||||
pushUndoState(false);
|
||||
beginEditBlock();
|
||||
replaceWithRegister(currentRange());
|
||||
@@ -3734,7 +3733,7 @@ void FakeVimHandler::Private::clearCurrentMode()
|
||||
void FakeVimHandler::Private::updateSelection()
|
||||
{
|
||||
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) {
|
||||
QTextEdit::ExtraSelection sel;
|
||||
sel.cursor = m_cursor;
|
||||
@@ -3753,7 +3752,7 @@ void FakeVimHandler::Private::updateSelection()
|
||||
|
||||
void FakeVimHandler::Private::updateHighlights()
|
||||
{
|
||||
if (s.useCoreSearch.value() || !s.hlSearch.value() || g.highlightsCleared) {
|
||||
if (s.useCoreSearch() || !s.hlSearch() || g.highlightsCleared) {
|
||||
if (m_highlighted.isEmpty())
|
||||
return;
|
||||
m_highlighted.clear();
|
||||
@@ -3800,7 +3799,7 @@ void FakeVimHandler::Private::updateMiniBuffer()
|
||||
} else if (!g.mapStates.isEmpty() && !g.mapStates.last().silent) {
|
||||
// Do not reset previous message when after running a mapped command.
|
||||
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;
|
||||
messageLevel = MessageShowCmd;
|
||||
} else if (g.mode == CommandMode && isVisualMode()) {
|
||||
@@ -3907,7 +3906,7 @@ bool FakeVimHandler::Private::handleCommandSubSubMode(const Input &input)
|
||||
handled = selectBlockTextObject(g.subsubdata.is('i'), '{', '}');
|
||||
else if (input.is('"') || input.is('\'') || input.is('`'))
|
||||
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'));
|
||||
else
|
||||
handled = false;
|
||||
@@ -4050,7 +4049,7 @@ bool FakeVimHandler::Private::handleMovement(const Input &input)
|
||||
g.subsubmode = NoSubSubMode;
|
||||
} else if (input.is('/') || input.is('?')) {
|
||||
g.lastSearchForward = input.is('/');
|
||||
if (s.useCoreSearch.value()) {
|
||||
if (s.useCoreSearch()) {
|
||||
// re-use the core dialog.
|
||||
g.findPending = true;
|
||||
m_findStartPosition = position();
|
||||
@@ -4225,7 +4224,7 @@ bool FakeVimHandler::Private::handleMovement(const Input &input)
|
||||
m_cursor = EDITOR(cursorForPosition(QPoint(0, EDITOR(height()) / 2)));
|
||||
handleStartOfLine();
|
||||
} else if (input.is('n') || input.is('N')) {
|
||||
if (s.useCoreSearch.value()) {
|
||||
if (s.useCoreSearch()) {
|
||||
bool forward = (input.is('n')) ? g.lastSearchForward : !g.lastSearchForward;
|
||||
int pos = position();
|
||||
q->findNextRequested(!forward);
|
||||
@@ -4314,7 +4313,7 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
|
||||
handled = handleNoSubMode(input);
|
||||
} else if (g.submode == ExchangeSubMode) {
|
||||
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
|
||||
g.submode = ExchangeSubMode;
|
||||
handled = true;
|
||||
@@ -4324,15 +4323,15 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
|
||||
} else if (g.submode == AddSurroundingSubMode) {
|
||||
handled = handleAddSurroundingSubMode(input);
|
||||
} else if (g.submode == ChangeSubMode && (input.is('s') || input.is('S'))
|
||||
&& s.emulateSurround.value()) {
|
||||
&& s.emulateSurround()) {
|
||||
g.submode = ChangeSurroundingSubMode;
|
||||
g.surroundUpperCaseS = input.is('S');
|
||||
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;
|
||||
handled = true;
|
||||
} else if (g.submode == YankSubMode && (input.is('s') || input.is('S'))
|
||||
&& s.emulateSurround.value()) {
|
||||
&& s.emulateSurround()) {
|
||||
g.submode = AddSurroundingSubMode;
|
||||
g.movetype = MoveInclusive;
|
||||
g.surroundUpperCaseS = input.is('S');
|
||||
@@ -4341,10 +4340,9 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
|
||||
|| g.submode == DeleteSubMode
|
||||
|| g.submode == YankSubMode) {
|
||||
handled = handleChangeDeleteYankSubModes(input);
|
||||
} else if (g.submode == CommentSubMode && s.emulateVimCommentary.value()) {
|
||||
} else if (g.submode == CommentSubMode && s.emulateVimCommentary()) {
|
||||
handled = handleCommentSubMode(input);
|
||||
} else if (g.submode == ReplaceWithRegisterSubMode
|
||||
&& s.emulateReplaceWithRegister.value()) {
|
||||
} else if (g.submode == ReplaceWithRegisterSubMode && s.emulateReplaceWithRegister()) {
|
||||
handled = handleReplaceWithRegisterSubMode(input);
|
||||
} else if (g.submode == ReplaceSubMode) {
|
||||
handled = handleReplaceSubMode(input);
|
||||
@@ -4487,7 +4485,7 @@ bool FakeVimHandler::Private::handleNoSubMode(const Input &input)
|
||||
setTargetColumn();
|
||||
} else if (input.isControl('a')) {
|
||||
changeNumberTextObject(count());
|
||||
} else if (g.gflag && input.is('c') && s.emulateVimCommentary.value()) {
|
||||
} else if (g.gflag && input.is('c') && s.emulateVimCommentary()) {
|
||||
if (isVisualMode()) {
|
||||
pushUndoState();
|
||||
|
||||
@@ -4512,7 +4510,7 @@ bool FakeVimHandler::Private::handleNoSubMode(const Input &input)
|
||||
pushUndoState();
|
||||
setAnchor();
|
||||
}
|
||||
} else if (g.gflag && input.is('r') && s.emulateReplaceWithRegister.value()) {
|
||||
} else if (g.gflag && input.is('r') && s.emulateReplaceWithRegister()) {
|
||||
g.submode = ReplaceWithRegisterSubMode;
|
||||
if (isVisualMode()) {
|
||||
dotCommand = visualDotCommand() + QString::number(count()) + "gr";
|
||||
@@ -4671,7 +4669,7 @@ bool FakeVimHandler::Private::handleNoSubMode(const Input &input)
|
||||
int repeat = count();
|
||||
while (--repeat >= 0)
|
||||
redo();
|
||||
} else if (input.is('S') && isVisualMode() && s.emulateSurround.value()) {
|
||||
} else if (input.is('S') && isVisualMode() && s.emulateSurround()) {
|
||||
g.submode = AddSurroundingSubMode;
|
||||
g.subsubmode = SurroundSubSubMode;
|
||||
} else if (input.is('s')) {
|
||||
@@ -4756,7 +4754,7 @@ bool FakeVimHandler::Private::handleNoSubMode(const Input &input)
|
||||
if (isVisualMode()) {
|
||||
leaveVisualMode();
|
||||
finishMovement();
|
||||
} else if (g.gflag || (g.submode == InvertCaseSubMode && s.tildeOp.value())) {
|
||||
} else if (g.gflag || (g.submode == InvertCaseSubMode && s.tildeOp())) {
|
||||
if (atEndOfLine())
|
||||
moveLeft();
|
||||
setAnchor();
|
||||
@@ -5404,8 +5402,8 @@ void FakeVimHandler::Private::handleInsertMode(const Input &input)
|
||||
if (!handleInsertInEditor(Input(Qt::Key_Backspace, Qt::NoModifier))) {
|
||||
joinPreviousEditBlock();
|
||||
if (!m_buffer->lastInsertion.isEmpty()
|
||||
|| s.backspace.value().contains("start")
|
||||
|| s.backspace.value().contains("2")) {
|
||||
|| s.backspace().contains("start")
|
||||
|| s.backspace().contains("2")) {
|
||||
const int line = cursorLine() + 1;
|
||||
const Column col = cursorColumn();
|
||||
QString data = lineContents(line);
|
||||
@@ -5438,7 +5436,7 @@ void FakeVimHandler::Private::handleInsertMode(const Input &input)
|
||||
} else if (input.isKey(Key_Tab)) {
|
||||
if (q->tabPressedInInsertMode()) {
|
||||
m_buffer->insertState.insertingSpaces = true;
|
||||
if (s.expandTab.value()) {
|
||||
if (s.expandTab()) {
|
||||
const int ts = s.tabStop();
|
||||
const int col = logicalCursorColumn();
|
||||
QString str = QString(ts - col % ts, ' ');
|
||||
@@ -5494,7 +5492,7 @@ void FakeVimHandler::Private::insertInInsertMode(const QString &text)
|
||||
{
|
||||
joinPreviousEditBlock();
|
||||
insertText(text);
|
||||
if (s.smartIndent.value() && isElectricCharacter(text.at(0))) {
|
||||
if (s.smartIndent() && isElectricCharacter(text.at(0))) {
|
||||
const QString leftText = block().text()
|
||||
.left(position() - 1 - block().position());
|
||||
if (leftText.simplified().isEmpty()) {
|
||||
@@ -6265,7 +6263,7 @@ bool FakeVimHandler::Private::handleExMoveCommand(const ExCommand &cmd)
|
||||
|
||||
if (!insertAtEnd)
|
||||
moveUp(1);
|
||||
if (s.startOfLine.value())
|
||||
if (s.startOfLine())
|
||||
moveToFirstNonBlankOnLine();
|
||||
|
||||
if (lastAnchor.line >= startLine && lastAnchor.line <= endLine)
|
||||
@@ -6794,7 +6792,7 @@ QTextCursor FakeVimHandler::Private::search(const SearchData &sd, int startPos,
|
||||
}
|
||||
|
||||
if (tc.isNull()) {
|
||||
if (s.wrapScan.value()) {
|
||||
if (s.wrapScan()) {
|
||||
tc = QTextCursor(document());
|
||||
tc.movePosition(sd.forward ? StartOfDocument : EndOfDocument);
|
||||
if (sd.forward)
|
||||
@@ -6954,7 +6952,7 @@ void FakeVimHandler::Private::shiftRegionRight(int repeat)
|
||||
std::swap(beginLine, endLine);
|
||||
targetPos = position();
|
||||
}
|
||||
if (s.startOfLine.value())
|
||||
if (s.startOfLine())
|
||||
targetPos = firstPositionInLine(beginLine);
|
||||
|
||||
const int sw = s.shiftWidth();
|
||||
@@ -7106,7 +7104,7 @@ void FakeVimHandler::Private::setupCharClass()
|
||||
const QChar c = QLatin1Char(i);
|
||||
m_charClass[i] = c.isSpace() ? 0 : 1;
|
||||
}
|
||||
const QString conf = s.isKeyword.value();
|
||||
const QString conf = s.isKeyword();
|
||||
for (const QString &part : conf.split(',')) {
|
||||
if (part.contains('-')) {
|
||||
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)
|
||||
{
|
||||
if (s.passKeys.value()) {
|
||||
if (s.passKeys()) {
|
||||
if (tc.hasSelection() && text.isEmpty()) {
|
||||
QKeyEvent event(QEvent::KeyPress, Qt::Key_Delete, Qt::NoModifier, QString());
|
||||
passEventToEditor(event, tc);
|
||||
@@ -7937,7 +7935,7 @@ void FakeVimHandler::Private::joinLines(int count, bool preserveSpace)
|
||||
moveRight();
|
||||
|
||||
// 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) == '/')
|
||||
moveRight(2);
|
||||
else if (characterAtCursor() == '*' || characterAtCursor() == '#')
|
||||
@@ -7955,7 +7953,7 @@ void FakeVimHandler::Private::joinLines(int count, bool preserveSpace)
|
||||
|
||||
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");
|
||||
if (passEventToEditor(event, m_cursor))
|
||||
return;
|
||||
@@ -7967,7 +7965,7 @@ void FakeVimHandler::Private::insertNewLine()
|
||||
|
||||
bool FakeVimHandler::Private::handleInsertInEditor(const Input &input)
|
||||
{
|
||||
if (m_buffer->editBlockLevel > 0 || !s.passKeys.value())
|
||||
if (m_buffer->editBlockLevel > 0 || !s.passKeys())
|
||||
return false;
|
||||
|
||||
joinPreviousEditBlock();
|
||||
@@ -8712,10 +8710,10 @@ QString FakeVimHandler::Private::tabExpand(int n) const
|
||||
|
||||
void FakeVimHandler::Private::insertAutomaticIndentation(bool goingDown, bool forceAutoIndent)
|
||||
{
|
||||
if (!forceAutoIndent && !s.autoIndent.value() && !s.smartIndent.value())
|
||||
if (!forceAutoIndent && !s.autoIndent() && !s.smartIndent())
|
||||
return;
|
||||
|
||||
if (s.smartIndent.value()) {
|
||||
if (s.smartIndent()) {
|
||||
QTextBlock bl = block();
|
||||
Range range(bl.position(), bl.position());
|
||||
indentText(range, '\n');
|
||||
@@ -8734,7 +8732,7 @@ void FakeVimHandler::Private::insertAutomaticIndentation(bool goingDown, bool fo
|
||||
|
||||
void FakeVimHandler::Private::handleStartOfLine()
|
||||
{
|
||||
if (s.startOfLine.value())
|
||||
if (s.startOfLine())
|
||||
moveToFirstNonBlankOnLine();
|
||||
}
|
||||
|
||||
@@ -9331,7 +9329,7 @@ void FakeVimHandler::Private::getRegisterType(int *reg, bool *isClipboard, bool
|
||||
*reg = c.toLower().unicode();
|
||||
|
||||
if (c == '"') {
|
||||
QStringList list = s.clipboard.value().split(',');
|
||||
QStringList list = s.clipboard().split(',');
|
||||
clipboard = list.contains("unnamedplus");
|
||||
selection = list.contains("unnamed");
|
||||
} else if (c == '+') {
|
||||
@@ -9385,7 +9383,7 @@ void FakeVimHandler::updateGlobalMarksFilenames(const QString &oldFileName, cons
|
||||
bool FakeVimHandler::eventFilter(QObject *ob, QEvent *ev)
|
||||
{
|
||||
#ifndef FAKEVIM_STANDALONE
|
||||
if (!fakeVimSettings()->useFakeVim.value())
|
||||
if (!settings().useFakeVim())
|
||||
return QObject::eventFilter(ob, ev);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -340,134 +340,6 @@ private:
|
||||
using ExCommandMap = QMap<QString, QRegularExpression>;
|
||||
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);
|
||||
void handleExCommand(FakeVimHandler *handler, bool *handled, const ExCommand &cmd);
|
||||
|
||||
void writeSettings();
|
||||
void readSettings();
|
||||
|
||||
void handleDelayedQuitAll(bool forced);
|
||||
@@ -1107,7 +978,7 @@ IAssistProcessor *FakeVimCompletionAssistProvider::createProcessor(const AssistI
|
||||
class FakeVimPluginRunData
|
||||
{
|
||||
public:
|
||||
FakeVimOptionPage optionsPage;
|
||||
FakeVimSettings settings;
|
||||
FakeVimExCommandsPage exCommandsPage;
|
||||
FakeVimUserCommandsPage userCommandsPage;
|
||||
|
||||
@@ -1179,7 +1050,7 @@ void FakeVimPluginPrivate::initialize()
|
||||
|
||||
Command *cmd = nullptr;
|
||||
|
||||
cmd = ActionManager::registerAction(fakeVimSettings()->useFakeVim.action(),
|
||||
cmd = ActionManager::registerAction(settings().useFakeVim.action(),
|
||||
INSTALL_HANDLER, Context(Core::Constants::C_GLOBAL), true);
|
||||
cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+Shift+Y,Meta+Shift+Y")
|
||||
: Tr::tr("Alt+Y,Alt+Y")));
|
||||
@@ -1217,7 +1088,7 @@ void FakeVimPluginPrivate::initialize()
|
||||
connect(DocumentManager::instance(), &DocumentManager::documentRenamed,
|
||||
this, &FakeVimPluginPrivate::documentRenamed);
|
||||
|
||||
FakeVimSettings &s = *fakeVimSettings();
|
||||
FakeVimSettings &s = settings();
|
||||
connect(&s.useFakeVim, &FvBoolAspect::valueChanged,
|
||||
this, &FakeVimPluginPrivate::setUseFakeVim);
|
||||
connect(&s.readVimRc, &FvBaseAspect::changed,
|
||||
@@ -1235,7 +1106,7 @@ void FakeVimPluginPrivate::initialize()
|
||||
connect(this, &FakeVimPluginPrivate::delayedQuitAllRequested,
|
||||
this, &FakeVimPluginPrivate::handleDelayedQuitAll, Qt::QueuedConnection);
|
||||
|
||||
setCursorBlinking(s.blinkingCursor.value());
|
||||
setCursorBlinking(s.blinkingCursor());
|
||||
}
|
||||
|
||||
void FakeVimPluginPrivate::userActionTriggered(int key)
|
||||
@@ -1244,7 +1115,7 @@ void FakeVimPluginPrivate::userActionTriggered(int key)
|
||||
FakeVimHandler *handler = m_editorToHandler[editor].handler;
|
||||
if (handler) {
|
||||
// If disabled, enable FakeVim mode just for single user command.
|
||||
bool enableFakeVim = !fakeVimSettings()->useFakeVim.value();
|
||||
bool enableFakeVim = !settings().useFakeVim();
|
||||
if (enableFakeVim)
|
||||
setUseFakeVimInternal(true);
|
||||
|
||||
@@ -1270,26 +1141,18 @@ void FakeVimPluginPrivate::createRelativeNumberWidget(IEditor *editor)
|
||||
{
|
||||
if (auto textEditor = TextEditorWidget::fromEditor(editor)) {
|
||||
auto relativeNumbers = new RelativeNumbersColumn(textEditor);
|
||||
connect(&fakeVimSettings()->relativeNumber, &FvBaseAspect::changed,
|
||||
connect(&settings().relativeNumber, &FvBaseAspect::changed,
|
||||
relativeNumbers, &QObject::deleteLater);
|
||||
connect(&fakeVimSettings()->useFakeVim, &FvBaseAspect::changed,
|
||||
connect(&settings().useFakeVim, &FvBaseAspect::changed,
|
||||
relativeNumbers, &QObject::deleteLater);
|
||||
relativeNumbers->show();
|
||||
}
|
||||
}
|
||||
|
||||
void FakeVimPluginPrivate::writeSettings()
|
||||
{
|
||||
QSettings *settings = ICore::settings();
|
||||
fakeVimSettings()->writeSettings(settings);
|
||||
}
|
||||
|
||||
void FakeVimPluginPrivate::readSettings()
|
||||
{
|
||||
QSettings *settings = ICore::settings();
|
||||
|
||||
fakeVimSettings()->readSettings(settings);
|
||||
|
||||
m_exCommandMap = m_defaultExCommandMap;
|
||||
int size = settings->beginReadArray(exCommandMapGroup);
|
||||
for (int i = 0; i < size; ++i) {
|
||||
@@ -1318,9 +1181,9 @@ void FakeVimPluginPrivate::maybeReadVimRc()
|
||||
//qDebug() << theFakeVimSetting(ConfigReadVimRc)
|
||||
// << theFakeVimSetting(ConfigReadVimRc)->value();
|
||||
//qDebug() << theFakeVimSetting(ConfigShiftWidth)->value();
|
||||
if (!fakeVimSettings()->readVimRc.value())
|
||||
if (!settings().readVimRc())
|
||||
return;
|
||||
QString fileName = fakeVimSettings()->vimRcPath.value();
|
||||
QString fileName = settings().vimRcPath();
|
||||
if (fileName.isEmpty()) {
|
||||
fileName = QStandardPaths::writableLocation(QStandardPaths::HomeLocation)
|
||||
+ QLatin1String(HostOsInfo::isWindowsHost() ? "/_vimrc" : "/.vimrc");
|
||||
@@ -1330,7 +1193,6 @@ void FakeVimPluginPrivate::maybeReadVimRc()
|
||||
QPlainTextEdit editor;
|
||||
FakeVimHandler handler(&editor);
|
||||
handler.handleCommand("source " + fileName);
|
||||
//writeSettings();
|
||||
//qDebug() << theFakeVimSetting(ConfigShiftWidth)->value();
|
||||
}
|
||||
|
||||
@@ -1659,9 +1521,9 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
|
||||
return;
|
||||
|
||||
TabSettings tabSettings;
|
||||
tabSettings.m_indentSize = fakeVimSettings()->shiftWidth();
|
||||
tabSettings.m_tabSize = fakeVimSettings()->tabStop();
|
||||
tabSettings.m_tabPolicy = fakeVimSettings()->expandTab()
|
||||
tabSettings.m_indentSize = settings().shiftWidth();
|
||||
tabSettings.m_tabSize = settings().tabStop();
|
||||
tabSettings.m_tabPolicy = settings().expandTab()
|
||||
? TabSettings::SpacesOnlyTabPolicy : TabSettings::TabsOnlyTabPolicy;
|
||||
tabSettings.m_continuationAlignBehavior =
|
||||
tew->textDocument()->tabSettings().m_continuationAlignBehavior;
|
||||
@@ -1885,19 +1747,15 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
|
||||
*output = proc.cleanedStdOut();
|
||||
});
|
||||
|
||||
connect(ICore::instance(), &ICore::saveSettingsRequested,
|
||||
this, &FakeVimPluginPrivate::writeSettings);
|
||||
|
||||
|
||||
handler->setCurrentFileName(editor->document()->filePath().toString());
|
||||
handler->installEventFilter();
|
||||
|
||||
// pop up the bar
|
||||
if (fakeVimSettings()->useFakeVim.value()) {
|
||||
if (settings().useFakeVim()) {
|
||||
resetCommandBuffer();
|
||||
handler->setupWidget();
|
||||
|
||||
if (fakeVimSettings()->relativeNumber.value())
|
||||
if (settings().relativeNumber())
|
||||
createRelativeNumberWidget(editor);
|
||||
}
|
||||
}
|
||||
@@ -1939,8 +1797,8 @@ void FakeVimPluginPrivate::setUseFakeVim(bool on)
|
||||
//qDebug() << "SET USE FAKEVIM" << on;
|
||||
Find::setUseFakeVim(on);
|
||||
setUseFakeVimInternal(on);
|
||||
setShowRelativeLineNumbers(fakeVimSettings()->relativeNumber.value());
|
||||
setCursorBlinking(fakeVimSettings()->blinkingCursor.value());
|
||||
setShowRelativeLineNumbers(settings().relativeNumber());
|
||||
setCursorBlinking(settings().blinkingCursor());
|
||||
}
|
||||
|
||||
void FakeVimPluginPrivate::setUseFakeVimInternal(bool on)
|
||||
@@ -1968,7 +1826,7 @@ void FakeVimPluginPrivate::setUseFakeVimInternal(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)
|
||||
createRelativeNumberWidget(it.key());
|
||||
}
|
||||
@@ -1979,7 +1837,7 @@ void FakeVimPluginPrivate::setCursorBlinking(bool on)
|
||||
if (m_savedCursorFlashTime == 0)
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -2123,7 +1981,7 @@ void FakeVimPluginPrivate::handleDelayedQuitAll(bool forced)
|
||||
|
||||
void FakeVimPluginPrivate::quitFakeVim()
|
||||
{
|
||||
fakeVimSettings()->useFakeVim.setValue(false);
|
||||
settings().useFakeVim.setValue(false);
|
||||
}
|
||||
|
||||
void FakeVimPluginPrivate::resetCommandBuffer()
|
||||
|
||||
@@ -93,8 +93,8 @@ FossilCommitWidget::FossilCommitWidget() : m_commitPanel(new QWidget)
|
||||
m_invalidBranchLabel->setType(InfoLabel::Error);
|
||||
|
||||
m_isPrivateCheckBox = new QCheckBox(Tr::tr("Private"));
|
||||
m_isPrivateCheckBox->setToolTip(Tr::tr("Create a private check-in that is never synced.\n"
|
||||
"Children of private check-ins are automatically private.\n"
|
||||
m_isPrivateCheckBox->setToolTip("<html>" + Tr::tr("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."));
|
||||
|
||||
m_tagsLineEdit = new QLineEdit;
|
||||
|
||||
@@ -88,9 +88,9 @@ FossilSettings::FossilSettings()
|
||||
logCount.setToolTip(Tr::tr("The number of recent commit log entries to show. "
|
||||
"Choose 0 to see all entries."));
|
||||
|
||||
setLayouter([this](QWidget *widget) {
|
||||
setLayouter([this] {
|
||||
using namespace Layouting;
|
||||
Column {
|
||||
return Column {
|
||||
Group {
|
||||
title(Tr::tr("Configuration")),
|
||||
Row { binaryPath }
|
||||
@@ -117,7 +117,7 @@ FossilSettings::FossilSettings()
|
||||
},
|
||||
},
|
||||
st
|
||||
}.attachTo(widget);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ PullOrPushDialog::PullOrPushDialog(Mode mode, QWidget *parent)
|
||||
m_localPathChooser->setPromptDialogFilter(Tr::tr(Constants::FOSSIL_FILE_FILTER));
|
||||
|
||||
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->setEnabled(false);
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
{
|
||||
"name": "Repo",
|
||||
"trDisplayName": "Remote repository:",
|
||||
"trToolTip": "For example: https://[user[:pass]@]host[:port]/[path]",
|
||||
"trToolTip": "For example: \"https://[user[:pass]@]host[:port]/[path]\".",
|
||||
"type": "LineEdit",
|
||||
"enabled": "%{isCloneRepo}",
|
||||
"mandatory": false
|
||||
|
||||
@@ -43,8 +43,8 @@ public:
|
||||
curlChooser->setCommandVersionArguments({"-V"});
|
||||
|
||||
auto portSpinBox = new QSpinBox(this);
|
||||
portSpinBox->setValue(p->server.port);
|
||||
portSpinBox->setRange(1, 65535);
|
||||
portSpinBox->setValue(p->server.port);
|
||||
|
||||
auto httpsCheckBox = new QCheckBox(Git::Tr::tr("HTTPS"));
|
||||
httpsCheckBox->setChecked(p->https);
|
||||
|
||||
@@ -441,7 +441,7 @@ ShowController::ShowController(IDocument *document, const QString &id)
|
||||
};
|
||||
|
||||
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) {
|
||||
tasks.append(ProcessTask(std::bind(setupFollow, _1, parents.at(i)),
|
||||
std::bind(onFollowDone, _1, i)));
|
||||
@@ -465,11 +465,11 @@ ShowController::ShowController(IDocument *document, const QString &id)
|
||||
parallel,
|
||||
onGroupSetup([this] { setStartupFile(VcsBase::source(this->document()).toString()); }),
|
||||
Group {
|
||||
optional,
|
||||
finishAllAndDone,
|
||||
ProcessTask(setupDescription, onDescriptionDone),
|
||||
Group {
|
||||
parallel,
|
||||
optional,
|
||||
finishAllAndDone,
|
||||
onGroupSetup(desciptionDetailsSetup),
|
||||
ProcessTask(setupBranches, onBranchesDone, onBranchesError),
|
||||
ProcessTask(setupPrecedes, onPrecedesDone, onPrecedesError),
|
||||
@@ -2873,7 +2873,7 @@ bool GitClient::addAndCommit(const FilePath &repositoryDirectory,
|
||||
GitPlugin::updateCurrentBranch();
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,13 +21,12 @@
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QFuture>
|
||||
#include <QHBoxLayout>
|
||||
#include <QRegularExpressionValidator>
|
||||
#include <QSettings>
|
||||
#include <QTextStream>
|
||||
|
||||
using namespace Core;
|
||||
using namespace TextEditor;
|
||||
using namespace Utils;
|
||||
using namespace VcsBase;
|
||||
|
||||
@@ -43,91 +42,106 @@ public:
|
||||
QString id() const { return recurseSubmodules ? ref + ".Rec" : ref; }
|
||||
};
|
||||
|
||||
class GitGrepRunner
|
||||
static QStringView nextLine(QStringView *remainingInput)
|
||||
{
|
||||
using PromiseType = QPromise<SearchResultItems>;
|
||||
|
||||
public:
|
||||
GitGrepRunner(const TextEditor::FileFindParameters ¶meters)
|
||||
: m_parameters(parameters)
|
||||
{
|
||||
m_directory = FilePath::fromString(parameters.additionalParameters.toString());
|
||||
m_vcsBinary = GitClient::instance()->vcsBinary();
|
||||
m_environment = GitClient::instance()->processEnvironment();
|
||||
const int newLinePos = remainingInput->indexOf('\n');
|
||||
if (newLinePos < 0) {
|
||||
QStringView ret = *remainingInput;
|
||||
*remainingInput = QStringView();
|
||||
return ret;
|
||||
}
|
||||
QStringView ret = remainingInput->left(newLinePos);
|
||||
*remainingInput = remainingInput->mid(newLinePos + 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct Match
|
||||
{
|
||||
Match() = default;
|
||||
Match(int start, int length) :
|
||||
matchStart(start), matchLength(length) {}
|
||||
struct Match
|
||||
{
|
||||
Match() = default;
|
||||
Match(int start, int length) :
|
||||
matchStart(start), matchLength(length) {}
|
||||
|
||||
int matchStart = 0;
|
||||
int matchLength = 0;
|
||||
QStringList regexpCapturedTexts;
|
||||
};
|
||||
int matchStart = 0;
|
||||
int matchLength = 0;
|
||||
QStringList regexpCapturedTexts;
|
||||
};
|
||||
|
||||
void processLine(const QString &line, SearchResultItems *resultList) const
|
||||
{
|
||||
static void processLine(QStringView line, SearchResultItems *resultList,
|
||||
const std::optional<QRegularExpression> ®Exp, 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> ®Exp, 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())
|
||||
return;
|
||||
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);
|
||||
continue;
|
||||
|
||||
for (const auto &match : std::as_const(matches)) {
|
||||
result.setMainRange(lineNumber, match.matchStart, match.matchLength);
|
||||
result.setUserData(match.regexpCapturedTexts);
|
||||
resultList->append(result);
|
||||
}
|
||||
processLine(line, &items, regExp, ref, directory);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
void read(PromiseType &fi, const QString &text)
|
||||
{
|
||||
SearchResultItems resultList;
|
||||
QString t = text;
|
||||
QTextStream stream(&t);
|
||||
while (!stream.atEnd() && !fi.isCanceled())
|
||||
processLine(stream.readLine(), &resultList);
|
||||
if (!resultList.isEmpty() && !fi.isCanceled())
|
||||
fi.addResult(resultList);
|
||||
}
|
||||
static void runGitGrep(QPromise<SearchResultItems> &promise, const FileFindParameters ¶meters)
|
||||
{
|
||||
const FilePath directory = FilePath::fromString(parameters.additionalParameters.toString());
|
||||
const GitGrepParameters gitParameters
|
||||
= parameters.searchEngineParameters.value<GitGrepParameters>();
|
||||
const QString ref = gitParameters.ref.isEmpty() ? QString() : gitParameters.ref + ':';
|
||||
|
||||
const auto setupProcess = [&](Process &process) {
|
||||
const FilePath vcsBinary = GitClient::instance()->vcsBinary();
|
||||
const Environment environment = GitClient::instance()->processEnvironment();
|
||||
|
||||
void operator()(PromiseType &promise)
|
||||
{
|
||||
QStringList arguments = {
|
||||
"-c", "color.grep.match=bold red",
|
||||
"-c", "color.grep=always",
|
||||
@@ -135,60 +149,41 @@ public:
|
||||
"-c", "color.grep.lineNumber=",
|
||||
"grep", "-zn", "--no-full-name"
|
||||
};
|
||||
if (!(m_parameters.flags & FindCaseSensitively))
|
||||
if (!(parameters.flags & FindCaseSensitively))
|
||||
arguments << "-i";
|
||||
if (m_parameters.flags & FindWholeWords)
|
||||
if (parameters.flags & FindWholeWords)
|
||||
arguments << "-w";
|
||||
if (m_parameters.flags & FindRegularExpression)
|
||||
if (parameters.flags & FindRegularExpression)
|
||||
arguments << "-P";
|
||||
else
|
||||
arguments << "-F";
|
||||
arguments << "-e" << m_parameters.text;
|
||||
GitGrepParameters params = m_parameters.searchEngineParameters.value<GitGrepParameters>();
|
||||
if (params.recurseSubmodules)
|
||||
arguments << "-e" << parameters.text;
|
||||
if (gitParameters.recurseSubmodules)
|
||||
arguments << "--recurse-submodules";
|
||||
if (!params.ref.isEmpty()) {
|
||||
arguments << params.ref;
|
||||
m_ref = params.ref + ':';
|
||||
if (!gitParameters.ref.isEmpty()) {
|
||||
arguments << gitParameters.ref;
|
||||
}
|
||||
const QStringList filterArgs =
|
||||
m_parameters.nameFilters.isEmpty() ? QStringList("*") // needed for exclusion filters
|
||||
: m_parameters.nameFilters;
|
||||
parameters.nameFilters.isEmpty() ? QStringList("*") // needed for exclusion filters
|
||||
: parameters.nameFilters;
|
||||
const QStringList exclusionArgs =
|
||||
Utils::transform(m_parameters.exclusionFilters, [](const QString &filter) {
|
||||
return QString(":!" + filter);
|
||||
});
|
||||
Utils::transform(parameters.exclusionFilters, [](const QString &filter) {
|
||||
return QString(":!" + filter);
|
||||
});
|
||||
arguments << "--" << filterArgs << exclusionArgs;
|
||||
|
||||
Process process;
|
||||
process.setEnvironment(m_environment);
|
||||
process.setCommand({m_vcsBinary, arguments});
|
||||
process.setWorkingDirectory(m_directory);
|
||||
process.setStdOutCallback([this, &promise](const QString &text) { read(promise, text); });
|
||||
process.start();
|
||||
process.waitForFinished();
|
||||
process.setEnvironment(environment);
|
||||
process.setCommand({vcsBinary, arguments});
|
||||
process.setWorkingDirectory(directory);
|
||||
};
|
||||
|
||||
switch (process.result()) {
|
||||
case ProcessResult::TerminatedAbnormally:
|
||||
case ProcessResult::StartFailed:
|
||||
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;
|
||||
}
|
||||
}
|
||||
const auto outputParser = [&ref, &directory](const QFuture<void> &future, const QString &input,
|
||||
const std::optional<QRegularExpression> ®Exp) {
|
||||
return parse(future, input, regExp, ref, directory);
|
||||
};
|
||||
|
||||
private:
|
||||
FilePath m_vcsBinary;
|
||||
FilePath m_directory;
|
||||
QString m_ref;
|
||||
TextEditor::FileFindParameters m_parameters;
|
||||
Environment m_environment;
|
||||
};
|
||||
TextEditor::searchInProcessOutput(promise, parameters, setupProcess, outputParser);
|
||||
}
|
||||
|
||||
static bool isGitDirectory(const FilePath &path)
|
||||
{
|
||||
@@ -211,18 +206,16 @@ GitGrep::GitGrep(GitClient *client)
|
||||
m_treeLineEdit->setValidator(new QRegularExpressionValidator(refExpression, this));
|
||||
layout->addWidget(m_treeLineEdit);
|
||||
// asynchronously check git version, add "recurse submodules" option if available
|
||||
Utils::onResultReady(client->gitVersion(),
|
||||
this,
|
||||
Utils::onResultReady(client->gitVersion(), this,
|
||||
[this, pLayout = QPointer<QHBoxLayout>(layout)](unsigned version) {
|
||||
if (version >= 0x021300 && pLayout) {
|
||||
m_recurseSubmodules = new QCheckBox(Tr::tr("Recurse submodules"));
|
||||
pLayout->addWidget(m_recurseSubmodules);
|
||||
}
|
||||
});
|
||||
TextEditor::FindInFiles *findInFiles = TextEditor::FindInFiles::instance();
|
||||
if (version >= 0x021300 && pLayout) {
|
||||
m_recurseSubmodules = new QCheckBox(Tr::tr("Recurse submodules"));
|
||||
pLayout->addWidget(m_recurseSubmodules);
|
||||
}
|
||||
});
|
||||
FindInFiles *findInFiles = FindInFiles::instance();
|
||||
QTC_ASSERT(findInFiles, return);
|
||||
connect(findInFiles, &TextEditor::FindInFiles::pathChanged,
|
||||
m_widget, [this](const FilePath &path) {
|
||||
connect(findInFiles, &FindInFiles::pathChanged, m_widget, [this](const FilePath &path) {
|
||||
setEnabled(isGitDirectory(path));
|
||||
});
|
||||
connect(this, &SearchEngine::enabledChanged, m_widget, &QWidget::setEnabled);
|
||||
@@ -271,14 +264,14 @@ void GitGrep::writeSettings(QSettings *settings) const
|
||||
settings->setValue(GitGrepRef, m_treeLineEdit->text());
|
||||
}
|
||||
|
||||
QFuture<SearchResultItems> GitGrep::executeSearch(const TextEditor::FileFindParameters ¶meters,
|
||||
TextEditor::BaseFileFind * /*baseFileFind*/)
|
||||
QFuture<SearchResultItems> GitGrep::executeSearch(const FileFindParameters ¶meters,
|
||||
BaseFileFind *)
|
||||
{
|
||||
return Utils::asyncRun(GitGrepRunner(parameters));
|
||||
return Utils::asyncRun(runGitGrep, parameters);
|
||||
}
|
||||
|
||||
IEditor *GitGrep::openEditor(const SearchResultItem &item,
|
||||
const TextEditor::FileFindParameters ¶meters)
|
||||
const FileFindParameters ¶meters)
|
||||
{
|
||||
const GitGrepParameters params = parameters.searchEngineParameters.value<GitGrepParameters>();
|
||||
const QStringList &itemPath = item.path();
|
||||
|
||||
@@ -9,10 +9,7 @@ QT_BEGIN_NAMESPACE
|
||||
class QCheckBox;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Utils {
|
||||
class FancyLineEdit;
|
||||
class SearchResultItem;
|
||||
}
|
||||
namespace Utils { class FancyLineEdit; }
|
||||
|
||||
namespace Git::Internal {
|
||||
|
||||
|
||||
@@ -90,8 +90,8 @@ GitSettings::GitSettings()
|
||||
instantBlame.setSettingsKey("Git Instant");
|
||||
instantBlame.setDefaultValue(true);
|
||||
instantBlame.setLabelText(Tr::tr("Add instant blame annotations to editor"));
|
||||
instantBlame.setToolTip(Tr::tr("Directly annotate each line in the editor "
|
||||
"when scrolling through the document."));
|
||||
instantBlame.setToolTip(
|
||||
Tr::tr("Annotate the current line in the editor with Git \"blame\" output."));
|
||||
|
||||
graphLog.setSettingsKey("GraphLog");
|
||||
|
||||
|
||||
@@ -356,6 +356,7 @@ void Client::setName(const QString &name)
|
||||
QString Client::name() const
|
||||
{
|
||||
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 d->m_displayName;
|
||||
}
|
||||
@@ -555,11 +556,17 @@ Client::State Client::state() const
|
||||
QString Client::stateString() const
|
||||
{
|
||||
switch (d->m_state){
|
||||
//: language client state
|
||||
case Uninitialized: return Tr::tr("uninitialized");
|
||||
//: language client state
|
||||
case InitializeRequested: return Tr::tr("initialize requested");
|
||||
//: language client state
|
||||
case Initialized: return Tr::tr("initialized");
|
||||
//: language client state
|
||||
case ShutdownRequested: return Tr::tr("shutdown requested");
|
||||
//: language client state
|
||||
case Shutdown: return Tr::tr("shut down");
|
||||
//: language client state
|
||||
case Error: return Tr::tr("error");
|
||||
}
|
||||
return {};
|
||||
@@ -1970,7 +1977,7 @@ void ClientPrivate::initializeCallback(const InitializeRequest::Response &initRe
|
||||
if (std::optional<ResponseError<InitializeError>> error = initResponse.error()) {
|
||||
if (std::optional<InitializeError> data = error->data()) {
|
||||
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(),
|
||||
title,
|
||||
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();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <languageserverprotocol/progresssupport.h>
|
||||
|
||||
#include <QTime>
|
||||
#include <QTimer>
|
||||
|
||||
using namespace LanguageServerProtocol;
|
||||
|
||||
@@ -81,9 +82,28 @@ void ProgressManager::beginProgress(const ProgressToken &token, const WorkDonePr
|
||||
auto interface = new QFutureInterface<void>();
|
||||
interface->reportStarted();
|
||||
interface->setProgressRange(0, 100); // LSP always reports percentage of the task
|
||||
const QString title = m_titles.value(token, begin.title());
|
||||
Core::FutureProgress *progress = Core::ProgressManager::addTask(
|
||||
interface->future(), title, languageClientProgressId(token));
|
||||
ProgressItem progressItem;
|
||||
progressItem.futureInterface = interface;
|
||||
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);
|
||||
if (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);
|
||||
else
|
||||
progress->setCancelEnabled(false);
|
||||
m_progress[token] = {progress, interface};
|
||||
if (LOGPROGRESS().isDebugEnabled())
|
||||
m_timer[token].start();
|
||||
reportProgress(token, begin);
|
||||
if (!progressItem.message.isEmpty()) {
|
||||
progress->setSubtitle(progressItem.message);
|
||||
progress->setSubtitleVisibleInStatusBar(true);
|
||||
}
|
||||
progressItem.progressInterface = progress;
|
||||
}
|
||||
|
||||
void ProgressManager::reportProgress(const ProgressToken &token,
|
||||
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) {
|
||||
const std::optional<QString> &message = report.message();
|
||||
if (message.has_value()) {
|
||||
progress.progressInterface->setSubtitle(*message);
|
||||
const bool showSubtitle = !message->isEmpty();
|
||||
progress.progressInterface->setSubtitleVisibleInStatusBar(showSubtitle);
|
||||
}
|
||||
} else if (message.has_value()) {
|
||||
progress.message = *message;
|
||||
}
|
||||
if (progress.futureInterface) {
|
||||
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)
|
||||
{
|
||||
const LanguageClientProgress &progress = m_progress.value(token);
|
||||
const ProgressItem &progress = m_progress.value(token);
|
||||
const QString &message = end.message().value_or(QString());
|
||||
if (progress.progressInterface) {
|
||||
if (!message.isEmpty()) {
|
||||
@@ -127,11 +150,11 @@ void ProgressManager::endProgress(const ProgressToken &token, const WorkDoneProg
|
||||
}
|
||||
progress.progressInterface->setSubtitle(message);
|
||||
progress.progressInterface->setSubtitleVisibleInStatusBar(!message.isEmpty());
|
||||
auto timer = m_timer.take(token);
|
||||
if (timer.isValid()) {
|
||||
if (progress.timer.isValid()) {
|
||||
qCDebug(LOGPROGRESS) << QString("%1 took %2")
|
||||
.arg(progress.progressInterface->title())
|
||||
.arg(QTime::fromMSecsSinceStartOfDay(timer.elapsed())
|
||||
.arg(QTime::fromMSecsSinceStartOfDay(
|
||||
progress.timer.elapsed())
|
||||
.toString(Qt::ISODateWithMs));
|
||||
}
|
||||
}
|
||||
@@ -140,7 +163,8 @@ void ProgressManager::endProgress(const ProgressToken &token, const WorkDoneProg
|
||||
|
||||
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)
|
||||
progress.futureInterface->reportFinished();
|
||||
delete progress.futureInterface;
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
#include <QFutureInterface>
|
||||
#include <QPointer>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QTimer;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace LanguageServerProtocol {
|
||||
class ProgressParams;
|
||||
class ProgressToken;
|
||||
@@ -46,15 +50,20 @@ private:
|
||||
const LanguageServerProtocol::WorkDoneProgressReport &report);
|
||||
void endProgress(const LanguageServerProtocol::ProgressToken &token,
|
||||
const LanguageServerProtocol::WorkDoneProgressEnd &end);
|
||||
void spawnProgressBar(const LanguageServerProtocol::ProgressToken &token);
|
||||
|
||||
struct LanguageClientProgress {
|
||||
struct ProgressItem
|
||||
{
|
||||
QPointer<Core::FutureProgress> progressInterface = 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, QElapsedTimer> m_timer;
|
||||
QMap<LanguageServerProtocol::ProgressToken, std::function<void()>> m_clickHandlers;
|
||||
QMap<LanguageServerProtocol::ProgressToken, std::function<void()>> m_cancelHandlers;
|
||||
};
|
||||
|
||||
@@ -9,7 +9,6 @@ add_qtc_plugin(Macros
|
||||
macrolocatorfilter.cpp macrolocatorfilter.h
|
||||
macromanager.cpp macromanager.h
|
||||
macrooptionspage.cpp macrooptionspage.h
|
||||
macrooptionswidget.cpp macrooptionswidget.h
|
||||
macros.qrc
|
||||
macrosconstants.h
|
||||
macrosplugin.cpp macrosplugin.h
|
||||
|
||||
@@ -3,15 +3,195 @@
|
||||
|
||||
#include "macrooptionspage.h"
|
||||
|
||||
#include "macro.h"
|
||||
#include "macromanager.h"
|
||||
#include "macrooptionswidget.h"
|
||||
#include "macrosconstants.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>
|
||||
|
||||
namespace Macros {
|
||||
namespace Internal {
|
||||
#include <utils/layoutbuilder.h>
|
||||
|
||||
#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()
|
||||
{
|
||||
@@ -21,5 +201,4 @@ MacroOptionsPage::MacroOptionsPage()
|
||||
setWidgetCreator([] { return new MacroOptionsWidget; });
|
||||
}
|
||||
|
||||
} // Internal
|
||||
} // Macros
|
||||
} // Macros::Internal
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
|
||||
#include <coreplugin/dialogs/ioptionspage.h>
|
||||
|
||||
namespace Macros {
|
||||
namespace Internal {
|
||||
namespace Macros::Internal {
|
||||
|
||||
class MacroOptionsPage final : public Core::IOptionsPage
|
||||
{
|
||||
@@ -14,5 +13,4 @@ public:
|
||||
MacroOptionsPage();
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Macros
|
||||
} // Macros::Internal
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -29,8 +29,6 @@ QtcPlugin {
|
||||
"macromanager.h",
|
||||
"macrooptionspage.cpp",
|
||||
"macrooptionspage.h",
|
||||
"macrooptionswidget.cpp",
|
||||
"macrooptionswidget.h",
|
||||
"macros.qrc",
|
||||
"macrosconstants.h",
|
||||
"macrosplugin.cpp",
|
||||
|
||||
@@ -24,7 +24,7 @@ add_qtc_plugin(McuSupport
|
||||
settingshandler.cpp settingshandler.h
|
||||
mcuqmlprojectnode.cpp mcuqmlprojectnode.h
|
||||
mcubuildstep.cpp mcubuildstep.h
|
||||
dialogs/mcukitcreationdialog.h dialogs/mcukitcreationdialog.cpp dialogs/mcukitcreationdialog.ui
|
||||
dialogs/mcukitcreationdialog.cpp dialogs/mcukitcreationdialog.h
|
||||
)
|
||||
|
||||
add_subdirectory(test)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user