forked from qt-creator/qt-creator
Merge remote-tracking branch 'origin/master' into 8.0
Change-Id: I0ab7200a8696e52122b2739cbc740e940336f962
This commit is contained in:
@@ -837,6 +837,8 @@ void Qt5InformationNodeInstanceServer::updateActiveSceneToEditView3D(bool timerC
|
|||||||
auto helper = qobject_cast<QmlDesigner::Internal::GeneralHelper *>(m_3dHelper);
|
auto helper = qobject_cast<QmlDesigner::Internal::GeneralHelper *>(m_3dHelper);
|
||||||
if (helper)
|
if (helper)
|
||||||
helper->storeToolState(helper->globalStateId(), helper->lastSceneIdKey(), QVariant(sceneId), 0);
|
helper->storeToolState(helper->globalStateId(), helper->lastSceneIdKey(), QVariant(sceneId), 0);
|
||||||
|
#else
|
||||||
|
Q_UNUSED(timerCall)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1157,6 +1159,8 @@ void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView(const Reques
|
|||||||
{PuppetToCreatorCommand::RenderModelNodePreviewImage,
|
{PuppetToCreatorCommand::RenderModelNodePreviewImage,
|
||||||
QVariant::fromValue(imgContainer)});
|
QVariant::fromValue(imgContainer)});
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
Q_UNUSED(cmd)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -82,6 +82,9 @@ void Quick3DNodeInstance::initialize(const ObjectNodeInstance::Pointer &objectNo
|
|||||||
m_dummyRootViewCreateFunction = "createViewForNode";
|
m_dummyRootViewCreateFunction = "createViewForNode";
|
||||||
|
|
||||||
Quick3DRenderableNodeInstance::initialize(objectNodeInstance, flags);
|
Quick3DRenderableNodeInstance::initialize(objectNodeInstance, flags);
|
||||||
|
#else
|
||||||
|
Q_UNUSED(objectNodeInstance)
|
||||||
|
Q_UNUSED(flags)
|
||||||
#endif // QUICK3D_MODULE
|
#endif // QUICK3D_MODULE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -32121,16 +32121,16 @@ Spustil jste Qemu?</translation>
|
|||||||
<translation>Qt %1 (%2)</translation>
|
<translation>Qt %1 (%2)</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>qmake does not exist or is not executable</source>
|
<source>%1 does not exist or is not executable</source>
|
||||||
<translation>Soubor qmake neexistuje nebo není spustitelný</translation>
|
<translation>Soubor %1 neexistuje nebo není spustitelný</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Qt version is not properly installed, please run make install</source>
|
<source>Qt version is not properly installed, please run make install</source>
|
||||||
<translation>Verze Qt není správně nainstalována. Proveďte, prosím, příkaz "make install"</translation>
|
<translation>Verze Qt není správně nainstalována. Proveďte, prosím, příkaz "make install"</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong?</source>
|
<source>Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong?</source>
|
||||||
<translation>Cestu ke spustitelným souborům instalace Qt se nepodařilo určit. Možná je cesta k qmake chybná?</translation>
|
<translation>Cestu ke spustitelným souborům instalace Qt se nepodařilo určit. Možná je cesta k %1 chybná?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>The default mkspec symlink is broken.</source>
|
<source>The default mkspec symlink is broken.</source>
|
||||||
|
@@ -35382,16 +35382,16 @@ For flere detaljer, se /etc/sysctl.d/10-ptrace.conf
|
|||||||
<translation>Ingen qmake-sti sat</translation>
|
<translation>Ingen qmake-sti sat</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>qmake does not exist or is not executable</source>
|
<source>%1 does not exist or is not executable</source>
|
||||||
<translation>qmake findes ikke eller er ikke eksekverbar</translation>
|
<translation>%1 findes ikke eller er ikke eksekverbar</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Qt version is not properly installed, please run make install</source>
|
<source>Qt version is not properly installed, please run make install</source>
|
||||||
<translation>Qt version er ikke ordentligt installeret, kør venligst make install</translation>
|
<translation>Qt version er ikke ordentligt installeret, kør venligst make install</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong?</source>
|
<source>Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong?</source>
|
||||||
<translation>Kunne ikke beslutte stien til binærene af Qt installationen, måske er qmake-stien forkert?</translation>
|
<translation>Kunne ikke beslutte stien til binærene af Qt installationen, måske er %1-stien forkert?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>The default mkspec symlink is broken.</source>
|
<source>The default mkspec symlink is broken.</source>
|
||||||
|
@@ -10113,8 +10113,8 @@ Dies ist unabhängig vom Wert der Eigenschaft "visible" in QML.</trans
|
|||||||
<translation>Es ist keine qmake-Pfad gesetzt</translation>
|
<translation>Es ist keine qmake-Pfad gesetzt</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>qmake does not exist or is not executable</source>
|
<source>%1 does not exist or is not executable</source>
|
||||||
<translation>Die qmake-Datei existiert nicht oder ist nicht ausführbar</translation>
|
<translation>Die %1-Datei existiert nicht oder ist nicht ausführbar</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Qt version has no name</source>
|
<source>Qt version has no name</source>
|
||||||
@@ -10141,8 +10141,8 @@ Dies ist unabhängig vom Wert der Eigenschaft "visible" in QML.</trans
|
|||||||
<translation>Die Qt-Version ist nicht richtig installiert, führen Sie bitte make install aus</translation>
|
<translation>Die Qt-Version ist nicht richtig installiert, führen Sie bitte make install aus</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong?</source>
|
<source>Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong?</source>
|
||||||
<translation>Der Pfad zu den ausführbaren Dateien der Qt-Installation konnte nicht bestimmt werden, möglicherweise ist der Pfad zu qmake falsch?</translation>
|
<translation>Der Pfad zu den ausführbaren Dateien der Qt-Installation konnte nicht bestimmt werden, möglicherweise ist der Pfad zu %1 falsch?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>The default mkspec symlink is broken.</source>
|
<source>The default mkspec symlink is broken.</source>
|
||||||
|
@@ -33604,8 +33604,8 @@ Nous allons essayer de travailler avec cela mais vous pourrez rencontrer des pro
|
|||||||
<translation>Chemin de qmake non spécifié</translation>
|
<translation>Chemin de qmake non spécifié</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>qmake does not exist or is not executable</source>
|
<source>%1 does not exist or is not executable</source>
|
||||||
<translation>qmake n'existe pas ou n'est pas exécutable</translation>
|
<translation>%1 n'existe pas ou n'est pas exécutable</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Qt version has no name</source>
|
<source>Qt version has no name</source>
|
||||||
@@ -33633,8 +33633,8 @@ Nous allons essayer de travailler avec cela mais vous pourrez rencontrer des pro
|
|||||||
<translation>La version de Qt n'est pas correctement installée, veuillez exécuter make install</translation>
|
<translation>La version de Qt n'est pas correctement installée, veuillez exécuter make install</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong?</source>
|
<source>Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong?</source>
|
||||||
<translation>Impossible de déterminer le chemin vers les programmes de Qt, peut-être que le chemin vers qmake est faux ?</translation>
|
<translation>Impossible de déterminer le chemin vers les programmes de Qt, peut-être que le chemin vers %1 est faux ?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>The default mkspec symlink is broken.</source>
|
<source>The default mkspec symlink is broken.</source>
|
||||||
|
@@ -41628,7 +41628,7 @@ Saving failed.</source>
|
|||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>qmake does not exist or is not executable</source>
|
<source>%1 does not exist or is not executable</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
@@ -41636,7 +41636,7 @@ Saving failed.</source>
|
|||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong?</source>
|
<source>Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong?</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
|
@@ -26108,16 +26108,16 @@ Do you want to save the data first?</source>
|
|||||||
<translation>qmake のパスが設定されていません</translation>
|
<translation>qmake のパスが設定されていません</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>qmake does not exist or is not executable</source>
|
<source>%1 does not exist or is not executable</source>
|
||||||
<translation>qmake が存在しないか実行可能ではありません</translation>
|
<translation>%1 が存在しないか実行可能ではありません</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Qt version is not properly installed, please run make install</source>
|
<source>Qt version is not properly installed, please run make install</source>
|
||||||
<translation>Qt バージョンが正しくインストールされていません。make install を実行してください</translation>
|
<translation>Qt バージョンが正しくインストールされていません。make install を実行してください</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong?</source>
|
<source>Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong?</source>
|
||||||
<translation>Qt インストール先のパスが特定できませんでした。qmake のパスが間違っていませんか?</translation>
|
<translation>Qt インストール先のパスが特定できませんでした。%1 のパスが間違っていませんか?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>The default mkspec symlink is broken.</source>
|
<source>The default mkspec symlink is broken.</source>
|
||||||
|
@@ -12382,8 +12382,8 @@ Dla projektów CMake, upewnij się, że zmienna QML_IMPORT_PATH jest obecna w CM
|
|||||||
<translation>Nie ustawiono ścieżki do qmake</translation>
|
<translation>Nie ustawiono ścieżki do qmake</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>qmake does not exist or is not executable</source>
|
<source>%1 does not exist or is not executable</source>
|
||||||
<translation>Brak qmake lub nie jest on plikiem wykonywalnym</translation>
|
<translation>Brak %1 lub nie jest on plikiem wykonywalnym</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Qt version has no name</source>
|
<source>Qt version has no name</source>
|
||||||
@@ -12410,8 +12410,8 @@ Dla projektów CMake, upewnij się, że zmienna QML_IMPORT_PATH jest obecna w CM
|
|||||||
<translation>Wersja Qt zainstalowana niepoprawnie, uruchom komendę: make install</translation>
|
<translation>Wersja Qt zainstalowana niepoprawnie, uruchom komendę: make install</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong?</source>
|
<source>Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong?</source>
|
||||||
<translation>Nie można określić ścieżki do plików binarnych instalacji Qt. Sprawdź ścieżkę do qmake.</translation>
|
<translation>Nie można określić ścieżki do plików binarnych instalacji Qt. Sprawdź ścieżkę do %1.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>The default mkspec symlink is broken.</source>
|
<source>The default mkspec symlink is broken.</source>
|
||||||
|
@@ -45319,8 +45319,8 @@ For more details, see /etc/sysctl.d/10-ptrace.conf
|
|||||||
<translation>Путь к qmake не указан</translation>
|
<translation>Путь к qmake не указан</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>qmake does not exist or is not executable</source>
|
<source>%1 does not exist or is not executable</source>
|
||||||
<translation>qmake отсутствует или не запускается</translation>
|
<translation>%1 отсутствует или не запускается</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Qt version has no name</source>
|
<source>Qt version has no name</source>
|
||||||
@@ -45347,8 +45347,8 @@ For more details, see /etc/sysctl.d/10-ptrace.conf
|
|||||||
<translation>Профиль Qt не установлен, пожалуйста выполните make install</translation>
|
<translation>Профиль Qt не установлен, пожалуйста выполните make install</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong?</source>
|
<source>Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong?</source>
|
||||||
<translation>Не удалось определить путь к утилитам Qt. Может путь к qmake неверен?</translation>
|
<translation>Не удалось определить путь к утилитам Qt. Может путь к %1 неверен?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>The default mkspec symlink is broken.</source>
|
<source>The default mkspec symlink is broken.</source>
|
||||||
|
@@ -21803,8 +21803,8 @@ Projekte programov QML izvede pregledovalnik QML in jih ni potrebno zgraditi.</t
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+58"/>
|
<location line="+58"/>
|
||||||
<source>qmake does not exist or is not executable</source>
|
<source>%1 does not exist or is not executable</source>
|
||||||
<translation>Datoteka »qmake« ne obstaja ali pa ni izvršljiva</translation>
|
<translation>Datoteka »%1« ne obstaja ali pa ni izvršljiva</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
@@ -21813,8 +21813,8 @@ Projekte programov QML izvede pregledovalnik QML in jih ni potrebno zgraditi.</t
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+2"/>
|
<location line="+2"/>
|
||||||
<source>Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong?</source>
|
<source>Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong?</source>
|
||||||
<translation>Ni bilo moč ugotoviti poti do programov namestitve Qt. Morda je pot do qmake napačna?</translation>
|
<translation>Ni bilo moč ugotoviti poti do programov namestitve Qt. Morda je pot do %1 napačna?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+3"/>
|
<location line="+3"/>
|
||||||
|
@@ -22711,8 +22711,8 @@ For more details, see /etc/sysctl.d/10-ptrace.conf
|
|||||||
<translation>Шлях до qmake не задано</translation>
|
<translation>Шлях до qmake не задано</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>qmake does not exist or is not executable</source>
|
<source>%1 does not exist or is not executable</source>
|
||||||
<translation>qmake не існує або не є виконуваним модулем</translation>
|
<translation>%1 не існує або не є виконуваним модулем</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>ABI detection failed: Make sure to use a matching compiler when building.</source>
|
<source>ABI detection failed: Make sure to use a matching compiler when building.</source>
|
||||||
@@ -22763,8 +22763,8 @@ For more details, see /etc/sysctl.d/10-ptrace.conf
|
|||||||
<translation>Версія Qt не встановлена як слід, будь ласка, запустіть make install</translation>
|
<translation>Версія Qt не встановлена як слід, будь ласка, запустіть make install</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong?</source>
|
<source>Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong?</source>
|
||||||
<translation>Не вдалось визначити шлях до виконуваних модулів встановлення Qt, можливо, шлях до qmake помилковий?</translation>
|
<translation>Не вдалось визначити шлях до виконуваних модулів встановлення Qt, можливо, шлях до %1 помилковий?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>The default mkspec symlink is broken.</source>
|
<source>The default mkspec symlink is broken.</source>
|
||||||
|
@@ -27948,8 +27948,8 @@ Did you start Qemu?</source>
|
|||||||
<translation>没有设置qmake路径</translation>
|
<translation>没有设置qmake路径</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>qmake does not exist or is not executable</source>
|
<source>%1 does not exist or is not executable</source>
|
||||||
<translation>qmake不存在或者不可执行</translation>
|
<translation>%1不存在或者不可执行</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Qt version has no name</source>
|
<source>Qt version has no name</source>
|
||||||
@@ -27976,8 +27976,8 @@ Did you start Qemu?</source>
|
|||||||
<translation>Qt没有被正确安装,请运行make install</translation>
|
<translation>Qt没有被正确安装,请运行make install</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong?</source>
|
<source>Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong?</source>
|
||||||
<translation>无法确定Qt安装的二进制所在的路径,或许qmake的路径设置出现了错误?</translation>
|
<translation>无法确定Qt安装的二进制所在的路径,或许%1的路径设置出现了错误?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>The default mkspec symlink is broken.</source>
|
<source>The default mkspec symlink is broken.</source>
|
||||||
|
@@ -15636,8 +15636,8 @@ Requires <b>Qt 4.7.4</b> or newer.</source>
|
|||||||
<translation>沒有設定 qmake 路徑</translation>
|
<translation>沒有設定 qmake 路徑</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>qmake does not exist or is not executable</source>
|
<source>%1 does not exist or is not executable</source>
|
||||||
<translation>qmake 不存在或無法執行</translation>
|
<translation>%1 不存在或無法執行</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Qt version has no name</source>
|
<source>Qt version has no name</source>
|
||||||
@@ -15664,8 +15664,8 @@ Requires <b>Qt 4.7.4</b> or newer.</source>
|
|||||||
<translation>Qt 沒有被正確安裝,請執行 make install</translation>
|
<translation>Qt 沒有被正確安裝,請執行 make install</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong?</source>
|
<source>Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong?</source>
|
||||||
<translation>無法決定 Qt 安裝版的路徑。也許是 qmake 的路徑設定有錯誤?</translation>
|
<translation>無法決定 Qt 安裝版的路徑。也許是 %1 的路徑設定有錯誤?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>The default mkspec symlink is broken.</source>
|
<source>The default mkspec symlink is broken.</source>
|
||||||
|
@@ -629,7 +629,7 @@ ClassOrNamespace *ClassOrNamespace::parent() const
|
|||||||
return _parent;
|
return _parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<ClassOrNamespace *> ClassOrNamespace::usings() const
|
const QList<ClassOrNamespace *> ClassOrNamespace::usings() const
|
||||||
{
|
{
|
||||||
const_cast<ClassOrNamespace *>(this)->flush();
|
const_cast<ClassOrNamespace *>(this)->flush();
|
||||||
return _usings;
|
return _usings;
|
||||||
@@ -641,7 +641,7 @@ QList<Enum *> ClassOrNamespace::unscopedEnums() const
|
|||||||
return _enums;
|
return _enums;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<Symbol *> ClassOrNamespace::symbols() const
|
const QList<Symbol *> ClassOrNamespace::symbols() const
|
||||||
{
|
{
|
||||||
const_cast<ClassOrNamespace *>(this)->flush();
|
const_cast<ClassOrNamespace *>(this)->flush();
|
||||||
return _symbols;
|
return _symbols;
|
||||||
|
@@ -70,9 +70,9 @@ public:
|
|||||||
ClassOrNamespace *instantiationOrigin() const;
|
ClassOrNamespace *instantiationOrigin() const;
|
||||||
|
|
||||||
ClassOrNamespace *parent() const;
|
ClassOrNamespace *parent() const;
|
||||||
QList<ClassOrNamespace *> usings() const;
|
const QList<ClassOrNamespace *> usings() const;
|
||||||
QList<Enum *> unscopedEnums() const;
|
QList<Enum *> unscopedEnums() const;
|
||||||
QList<Symbol *> symbols() const;
|
const QList<Symbol *> symbols() const;
|
||||||
|
|
||||||
ClassOrNamespace *globalNamespace() const;
|
ClassOrNamespace *globalNamespace() const;
|
||||||
|
|
||||||
|
@@ -41,7 +41,61 @@ bool BuildableHelperLibrary::isQtChooser(const FilePath &filePath)
|
|||||||
return filePath.symLinkTarget().endsWith("/qtchooser");
|
return filePath.symLinkTarget().endsWith("/qtchooser");
|
||||||
}
|
}
|
||||||
|
|
||||||
FilePath BuildableHelperLibrary::qtChooserToQmakePath(const FilePath &qtChooser)
|
static const QStringList &queryToolNames()
|
||||||
|
{
|
||||||
|
static const QStringList names = {"qmake", "qtpaths"};
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isQueryTool(FilePath path)
|
||||||
|
{
|
||||||
|
if (path.isEmpty())
|
||||||
|
return false;
|
||||||
|
if (BuildableHelperLibrary::isQtChooser(path))
|
||||||
|
path = BuildableHelperLibrary::qtChooserToQueryToolPath(path.symLinkTarget());
|
||||||
|
if (!path.exists())
|
||||||
|
return false;
|
||||||
|
QtcProcess proc;
|
||||||
|
proc.setCommand({path, {"-query"}});
|
||||||
|
proc.runBlocking();
|
||||||
|
if (proc.result() != ProcessResult::FinishedWithSuccess)
|
||||||
|
return false;
|
||||||
|
const QString output = proc.stdOut();
|
||||||
|
// Exemplary output of "[qmake|qtpaths] -query":
|
||||||
|
// QT_SYSROOT:...
|
||||||
|
// QT_INSTALL_PREFIX:...
|
||||||
|
// [...]
|
||||||
|
// QT_VERSION:6.4.0
|
||||||
|
return output.contains("QT_VERSION:");
|
||||||
|
}
|
||||||
|
|
||||||
|
static FilePath findQueryToolInDir(const FilePath &dir)
|
||||||
|
{
|
||||||
|
if (dir.isEmpty())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
for (const QString &queryTool : queryToolNames()) {
|
||||||
|
FilePath queryToolPath = dir.pathAppended(queryTool).withExecutableSuffix();
|
||||||
|
if (queryToolPath.exists()) {
|
||||||
|
if (isQueryTool(queryToolPath))
|
||||||
|
return queryToolPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefer qmake-qt5 to qmake-qt4 by sorting the filenames in reverse order.
|
||||||
|
const FilePaths candidates = dir.dirEntries(
|
||||||
|
{BuildableHelperLibrary::possibleQtQueryTools(queryTool), QDir::Files},
|
||||||
|
QDir::Name | QDir::Reversed);
|
||||||
|
for (const FilePath &candidate : candidates) {
|
||||||
|
if (candidate == queryToolPath)
|
||||||
|
continue;
|
||||||
|
if (isQueryTool(candidate))
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
FilePath BuildableHelperLibrary::qtChooserToQueryToolPath(const FilePath &qtChooser)
|
||||||
{
|
{
|
||||||
const QString toolDir = QLatin1String("QTTOOLDIR=\"");
|
const QString toolDir = QLatin1String("QTTOOLDIR=\"");
|
||||||
QtcProcess proc;
|
QtcProcess proc;
|
||||||
@@ -51,6 +105,10 @@ FilePath BuildableHelperLibrary::qtChooserToQmakePath(const FilePath &qtChooser)
|
|||||||
if (proc.result() != ProcessResult::FinishedWithSuccess)
|
if (proc.result() != ProcessResult::FinishedWithSuccess)
|
||||||
return {};
|
return {};
|
||||||
const QString output = proc.stdOut();
|
const QString output = proc.stdOut();
|
||||||
|
// Exemplary output of "qtchooser -print-env":
|
||||||
|
// QT_SELECT="default"
|
||||||
|
// QTTOOLDIR="/usr/lib/qt5/bin"
|
||||||
|
// QTLIBDIR="/usr/lib/x86_64-linux-gnu"
|
||||||
int pos = output.indexOf(toolDir);
|
int pos = output.indexOf(toolDir);
|
||||||
if (pos == -1)
|
if (pos == -1)
|
||||||
return {};
|
return {};
|
||||||
@@ -59,44 +117,13 @@ FilePath BuildableHelperLibrary::qtChooserToQmakePath(const FilePath &qtChooser)
|
|||||||
if (end == -1)
|
if (end == -1)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
FilePath qmake = qtChooser;
|
FilePath queryToolPath = qtChooser;
|
||||||
qmake.setPath(output.mid(pos, end - pos) + "/qmake");
|
for (const QString &queryTool : queryToolNames()) {
|
||||||
return qmake;
|
queryToolPath.setPath(output.mid(pos, end - pos) + "/" + queryTool);
|
||||||
}
|
if (queryToolPath.exists())
|
||||||
|
return queryToolPath;
|
||||||
static bool isQmake(FilePath path)
|
|
||||||
{
|
|
||||||
if (path.isEmpty())
|
|
||||||
return false;
|
|
||||||
if (BuildableHelperLibrary::isQtChooser(path))
|
|
||||||
path = BuildableHelperLibrary::qtChooserToQmakePath(path.symLinkTarget());
|
|
||||||
if (!path.exists())
|
|
||||||
return false;
|
|
||||||
return !BuildableHelperLibrary::qtVersionForQMake(path).isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
static FilePath findQmakeInDir(const FilePath &dir)
|
|
||||||
{
|
|
||||||
if (dir.isEmpty())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
FilePath qmakePath = dir.pathAppended("qmake").withExecutableSuffix();
|
|
||||||
if (qmakePath.exists()) {
|
|
||||||
if (isQmake(qmakePath))
|
|
||||||
return qmakePath;
|
|
||||||
}
|
}
|
||||||
|
return queryToolPath;
|
||||||
// Prefer qmake-qt5 to qmake-qt4 by sorting the filenames in reverse order.
|
|
||||||
const FilePaths candidates = dir.dirEntries(
|
|
||||||
{BuildableHelperLibrary::possibleQMakeCommands(), QDir::Files},
|
|
||||||
QDir::Name | QDir::Reversed);
|
|
||||||
for (const FilePath &candidate : candidates) {
|
|
||||||
if (candidate == qmakePath)
|
|
||||||
continue;
|
|
||||||
if (isQmake(candidate))
|
|
||||||
return candidate;
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FilePath BuildableHelperLibrary::findSystemQt(const Environment &env)
|
FilePath BuildableHelperLibrary::findSystemQt(const Environment &env)
|
||||||
@@ -107,86 +134,55 @@ FilePath BuildableHelperLibrary::findSystemQt(const Environment &env)
|
|||||||
|
|
||||||
FilePaths BuildableHelperLibrary::findQtsInEnvironment(const Environment &env, int maxCount)
|
FilePaths BuildableHelperLibrary::findQtsInEnvironment(const Environment &env, int maxCount)
|
||||||
{
|
{
|
||||||
FilePaths qmakeList;
|
FilePaths queryToolList;
|
||||||
std::set<QString> canonicalEnvPaths;
|
std::set<QString> canonicalEnvPaths;
|
||||||
const FilePaths paths = env.path();
|
const FilePaths paths = env.path();
|
||||||
for (const FilePath &path : paths) {
|
for (const FilePath &path : paths) {
|
||||||
if (!canonicalEnvPaths.insert(path.toFileInfo().canonicalFilePath()).second)
|
if (!canonicalEnvPaths.insert(path.toFileInfo().canonicalFilePath()).second)
|
||||||
continue;
|
continue;
|
||||||
const FilePath qmake = findQmakeInDir(path);
|
const FilePath queryTool = findQueryToolInDir(path);
|
||||||
if (qmake.isEmpty())
|
if (queryTool.isEmpty())
|
||||||
continue;
|
continue;
|
||||||
qmakeList << qmake;
|
queryToolList << queryTool;
|
||||||
if (maxCount != -1 && qmakeList.size() == maxCount)
|
if (maxCount != -1 && queryToolList.size() == maxCount)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return qmakeList;
|
return queryToolList;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString BuildableHelperLibrary::qtVersionForQMake(const FilePath &qmakePath)
|
QString BuildableHelperLibrary::filterForQtQueryToolsFileDialog()
|
||||||
{
|
{
|
||||||
if (qmakePath.isEmpty())
|
QStringList toolFilters;
|
||||||
return QString();
|
for (const QString &queryTool : queryToolNames()) {
|
||||||
|
for (const QString &tool: BuildableHelperLibrary::possibleQtQueryTools(queryTool)) {
|
||||||
QtcProcess qmake;
|
QString toolFilter;
|
||||||
qmake.setTimeoutS(5);
|
|
||||||
qmake.setCommand({qmakePath, {"--version"}});
|
|
||||||
qmake.runBlocking();
|
|
||||||
if (qmake.result() != ProcessResult::FinishedWithSuccess) {
|
|
||||||
qWarning() << qmake.exitMessage();
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString output = qmake.allOutput();
|
|
||||||
static const QRegularExpression regexp("(QMake version:?)[\\s]*([\\d.]*)",
|
|
||||||
QRegularExpression::CaseInsensitiveOption);
|
|
||||||
const QRegularExpressionMatch match = regexp.match(output);
|
|
||||||
const QString qmakeVersion = match.captured(2);
|
|
||||||
if (qmakeVersion.startsWith(QLatin1String("2."))
|
|
||||||
|| qmakeVersion.startsWith(QLatin1String("3."))) {
|
|
||||||
static const QRegularExpression regexp2("Using Qt version[\\s]*([\\d\\.]*)",
|
|
||||||
QRegularExpression::CaseInsensitiveOption);
|
|
||||||
const QRegularExpressionMatch match2 = regexp2.match(output);
|
|
||||||
const QString version = match2.captured(1);
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString BuildableHelperLibrary::filterForQmakeFileDialog()
|
|
||||||
{
|
|
||||||
QString filter = QLatin1String("qmake (");
|
|
||||||
const QStringList commands = possibleQMakeCommands();
|
|
||||||
for (int i = 0; i < commands.size(); ++i) {
|
|
||||||
if (i)
|
|
||||||
filter += QLatin1Char(' ');
|
|
||||||
if (HostOsInfo::isMacHost())
|
if (HostOsInfo::isMacHost())
|
||||||
// work around QTBUG-7739 that prohibits filters that don't start with *
|
// work around QTBUG-7739 that prohibits filters that don't start with *
|
||||||
filter += QLatin1Char('*');
|
toolFilter += QLatin1Char('*');
|
||||||
filter += commands.at(i);
|
toolFilter += tool;
|
||||||
if (HostOsInfo::isAnyUnixHost() && !HostOsInfo::isMacHost())
|
if (HostOsInfo::isAnyUnixHost() && !HostOsInfo::isMacHost())
|
||||||
// kde bug, we need at least one wildcard character
|
// kde bug, we need at least one wildcard character
|
||||||
// see QTCREATORBUG-7771
|
// see QTCREATORBUG-7771
|
||||||
filter += QLatin1Char('*');
|
toolFilter += QLatin1Char('*');
|
||||||
|
toolFilters.append(toolFilter);
|
||||||
}
|
}
|
||||||
filter += QLatin1Char(')');
|
}
|
||||||
return filter;
|
return queryToolNames().join(", ") + " (" + toolFilters.join(" ") + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QStringList BuildableHelperLibrary::possibleQtQueryTools(const QString &tool)
|
||||||
QStringList BuildableHelperLibrary::possibleQMakeCommands()
|
|
||||||
{
|
{
|
||||||
// On Windows it is always "qmake.exe"
|
// On Windows it is "<queryTool>.exe" or "<queryTool>.bat"
|
||||||
// On Unix some distributions renamed qmake with a postfix to avoid clashes
|
// On Unix some distributions renamed qmake with a postfix to avoid clashes
|
||||||
// On OS X, Qt 4 binary packages also has renamed qmake. There are also symbolic links that are
|
// On OS X, Qt 4 binary packages also has renamed qmake. There are also symbolic links that are
|
||||||
// named "qmake", but the file dialog always checks against resolved links (native Cocoa issue)
|
// named <tool>, but the file dialog always checks against resolved links (native Cocoa issue)
|
||||||
QStringList commands(HostOsInfo::withExecutableSuffix("qmake*"));
|
QStringList tools(HostOsInfo::withExecutableSuffix(tool + "*"));
|
||||||
|
|
||||||
// Qt 6 CMake built targets, such as Android, are dependent on the host installation
|
// Qt 6 CMake built targets, such as Android, are dependent on the host installation
|
||||||
// and use a script wrapper around the host qmake executable
|
// and use a script wrapper around the host queryTool executable
|
||||||
if (HostOsInfo::isWindowsHost())
|
if (HostOsInfo::isWindowsHost())
|
||||||
commands.append("qmake*.bat");
|
tools.append(tool + "*.bat");
|
||||||
return commands;
|
return tools;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Utils
|
} // namespace Utils
|
||||||
|
@@ -45,12 +45,11 @@ public:
|
|||||||
static FilePath findSystemQt(const Environment &env);
|
static FilePath findSystemQt(const Environment &env);
|
||||||
static FilePaths findQtsInEnvironment(const Environment &env, int maxCount = -1);
|
static FilePaths findQtsInEnvironment(const Environment &env, int maxCount = -1);
|
||||||
static bool isQtChooser(const FilePath &filePath);
|
static bool isQtChooser(const FilePath &filePath);
|
||||||
static FilePath qtChooserToQmakePath(const FilePath &path);
|
static FilePath qtChooserToQueryToolPath(const FilePath &path);
|
||||||
// return true if the qmake at qmakePath is a Qt (used by QtVersion)
|
// returns something like qmake4, qmake, qmake-qt4, qtpaths
|
||||||
static QString qtVersionForQMake(const FilePath &qmakePath);
|
// or whatever distributions have chosen (used by QtVersion)
|
||||||
// returns something like qmake4, qmake, qmake-qt4 or whatever distributions have chosen (used by QtVersion)
|
static QStringList possibleQtQueryTools(const QString &tool);
|
||||||
static QStringList possibleQMakeCommands();
|
static QString filterForQtQueryToolsFileDialog();
|
||||||
static QString filterForQmakeFileDialog();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // Utils
|
} // Utils
|
||||||
|
@@ -65,6 +65,7 @@ HostOsInfo::HostArchitecture HostOsInfo::hostArchitecture()
|
|||||||
case PROCESSOR_ARCHITECTURE_IA64:
|
case PROCESSOR_ARCHITECTURE_IA64:
|
||||||
return HostOsInfo::HostArchitectureItanium;
|
return HostOsInfo::HostArchitectureItanium;
|
||||||
case PROCESSOR_ARCHITECTURE_ARM:
|
case PROCESSOR_ARCHITECTURE_ARM:
|
||||||
|
case PROCESSOR_ARCHITECTURE_ARM64:
|
||||||
return HostOsInfo::HostArchitectureArm;
|
return HostOsInfo::HostArchitectureArm;
|
||||||
default:
|
default:
|
||||||
return HostOsInfo::HostArchitectureUnknown;
|
return HostOsInfo::HostArchitectureUnknown;
|
||||||
|
@@ -192,7 +192,7 @@ public:
|
|||||||
EnvironmentChange m_environmentChange;
|
EnvironmentChange m_environmentChange;
|
||||||
BinaryVersionToolTipEventFilter *m_binaryVersionToolTipEventFilter = nullptr;
|
BinaryVersionToolTipEventFilter *m_binaryVersionToolTipEventFilter = nullptr;
|
||||||
QList<QAbstractButton *> m_buttons;
|
QList<QAbstractButton *> m_buttons;
|
||||||
MacroExpander *m_macroExpander = globalMacroExpander();
|
const MacroExpander *m_macroExpander = globalMacroExpander();
|
||||||
std::function<void()> m_openTerminal;
|
std::function<void()> m_openTerminal;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -732,7 +732,7 @@ void PathChooser::setHistoryCompleter(const QString &historyKey, bool restoreLas
|
|||||||
d->m_lineEdit->setHistoryCompleter(historyKey, restoreLastItemFromHistory);
|
d->m_lineEdit->setHistoryCompleter(historyKey, restoreLastItemFromHistory);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PathChooser::setMacroExpander(MacroExpander *macroExpander)
|
void PathChooser::setMacroExpander(const MacroExpander *macroExpander)
|
||||||
{
|
{
|
||||||
d->m_macroExpander = macroExpander;
|
d->m_macroExpander = macroExpander;
|
||||||
}
|
}
|
||||||
|
@@ -136,7 +136,7 @@ public:
|
|||||||
// Sets a macro expander that is used when producing path and fileName.
|
// Sets a macro expander that is used when producing path and fileName.
|
||||||
// By default, the global expander is used.
|
// By default, the global expander is used.
|
||||||
// nullptr can be passed to disable macro expansion.
|
// nullptr can be passed to disable macro expansion.
|
||||||
void setMacroExpander(MacroExpander *macroExpander);
|
void setMacroExpander(const MacroExpander *macroExpander);
|
||||||
|
|
||||||
bool isReadOnly() const;
|
bool isReadOnly() const;
|
||||||
void setReadOnly(bool b);
|
void setReadOnly(bool b);
|
||||||
|
@@ -145,7 +145,8 @@ QTCREATOR_UTILS_EXPORT bool is64BitWindowsSystem()
|
|||||||
SYSTEM_INFO systemInfo;
|
SYSTEM_INFO systemInfo;
|
||||||
GetNativeSystemInfo(&systemInfo);
|
GetNativeSystemInfo(&systemInfo);
|
||||||
return systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64
|
return systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64
|
||||||
|| systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64;
|
|| systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64
|
||||||
|
|| systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM64;
|
||||||
#else
|
#else
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
|
@@ -28,11 +28,11 @@
|
|||||||
#include "androidconfigurations.h"
|
#include "androidconfigurations.h"
|
||||||
#include "androidconstants.h"
|
#include "androidconstants.h"
|
||||||
#include "androidmanager.h"
|
#include "androidmanager.h"
|
||||||
#include "androidrunconfiguration.h"
|
|
||||||
|
|
||||||
#include <debugger/debuggerkitinformation.h>
|
#include <debugger/debuggerkitinformation.h>
|
||||||
#include <debugger/debuggerrunconfigurationaspect.h>
|
#include <debugger/debuggerrunconfigurationaspect.h>
|
||||||
|
|
||||||
|
#include <projectexplorer/buildconfiguration.h>
|
||||||
#include <projectexplorer/projectexplorerconstants.h>
|
#include <projectexplorer/projectexplorerconstants.h>
|
||||||
#include <projectexplorer/environmentaspect.h>
|
#include <projectexplorer/environmentaspect.h>
|
||||||
#include <projectexplorer/runconfigurationaspects.h>
|
#include <projectexplorer/runconfigurationaspects.h>
|
||||||
@@ -274,9 +274,8 @@ AndroidRunnerWorker::AndroidRunnerWorker(RunWorker *runner, const QString &packa
|
|||||||
qCDebug(androidRunWorkerLog) << "Environment variables for the app"
|
qCDebug(androidRunWorkerLog) << "Environment variables for the app"
|
||||||
<< m_extraEnvVars.toStringList();
|
<< m_extraEnvVars.toStringList();
|
||||||
|
|
||||||
if (target->buildConfigurations().first()->buildType() != BuildConfiguration::BuildType::Release) {
|
if (target->buildConfigurations().first()->buildType() != BuildConfiguration::BuildType::Release)
|
||||||
m_extraAppParams = runControl->runnable().command.arguments();
|
m_extraAppParams = runControl->commandLine().arguments();
|
||||||
}
|
|
||||||
|
|
||||||
if (auto aspect = runControl->aspect(Constants::ANDROID_AM_START_ARGS)) {
|
if (auto aspect = runControl->aspect(Constants::ANDROID_AM_START_ARGS)) {
|
||||||
QTC_CHECK(aspect->value.type() == QVariant::String);
|
QTC_CHECK(aspect->value.type() == QVariant::String);
|
||||||
|
@@ -32,6 +32,7 @@
|
|||||||
#include "../itestframework.h"
|
#include "../itestframework.h"
|
||||||
#include "../testsettings.h"
|
#include "../testsettings.h"
|
||||||
|
|
||||||
|
#include <projectexplorer/buildconfiguration.h>
|
||||||
#include <projectexplorer/buildsystem.h>
|
#include <projectexplorer/buildsystem.h>
|
||||||
#include <projectexplorer/environmentaspect.h>
|
#include <projectexplorer/environmentaspect.h>
|
||||||
#include <projectexplorer/project.h>
|
#include <projectexplorer/project.h>
|
||||||
|
@@ -53,7 +53,7 @@ public:
|
|||||||
exeAspect->setPlaceHolderText(tr("Unknown"));
|
exeAspect->setPlaceHolderText(tr("Unknown"));
|
||||||
|
|
||||||
addAspect<ArgumentsAspect>(macroExpander());
|
addAspect<ArgumentsAspect>(macroExpander());
|
||||||
addAspect<WorkingDirectoryAspect>(nullptr);
|
addAspect<WorkingDirectoryAspect>(macroExpander(), nullptr);
|
||||||
|
|
||||||
setUpdater([this, exeAspect] {
|
setUpdater([this, exeAspect] {
|
||||||
const BuildTargetInfo bti = buildTargetInfo();
|
const BuildTargetInfo bti = buildTargetInfo();
|
||||||
@@ -80,7 +80,7 @@ public:
|
|||||||
exeAspect->setExpectedKind(PathChooser::Any);
|
exeAspect->setExpectedKind(PathChooser::Any);
|
||||||
|
|
||||||
addAspect<ArgumentsAspect>(macroExpander());
|
addAspect<ArgumentsAspect>(macroExpander());
|
||||||
addAspect<WorkingDirectoryAspect>(nullptr);
|
addAspect<WorkingDirectoryAspect>(macroExpander(), nullptr);
|
||||||
|
|
||||||
setDefaultDisplayName(RunConfigurationFactory::decoratedTargetName(tr("Custom Executable"), target));
|
setDefaultDisplayName(RunConfigurationFactory::decoratedTargetName(tr("Custom Executable"), target));
|
||||||
}
|
}
|
||||||
|
@@ -179,8 +179,6 @@ bool GdbServerProvider::aboutToRun(DebuggerRunTool *runTool,
|
|||||||
|
|
||||||
Runnable inferior;
|
Runnable inferior;
|
||||||
inferior.command.setExecutable(bin);
|
inferior.command.setExecutable(bin);
|
||||||
inferior.extraData.insert(Debugger::Constants::kPeripheralDescriptionFile,
|
|
||||||
m_peripheralDescriptionFile.toVariant());
|
|
||||||
if (const auto argAspect = runControl->aspect<ArgumentsAspect>())
|
if (const auto argAspect = runControl->aspect<ArgumentsAspect>())
|
||||||
inferior.command.setArguments(argAspect->arguments);
|
inferior.command.setArguments(argAspect->arguments);
|
||||||
runTool->setInferior(inferior);
|
runTool->setInferior(inferior);
|
||||||
@@ -191,6 +189,7 @@ bool GdbServerProvider::aboutToRun(DebuggerRunTool *runTool,
|
|||||||
runTool->setRemoteChannel(channelString());
|
runTool->setRemoteChannel(channelString());
|
||||||
runTool->setUseContinueInsteadOfRun(true);
|
runTool->setUseContinueInsteadOfRun(true);
|
||||||
runTool->setUseExtendedRemote(useExtendedRemote());
|
runTool->setUseExtendedRemote(useExtendedRemote());
|
||||||
|
runTool->runParameters().peripheralDescriptionFile = m_peripheralDescriptionFile;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -216,11 +216,10 @@ bool UvscServerProvider::aboutToRun(DebuggerRunTool *runTool, QString &errorMess
|
|||||||
|
|
||||||
Runnable inferior;
|
Runnable inferior;
|
||||||
inferior.command.setExecutable(bin);
|
inferior.command.setExecutable(bin);
|
||||||
inferior.extraData.insert(Debugger::Constants::kPeripheralDescriptionFile,
|
runTool->runParameters().peripheralDescriptionFile = peripheralDescriptionFile;
|
||||||
peripheralDescriptionFile.toVariant());
|
runTool->runParameters().uVisionProjectFilePath = projFilePath;
|
||||||
inferior.extraData.insert(Debugger::Constants::kUVisionProjectFilePath, projFilePath.toString());
|
runTool->runParameters().uVisionOptionsFilePath = optFilePath;
|
||||||
inferior.extraData.insert(Debugger::Constants::kUVisionOptionsFilePath, optFilePath.toString());
|
runTool->runParameters().uVisionSimulator = isSimulator();
|
||||||
inferior.extraData.insert(Debugger::Constants::kUVisionSimulator, isSimulator());
|
|
||||||
runTool->setInferior(inferior);
|
runTool->setInferior(inferior);
|
||||||
runTool->setSymbolFile(bin);
|
runTool->setSymbolFile(bin);
|
||||||
runTool->setStartMode(AttachToRemoteServer);
|
runTool->setStartMode(AttachToRemoteServer);
|
||||||
|
@@ -83,8 +83,6 @@ public:
|
|||||||
int lowerPort = 0;
|
int lowerPort = 0;
|
||||||
int upperPort = 0;
|
int upperPort = 0;
|
||||||
|
|
||||||
Runnable r = runnable();
|
|
||||||
|
|
||||||
CommandLine cmd;
|
CommandLine cmd;
|
||||||
cmd.setExecutable(device()->filePath(Constants::AppcontrollerFilepath));
|
cmd.setExecutable(device()->filePath(Constants::AppcontrollerFilepath));
|
||||||
|
|
||||||
@@ -118,11 +116,11 @@ public:
|
|||||||
}
|
}
|
||||||
cmd.addArg("--port-range");
|
cmd.addArg("--port-range");
|
||||||
cmd.addArg(QString("%1-%2").arg(lowerPort).arg(upperPort));
|
cmd.addArg(QString("%1-%2").arg(lowerPort).arg(upperPort));
|
||||||
cmd.addCommandLineAsArgs(r.command);
|
cmd.addCommandLineAsArgs(runControl()->commandLine());
|
||||||
|
|
||||||
m_launcher.setCommand(cmd);
|
m_launcher.setCommand(cmd);
|
||||||
m_launcher.setWorkingDirectory(r.workingDirectory);
|
m_launcher.setWorkingDirectory(runControl()->workingDirectory());
|
||||||
m_launcher.setEnvironment(r.environment);
|
m_launcher.setEnvironment(runControl()->environment());
|
||||||
m_launcher.start();
|
m_launcher.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -102,7 +102,7 @@ QdbRunConfiguration::QdbRunConfiguration(Target *target, Id id)
|
|||||||
auto envAspect = addAspect<RemoteLinux::RemoteLinuxEnvironmentAspect>(target);
|
auto envAspect = addAspect<RemoteLinux::RemoteLinuxEnvironmentAspect>(target);
|
||||||
|
|
||||||
addAspect<ArgumentsAspect>(macroExpander());
|
addAspect<ArgumentsAspect>(macroExpander());
|
||||||
addAspect<WorkingDirectoryAspect>(envAspect);
|
addAspect<WorkingDirectoryAspect>(macroExpander(), envAspect);
|
||||||
addAspect<FullCommandLineAspect>(this);
|
addAspect<FullCommandLineAspect>(this);
|
||||||
|
|
||||||
setUpdater([this, target, exeAspect, symbolsAspect] {
|
setUpdater([this, target, exeAspect, symbolsAspect] {
|
||||||
|
@@ -15,11 +15,12 @@ add_qtc_plugin(ClangCodeModel
|
|||||||
clangcodemodelplugin.cpp clangcodemodelplugin.h
|
clangcodemodelplugin.cpp clangcodemodelplugin.h
|
||||||
clangcompletioncontextanalyzer.cpp clangcompletioncontextanalyzer.h
|
clangcompletioncontextanalyzer.cpp clangcompletioncontextanalyzer.h
|
||||||
clangconstants.h
|
clangconstants.h
|
||||||
clangdclient.cpp clangdclient.h
|
|
||||||
clangdast.cpp clangdast.h
|
clangdast.cpp clangdast.h
|
||||||
|
clangdclient.cpp clangdclient.h
|
||||||
clangdiagnostictooltipwidget.cpp clangdiagnostictooltipwidget.h
|
clangdiagnostictooltipwidget.cpp clangdiagnostictooltipwidget.h
|
||||||
clangdquickfixfactory.cpp clangdquickfixfactory.h
|
clangdquickfixfactory.cpp clangdquickfixfactory.h
|
||||||
clangdqpropertyhighlighter.cpp clangdqpropertyhighlighter.h
|
clangdqpropertyhighlighter.cpp clangdqpropertyhighlighter.h
|
||||||
|
clangdsemantichighlighting.cpp clangdsemantichighlighting.h
|
||||||
clangeditordocumentprocessor.cpp clangeditordocumentprocessor.h
|
clangeditordocumentprocessor.cpp clangeditordocumentprocessor.h
|
||||||
clangfixitoperation.cpp clangfixitoperation.h
|
clangfixitoperation.cpp clangfixitoperation.h
|
||||||
clangdlocatorfilters.cpp clangdlocatorfilters.h
|
clangdlocatorfilters.cpp clangdlocatorfilters.h
|
||||||
@@ -28,6 +29,7 @@ add_qtc_plugin(ClangCodeModel
|
|||||||
clangtextmark.cpp clangtextmark.h
|
clangtextmark.cpp clangtextmark.h
|
||||||
clanguiheaderondiskmanager.cpp clanguiheaderondiskmanager.h
|
clanguiheaderondiskmanager.cpp clanguiheaderondiskmanager.h
|
||||||
clangutils.cpp clangutils.h
|
clangutils.cpp clangutils.h
|
||||||
|
tasktimers.cpp tasktimers.h
|
||||||
EXPLICIT_MOC clangcodemodelplugin.h
|
EXPLICIT_MOC clangcodemodelplugin.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -42,6 +42,8 @@ QtcPlugin {
|
|||||||
"clangdqpropertyhighlighter.h",
|
"clangdqpropertyhighlighter.h",
|
||||||
"clangdquickfixfactory.cpp",
|
"clangdquickfixfactory.cpp",
|
||||||
"clangdquickfixfactory.h",
|
"clangdquickfixfactory.h",
|
||||||
|
"clangdsemantichighlighting.cpp",
|
||||||
|
"clangdsemantichighlighting.h",
|
||||||
"clangeditordocumentprocessor.cpp",
|
"clangeditordocumentprocessor.cpp",
|
||||||
"clangeditordocumentprocessor.h",
|
"clangeditordocumentprocessor.h",
|
||||||
"clangfixitoperation.cpp",
|
"clangfixitoperation.cpp",
|
||||||
@@ -56,6 +58,8 @@ QtcPlugin {
|
|||||||
"clanguiheaderondiskmanager.h",
|
"clanguiheaderondiskmanager.h",
|
||||||
"clangutils.cpp",
|
"clangutils.cpp",
|
||||||
"clangutils.h",
|
"clangutils.h",
|
||||||
|
"tasktimers.cpp",
|
||||||
|
"tasktimers.h",
|
||||||
]
|
]
|
||||||
|
|
||||||
Group {
|
Group {
|
||||||
|
File diff suppressed because it is too large
Load Diff
925
src/plugins/clangcodemodel/clangdsemantichighlighting.cpp
Normal file
925
src/plugins/clangcodemodel/clangdsemantichighlighting.cpp
Normal file
@@ -0,0 +1,925 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "clangdsemantichighlighting.h"
|
||||||
|
|
||||||
|
#include "clangdast.h"
|
||||||
|
#include "clangdclient.h"
|
||||||
|
#include "clangdqpropertyhighlighter.h"
|
||||||
|
#include "clangmodelmanagersupport.h"
|
||||||
|
#include "tasktimers.h"
|
||||||
|
|
||||||
|
#include <cppeditor/semantichighlighter.h>
|
||||||
|
#include <languageclient/semantichighlightsupport.h>
|
||||||
|
#include <languageserverprotocol/lsptypes.h>
|
||||||
|
#include <texteditor/blockrange.h>
|
||||||
|
#include <texteditor/textstyles.h>
|
||||||
|
|
||||||
|
#include <QtConcurrent>
|
||||||
|
#include <QTextDocument>
|
||||||
|
|
||||||
|
using namespace LanguageClient;
|
||||||
|
using namespace LanguageServerProtocol;
|
||||||
|
using namespace TextEditor;
|
||||||
|
|
||||||
|
namespace ClangCodeModel::Internal {
|
||||||
|
Q_LOGGING_CATEGORY(clangdLogHighlight, "qtc.clangcodemodel.clangd.highlight", QtWarningMsg);
|
||||||
|
|
||||||
|
// clangd reports also the #ifs, #elses and #endifs around the disabled code as disabled,
|
||||||
|
// and not even in a consistent manner. We don't want this, so we have to clean up here.
|
||||||
|
// But note that we require this behavior, as otherwise we would not be able to grey out
|
||||||
|
// e.g. empty lines after an #ifdef, due to the lack of symbols.
|
||||||
|
static QList<BlockRange> cleanupDisabledCode(HighlightingResults &results, const QTextDocument *doc,
|
||||||
|
const QString &docContent)
|
||||||
|
{
|
||||||
|
QList<BlockRange> ifdefedOutRanges;
|
||||||
|
int rangeStartPos = -1;
|
||||||
|
for (auto it = results.begin(); it != results.end();) {
|
||||||
|
const bool wasIfdefedOut = rangeStartPos != -1;
|
||||||
|
const bool isIfDefedOut = it->textStyles.mainStyle == C_DISABLED_CODE;
|
||||||
|
if (!isIfDefedOut) {
|
||||||
|
if (wasIfdefedOut) {
|
||||||
|
const QTextBlock block = doc->findBlockByNumber(it->line - 1);
|
||||||
|
ifdefedOutRanges << BlockRange(rangeStartPos, block.position());
|
||||||
|
rangeStartPos = -1;
|
||||||
|
}
|
||||||
|
++it;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wasIfdefedOut)
|
||||||
|
rangeStartPos = doc->findBlockByNumber(it->line - 1).position();
|
||||||
|
|
||||||
|
// Does the current line contain a potential "ifdefed-out switcher"?
|
||||||
|
// If not, no state change is possible and we continue with the next line.
|
||||||
|
const auto isPreprocessorControlStatement = [&] {
|
||||||
|
const int pos = Utils::Text::positionInText(doc, it->line, it->column);
|
||||||
|
const QStringView content = subViewLen(docContent, pos, it->length).trimmed();
|
||||||
|
if (content.isEmpty() || content.first() != '#')
|
||||||
|
return false;
|
||||||
|
int offset = 1;
|
||||||
|
while (offset < content.size() && content.at(offset).isSpace())
|
||||||
|
++offset;
|
||||||
|
if (offset == content.size())
|
||||||
|
return false;
|
||||||
|
const QStringView ppDirective = content.mid(offset);
|
||||||
|
return ppDirective.startsWith(QLatin1String("if"))
|
||||||
|
|| ppDirective.startsWith(QLatin1String("elif"))
|
||||||
|
|| ppDirective.startsWith(QLatin1String("else"))
|
||||||
|
|| ppDirective.startsWith(QLatin1String("endif"));
|
||||||
|
};
|
||||||
|
if (!isPreprocessorControlStatement()) {
|
||||||
|
++it;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wasIfdefedOut) {
|
||||||
|
// The #if or #else that starts disabled code should not be disabled.
|
||||||
|
const QTextBlock nextBlock = doc->findBlockByNumber(it->line);
|
||||||
|
rangeStartPos = nextBlock.isValid() ? nextBlock.position() : -1;
|
||||||
|
it = results.erase(it);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wasIfdefedOut && (it + 1 == results.end()
|
||||||
|
|| (it + 1)->textStyles.mainStyle != C_DISABLED_CODE
|
||||||
|
|| (it + 1)->line != it->line + 1)) {
|
||||||
|
// The #else or #endif that ends disabled code should not be disabled.
|
||||||
|
const QTextBlock block = doc->findBlockByNumber(it->line - 1);
|
||||||
|
ifdefedOutRanges << BlockRange(rangeStartPos, block.position());
|
||||||
|
rangeStartPos = -1;
|
||||||
|
it = results.erase(it);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rangeStartPos != -1)
|
||||||
|
ifdefedOutRanges << BlockRange(rangeStartPos, doc->characterCount());
|
||||||
|
|
||||||
|
qCDebug(clangdLogHighlight) << "found" << ifdefedOutRanges.size() << "ifdefed-out ranges";
|
||||||
|
if (clangdLogHighlight().isDebugEnabled()) {
|
||||||
|
for (const BlockRange &r : qAsConst(ifdefedOutRanges))
|
||||||
|
qCDebug(clangdLogHighlight) << r.first() << r.last();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ifdefedOutRanges;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ExtraHighlightingResultsCollector
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ExtraHighlightingResultsCollector(QFutureInterface<HighlightingResult> &future,
|
||||||
|
HighlightingResults &results,
|
||||||
|
const Utils::FilePath &filePath, const ClangdAstNode &ast,
|
||||||
|
const QTextDocument *doc, const QString &docContent);
|
||||||
|
|
||||||
|
void collect();
|
||||||
|
private:
|
||||||
|
static bool lessThan(const HighlightingResult &r1, const HighlightingResult &r2);
|
||||||
|
static int onlyIndexOf(const QStringView &text, const QStringView &subString, int from = 0);
|
||||||
|
int posForNodeStart(const ClangdAstNode &node) const;
|
||||||
|
int posForNodeEnd(const ClangdAstNode &node) const;
|
||||||
|
void insertResult(const HighlightingResult &result);
|
||||||
|
void insertResult(const ClangdAstNode &node, TextStyle style);
|
||||||
|
void insertAngleBracketInfo(int searchStart1, int searchEnd1, int searchStart2, int searchEnd2);
|
||||||
|
void setResultPosFromRange(HighlightingResult &result, const Range &range);
|
||||||
|
void collectFromNode(const ClangdAstNode &node);
|
||||||
|
void visitNode(const ClangdAstNode&node);
|
||||||
|
|
||||||
|
QFutureInterface<HighlightingResult> &m_future;
|
||||||
|
HighlightingResults &m_results;
|
||||||
|
const Utils::FilePath m_filePath;
|
||||||
|
const ClangdAstNode &m_ast;
|
||||||
|
const QTextDocument * const m_doc;
|
||||||
|
const QString &m_docContent;
|
||||||
|
ClangdAstNode::FileStatus m_currentFileStatus = ClangdAstNode::FileStatus::Unknown;
|
||||||
|
};
|
||||||
|
|
||||||
|
void doSemanticHighlighting(
|
||||||
|
QFutureInterface<HighlightingResult> &future,
|
||||||
|
const Utils::FilePath &filePath,
|
||||||
|
const QList<ExpandedSemanticToken> &tokens,
|
||||||
|
const QString &docContents,
|
||||||
|
const ClangdAstNode &ast,
|
||||||
|
const QPointer<TextDocument> &textDocument,
|
||||||
|
int docRevision,
|
||||||
|
const QVersionNumber &clangdVersion,
|
||||||
|
const TaskTimer &taskTimer)
|
||||||
|
{
|
||||||
|
ThreadedSubtaskTimer t("highlighting", taskTimer);
|
||||||
|
if (future.isCanceled()) {
|
||||||
|
future.reportFinished();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QTextDocument doc(docContents);
|
||||||
|
const auto tokenRange = [&doc](const ExpandedSemanticToken &token) {
|
||||||
|
const Position startPos(token.line - 1, token.column - 1);
|
||||||
|
const Position endPos = startPos.withOffset(token.length, &doc);
|
||||||
|
return Range(startPos, endPos);
|
||||||
|
};
|
||||||
|
const auto isOutputParameter = [&ast, &tokenRange](const ExpandedSemanticToken &token) {
|
||||||
|
if (token.modifiers.contains(QLatin1String("usedAsMutableReference")))
|
||||||
|
return true;
|
||||||
|
if (token.type != "variable" && token.type != "property" && token.type != "parameter")
|
||||||
|
return false;
|
||||||
|
const Range range = tokenRange(token);
|
||||||
|
const ClangdAstPath path = getAstPath(ast, range);
|
||||||
|
if (path.size() < 2)
|
||||||
|
return false;
|
||||||
|
if (token.type == "property"
|
||||||
|
&& (path.rbegin()->kind() == "MemberInitializer"
|
||||||
|
|| path.rbegin()->kind() == "CXXConstruct")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (path.rbegin()->hasConstType())
|
||||||
|
return false;
|
||||||
|
for (auto it = path.rbegin() + 1; it != path.rend(); ++it) {
|
||||||
|
if (it->kind() == "CXXConstruct" || it->kind() == "MemberInitializer")
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (it->kind() == "Call") {
|
||||||
|
// The first child is e.g. a called lambda or an object on which
|
||||||
|
// the call happens, and should not be highlighted as an output argument.
|
||||||
|
// If the call is not fully resolved (as in templates), we don't
|
||||||
|
// know whether the argument is passed as const or not.
|
||||||
|
if (it->arcanaContains("dependent type"))
|
||||||
|
return false;
|
||||||
|
const QList<ClangdAstNode> children = it->children().value_or(QList<ClangdAstNode>());
|
||||||
|
return children.isEmpty()
|
||||||
|
|| (children.first().range() != (it - 1)->range()
|
||||||
|
&& children.first().kind() != "UnresolvedLookup");
|
||||||
|
}
|
||||||
|
|
||||||
|
// The token should get marked for e.g. lambdas, but not for assignment operators,
|
||||||
|
// where the user sees that it's being written.
|
||||||
|
if (it->kind() == "CXXOperatorCall") {
|
||||||
|
const QList<ClangdAstNode> children = it->children().value_or(QList<ClangdAstNode>());
|
||||||
|
|
||||||
|
// Child 1 is the call itself, Child 2 is the named entity on which the call happens
|
||||||
|
// (a lambda or a class instance), after that follow the actual call arguments.
|
||||||
|
if (children.size() < 2)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// The call itself is never modifiable.
|
||||||
|
if (children.first().range() == range)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// The callable is never displayed as an output parameter.
|
||||||
|
// TODO: A good argument can be made to display objects on which a non-const
|
||||||
|
// operator or function is called as output parameters.
|
||||||
|
if (children.at(1).range().contains(range))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
QList<ClangdAstNode> firstChildTree{children.first()};
|
||||||
|
while (!firstChildTree.isEmpty()) {
|
||||||
|
const ClangdAstNode n = firstChildTree.takeFirst();
|
||||||
|
const QString detail = n.detail().value_or(QString());
|
||||||
|
if (detail.startsWith("operator")) {
|
||||||
|
return !detail.contains('=')
|
||||||
|
&& !detail.contains("++") && !detail.contains("--")
|
||||||
|
&& !detail.contains("<<") && !detail.contains(">>")
|
||||||
|
&& !detail.contains("*");
|
||||||
|
}
|
||||||
|
firstChildTree << n.children().value_or(QList<ClangdAstNode>());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it->kind() == "Lambda")
|
||||||
|
return false;
|
||||||
|
if (it->kind() == "BinaryOperator")
|
||||||
|
return false;
|
||||||
|
if (it->hasConstType())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (it->kind() == "CXXMemberCall") {
|
||||||
|
if (it == path.rbegin())
|
||||||
|
return false;
|
||||||
|
const QList<ClangdAstNode> children = it->children().value_or(QList<ClangdAstNode>());
|
||||||
|
QTC_ASSERT(!children.isEmpty(), return false);
|
||||||
|
|
||||||
|
// The called object is never displayed as an output parameter.
|
||||||
|
// TODO: A good argument can be made to display objects on which a non-const
|
||||||
|
// operator or function is called as output parameters.
|
||||||
|
return (it - 1)->range() != children.first().range();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it->kind() == "Member" && it->arcanaContains("(")
|
||||||
|
&& !it->arcanaContains("bound member function type")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::function<HighlightingResult(const ExpandedSemanticToken &)> toResult
|
||||||
|
= [&ast, &isOutputParameter, &clangdVersion, &tokenRange]
|
||||||
|
(const ExpandedSemanticToken &token) {
|
||||||
|
TextStyles styles;
|
||||||
|
if (token.type == "variable") {
|
||||||
|
if (token.modifiers.contains(QLatin1String("functionScope"))) {
|
||||||
|
styles.mainStyle = C_LOCAL;
|
||||||
|
} else if (token.modifiers.contains(QLatin1String("classScope"))) {
|
||||||
|
styles.mainStyle = C_FIELD;
|
||||||
|
} else if (token.modifiers.contains(QLatin1String("fileScope"))
|
||||||
|
|| token.modifiers.contains(QLatin1String("globalScope"))) {
|
||||||
|
styles.mainStyle = C_GLOBAL;
|
||||||
|
}
|
||||||
|
} else if (token.type == "function" || token.type == "method") {
|
||||||
|
styles.mainStyle = token.modifiers.contains(QLatin1String("virtual"))
|
||||||
|
? C_VIRTUAL_METHOD : C_FUNCTION;
|
||||||
|
if (ast.isValid()) {
|
||||||
|
const ClangdAstPath path = getAstPath(ast, tokenRange(token));
|
||||||
|
if (path.length() > 1) {
|
||||||
|
const ClangdAstNode declNode = path.at(path.length() - 2);
|
||||||
|
if (declNode.kind() == "Function" || declNode.kind() == "CXXMethod") {
|
||||||
|
if (clangdVersion < QVersionNumber(14)
|
||||||
|
&& declNode.arcanaContains("' virtual")) {
|
||||||
|
styles.mainStyle = C_VIRTUAL_METHOD;
|
||||||
|
}
|
||||||
|
if (declNode.hasChildWithRole("statement"))
|
||||||
|
styles.mixinStyles.push_back(C_FUNCTION_DEFINITION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (token.type == "class") {
|
||||||
|
styles.mainStyle = C_TYPE;
|
||||||
|
|
||||||
|
// clang hardly ever differentiates between constructors and the associated class,
|
||||||
|
// whereas we highlight constructors as functions.
|
||||||
|
if (ast.isValid()) {
|
||||||
|
const ClangdAstPath path = getAstPath(ast, tokenRange(token));
|
||||||
|
if (!path.isEmpty()) {
|
||||||
|
if (path.last().kind() == "CXXConstructor") {
|
||||||
|
if (!path.last().arcanaContains("implicit"))
|
||||||
|
styles.mainStyle = C_FUNCTION;
|
||||||
|
} else if (path.last().kind() == "Record" && path.length() > 1) {
|
||||||
|
const ClangdAstNode node = path.at(path.length() - 2);
|
||||||
|
if (node.kind() == "CXXDestructor" && !node.arcanaContains("implicit")) {
|
||||||
|
styles.mainStyle = C_FUNCTION;
|
||||||
|
|
||||||
|
// https://github.com/clangd/clangd/issues/872
|
||||||
|
if (node.role() == "declaration")
|
||||||
|
styles.mixinStyles.push_back(C_DECLARATION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (token.type == "comment") { // "comment" means code disabled via the preprocessor
|
||||||
|
styles.mainStyle = C_DISABLED_CODE;
|
||||||
|
} else if (token.type == "namespace") {
|
||||||
|
styles.mainStyle = C_NAMESPACE;
|
||||||
|
} else if (token.type == "property") {
|
||||||
|
styles.mainStyle = C_FIELD;
|
||||||
|
} else if (token.type == "enum") {
|
||||||
|
styles.mainStyle = C_TYPE;
|
||||||
|
} else if (token.type == "enumMember") {
|
||||||
|
styles.mainStyle = C_ENUMERATION;
|
||||||
|
} else if (token.type == "parameter") {
|
||||||
|
styles.mainStyle = C_PARAMETER;
|
||||||
|
} else if (token.type == "macro") {
|
||||||
|
styles.mainStyle = C_PREPROCESSOR;
|
||||||
|
} else if (token.type == "type") {
|
||||||
|
styles.mainStyle = C_TYPE;
|
||||||
|
} else if (token.type == "typeParameter") {
|
||||||
|
// clangd reports both type and non-type template parameters as type parameters,
|
||||||
|
// but the latter can be distinguished by the readonly modifier.
|
||||||
|
styles.mainStyle = token.modifiers.contains(QLatin1String("readonly"))
|
||||||
|
? C_PARAMETER : C_TYPE;
|
||||||
|
}
|
||||||
|
if (token.modifiers.contains(QLatin1String("declaration")))
|
||||||
|
styles.mixinStyles.push_back(C_DECLARATION);
|
||||||
|
if (token.modifiers.contains(QLatin1String("static"))) {
|
||||||
|
if (styles.mainStyle != C_FIELD && styles.mainStyle != C_TEXT)
|
||||||
|
styles.mixinStyles.push_back(styles.mainStyle);
|
||||||
|
styles.mainStyle = C_STATIC_MEMBER;
|
||||||
|
}
|
||||||
|
if (isOutputParameter(token))
|
||||||
|
styles.mixinStyles.push_back(C_OUTPUT_ARGUMENT);
|
||||||
|
qCDebug(clangdLogHighlight) << "adding highlighting result"
|
||||||
|
<< token.line << token.column << token.length << int(styles.mainStyle);
|
||||||
|
return HighlightingResult(token.line, token.column, token.length, styles);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto results = QtConcurrent::blockingMapped<HighlightingResults>(tokens, toResult);
|
||||||
|
const QList<BlockRange> ifdefedOutBlocks = cleanupDisabledCode(results, &doc, docContents);
|
||||||
|
ExtraHighlightingResultsCollector(future, results, filePath, ast, &doc, docContents).collect();
|
||||||
|
if (!future.isCanceled()) {
|
||||||
|
qCInfo(clangdLogHighlight) << "reporting" << results.size() << "highlighting results";
|
||||||
|
QMetaObject::invokeMethod(textDocument, [textDocument, ifdefedOutBlocks, docRevision] {
|
||||||
|
if (textDocument && textDocument->document()->revision() == docRevision)
|
||||||
|
textDocument->setIfdefedOutBlocks(ifdefedOutBlocks);
|
||||||
|
}, Qt::QueuedConnection);
|
||||||
|
QList<Range> virtualRanges;
|
||||||
|
for (const HighlightingResult &r : results) {
|
||||||
|
if (r.textStyles.mainStyle != C_VIRTUAL_METHOD)
|
||||||
|
continue;
|
||||||
|
const Position startPos(r.line - 1, r.column - 1);
|
||||||
|
virtualRanges << Range(startPos, startPos.withOffset(r.length, &doc));
|
||||||
|
}
|
||||||
|
QMetaObject::invokeMethod(ClangModelManagerSupport::instance(),
|
||||||
|
[filePath, virtualRanges, docRevision] {
|
||||||
|
if (ClangdClient * const client
|
||||||
|
= ClangModelManagerSupport::instance()->clientForFile(filePath)) {
|
||||||
|
client->setVirtualRanges(filePath, virtualRanges, docRevision);
|
||||||
|
}
|
||||||
|
}, Qt::QueuedConnection);
|
||||||
|
future.reportResults(QVector<HighlightingResult>(results.cbegin(), results.cend()));
|
||||||
|
}
|
||||||
|
future.reportFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
ExtraHighlightingResultsCollector::ExtraHighlightingResultsCollector(
|
||||||
|
QFutureInterface<HighlightingResult> &future, HighlightingResults &results,
|
||||||
|
const Utils::FilePath &filePath, const ClangdAstNode &ast, const QTextDocument *doc,
|
||||||
|
const QString &docContent)
|
||||||
|
: m_future(future), m_results(results), m_filePath(filePath), m_ast(ast), m_doc(doc),
|
||||||
|
m_docContent(docContent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExtraHighlightingResultsCollector::collect()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < m_results.length(); ++i) {
|
||||||
|
const HighlightingResult res = m_results.at(i);
|
||||||
|
if (res.textStyles.mainStyle != C_PREPROCESSOR || res.length != 10)
|
||||||
|
continue;
|
||||||
|
const int pos = Utils::Text::positionInText(m_doc, res.line, res.column);
|
||||||
|
if (subViewLen(m_docContent, pos, 10) != QLatin1String("Q_PROPERTY"))
|
||||||
|
continue;
|
||||||
|
int endPos;
|
||||||
|
if (i < m_results.length() - 1) {
|
||||||
|
const HighlightingResult nextRes = m_results.at(i + 1);
|
||||||
|
endPos = Utils::Text::positionInText(m_doc, nextRes.line, nextRes.column);
|
||||||
|
} else {
|
||||||
|
endPos = m_docContent.length();
|
||||||
|
}
|
||||||
|
const QString qPropertyString = m_docContent.mid(pos, endPos - pos);
|
||||||
|
QPropertyHighlighter propHighlighter(m_doc, qPropertyString, pos);
|
||||||
|
for (const HighlightingResult &newRes : propHighlighter.highlight())
|
||||||
|
m_results.insert(++i, newRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_ast.isValid())
|
||||||
|
return;
|
||||||
|
visitNode(m_ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExtraHighlightingResultsCollector::lessThan(const HighlightingResult &r1,
|
||||||
|
const HighlightingResult &r2)
|
||||||
|
{
|
||||||
|
return r1.line < r2.line || (r1.line == r2.line && r1.column < r2.column)
|
||||||
|
|| (r1.line == r2.line && r1.column == r2.column && r1.length < r2.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ExtraHighlightingResultsCollector::onlyIndexOf(const QStringView &text,
|
||||||
|
const QStringView &subString, int from)
|
||||||
|
{
|
||||||
|
const int firstIndex = text.indexOf(subString, from);
|
||||||
|
if (firstIndex == -1)
|
||||||
|
return -1;
|
||||||
|
const int nextIndex = text.indexOf(subString, firstIndex + 1);
|
||||||
|
|
||||||
|
// The second condion deals with the off-by-one error in TemplateSpecialization nodes;
|
||||||
|
// see collectFromNode().
|
||||||
|
return nextIndex == -1 || nextIndex == firstIndex + 1 ? firstIndex : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unfortunately, the exact position of a specific token is usually not
|
||||||
|
// recorded in the AST, so if we need that, we have to search for it textually.
|
||||||
|
// In corner cases, this might get sabotaged by e.g. comments, in which case we give up.
|
||||||
|
int ExtraHighlightingResultsCollector::posForNodeStart(const ClangdAstNode &node) const
|
||||||
|
{
|
||||||
|
return Utils::Text::positionInText(m_doc, node.range().start().line() + 1,
|
||||||
|
node.range().start().character() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ExtraHighlightingResultsCollector::posForNodeEnd(const ClangdAstNode &node) const
|
||||||
|
{
|
||||||
|
return Utils::Text::positionInText(m_doc, node.range().end().line() + 1,
|
||||||
|
node.range().end().character() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExtraHighlightingResultsCollector::insertResult(const HighlightingResult &result)
|
||||||
|
{
|
||||||
|
if (!result.isValid()) // Some nodes don't have a range.
|
||||||
|
return;
|
||||||
|
const auto it = std::lower_bound(m_results.begin(), m_results.end(), result, lessThan);
|
||||||
|
if (it == m_results.end() || *it != result) {
|
||||||
|
|
||||||
|
// Prevent inserting expansions for function-like macros. For instance:
|
||||||
|
// #define TEST() "blubb"
|
||||||
|
// const char *s = TEST();
|
||||||
|
// The macro name is always shorter than the expansion and starts at the same
|
||||||
|
// location, so it should occur right before the insertion position.
|
||||||
|
if (it > m_results.begin() && (it - 1)->line == result.line
|
||||||
|
&& (it - 1)->column == result.column
|
||||||
|
&& (it - 1)->textStyles.mainStyle == C_PREPROCESSOR) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bogus ranges; e.g. QTCREATORBUG-27601
|
||||||
|
if (it != m_results.end()) {
|
||||||
|
const int nextStartPos = Utils::Text::positionInText(m_doc, it->line, it->column);
|
||||||
|
const int resultEndPos = Utils::Text::positionInText(m_doc, result.line, result.column)
|
||||||
|
+ result.length;
|
||||||
|
if (resultEndPos > nextStartPos)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qCDebug(clangdLogHighlight) << "adding additional highlighting result"
|
||||||
|
<< result.line << result.column << result.length;
|
||||||
|
m_results.insert(it, result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is for conversion operators, whose type part is only reported as a type by clangd.
|
||||||
|
if ((it->textStyles.mainStyle == C_TYPE
|
||||||
|
|| it->textStyles.mainStyle == C_PRIMITIVE_TYPE)
|
||||||
|
&& !result.textStyles.mixinStyles.empty()
|
||||||
|
&& result.textStyles.mixinStyles.at(0) == C_OPERATOR) {
|
||||||
|
it->textStyles.mixinStyles = result.textStyles.mixinStyles;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExtraHighlightingResultsCollector::insertResult(const ClangdAstNode &node, TextStyle style)
|
||||||
|
{
|
||||||
|
HighlightingResult result;
|
||||||
|
result.useTextSyles = true;
|
||||||
|
result.textStyles.mainStyle = style;
|
||||||
|
setResultPosFromRange(result, node.range());
|
||||||
|
insertResult(result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For matching the "<" and ">" brackets of template declarations, specializations
|
||||||
|
// and instantiations.
|
||||||
|
void ExtraHighlightingResultsCollector::insertAngleBracketInfo(int searchStart1, int searchEnd1,
|
||||||
|
int searchStart2, int searchEnd2)
|
||||||
|
{
|
||||||
|
const int openingAngleBracketPos = onlyIndexOf(
|
||||||
|
subViewEnd(m_docContent, searchStart1, searchEnd1),
|
||||||
|
QStringView(QStringLiteral("<")));
|
||||||
|
if (openingAngleBracketPos == -1)
|
||||||
|
return;
|
||||||
|
const int absOpeningAngleBracketPos = searchStart1 + openingAngleBracketPos;
|
||||||
|
if (absOpeningAngleBracketPos > searchStart2)
|
||||||
|
searchStart2 = absOpeningAngleBracketPos + 1;
|
||||||
|
if (searchStart2 >= searchEnd2)
|
||||||
|
return;
|
||||||
|
const int closingAngleBracketPos = onlyIndexOf(
|
||||||
|
subViewEnd(m_docContent, searchStart2, searchEnd2),
|
||||||
|
QStringView(QStringLiteral(">")));
|
||||||
|
if (closingAngleBracketPos == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const int absClosingAngleBracketPos = searchStart2 + closingAngleBracketPos;
|
||||||
|
if (absOpeningAngleBracketPos > absClosingAngleBracketPos)
|
||||||
|
return;
|
||||||
|
|
||||||
|
HighlightingResult result;
|
||||||
|
result.useTextSyles = true;
|
||||||
|
result.textStyles.mainStyle = C_PUNCTUATION;
|
||||||
|
Utils::Text::convertPosition(m_doc, absOpeningAngleBracketPos, &result.line, &result.column);
|
||||||
|
result.length = 1;
|
||||||
|
result.kind = CppEditor::SemanticHighlighter::AngleBracketOpen;
|
||||||
|
insertResult(result);
|
||||||
|
Utils::Text::convertPosition(m_doc, absClosingAngleBracketPos, &result.line, &result.column);
|
||||||
|
result.kind = CppEditor::SemanticHighlighter::AngleBracketClose;
|
||||||
|
insertResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExtraHighlightingResultsCollector::setResultPosFromRange(HighlightingResult &result,
|
||||||
|
const Range &range)
|
||||||
|
{
|
||||||
|
if (!range.isValid())
|
||||||
|
return;
|
||||||
|
const Position startPos = range.start();
|
||||||
|
const Position endPos = range.end();
|
||||||
|
result.line = startPos.line() + 1;
|
||||||
|
result.column = startPos.character() + 1;
|
||||||
|
result.length = endPos.toPositionInDocument(m_doc) - startPos.toPositionInDocument(m_doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExtraHighlightingResultsCollector::collectFromNode(const ClangdAstNode &node)
|
||||||
|
{
|
||||||
|
if (node.kind() == "UserDefinedLiteral")
|
||||||
|
return;
|
||||||
|
if (node.kind().endsWith("Literal")) {
|
||||||
|
const bool isKeyword = node.kind() == "CXXBoolLiteral"
|
||||||
|
|| node.kind() == "CXXNullPtrLiteral";
|
||||||
|
const bool isStringLike = !isKeyword && (node.kind().startsWith("String")
|
||||||
|
|| node.kind().startsWith("Character"));
|
||||||
|
const TextStyle style = isKeyword ? C_KEYWORD : isStringLike ? C_STRING : C_NUMBER;
|
||||||
|
insertResult(node, style);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (node.role() == "type" && node.kind() == "Builtin") {
|
||||||
|
insertResult(node, C_PRIMITIVE_TYPE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (node.role() == "attribute" && (node.kind() == "Override" || node.kind() == "Final")) {
|
||||||
|
insertResult(node, C_KEYWORD);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool isExpression = node.role() == "expression";
|
||||||
|
if (isExpression && node.kind() == "Predefined") {
|
||||||
|
insertResult(node, C_PREPROCESSOR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool isDeclaration = node.role() == "declaration";
|
||||||
|
const int nodeStartPos = posForNodeStart(node);
|
||||||
|
const int nodeEndPos = posForNodeEnd(node);
|
||||||
|
const QList<ClangdAstNode> children = node.children().value_or(QList<ClangdAstNode>());
|
||||||
|
|
||||||
|
// Match question mark and colon in ternary operators.
|
||||||
|
if (isExpression && node.kind() == "ConditionalOperator") {
|
||||||
|
if (children.size() != 3)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// The question mark is between sub-expressions 1 and 2, the colon is between
|
||||||
|
// sub-expressions 2 and 3.
|
||||||
|
const int searchStartPosQuestionMark = posForNodeEnd(children.first());
|
||||||
|
const int searchEndPosQuestionMark = posForNodeStart(children.at(1));
|
||||||
|
QStringView content = subViewEnd(m_docContent, searchStartPosQuestionMark,
|
||||||
|
searchEndPosQuestionMark);
|
||||||
|
const int questionMarkPos = onlyIndexOf(content, QStringView(QStringLiteral("?")));
|
||||||
|
if (questionMarkPos == -1)
|
||||||
|
return;
|
||||||
|
const int searchStartPosColon = posForNodeEnd(children.at(1));
|
||||||
|
const int searchEndPosColon = posForNodeStart(children.at(2));
|
||||||
|
content = subViewEnd(m_docContent, searchStartPosColon, searchEndPosColon);
|
||||||
|
const int colonPos = onlyIndexOf(content, QStringView(QStringLiteral(":")));
|
||||||
|
if (colonPos == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const int absQuestionMarkPos = searchStartPosQuestionMark + questionMarkPos;
|
||||||
|
const int absColonPos = searchStartPosColon + colonPos;
|
||||||
|
if (absQuestionMarkPos > absColonPos)
|
||||||
|
return;
|
||||||
|
|
||||||
|
HighlightingResult result;
|
||||||
|
result.useTextSyles = true;
|
||||||
|
result.textStyles.mainStyle = C_PUNCTUATION;
|
||||||
|
result.textStyles.mixinStyles.push_back(C_OPERATOR);
|
||||||
|
Utils::Text::convertPosition(m_doc, absQuestionMarkPos, &result.line, &result.column);
|
||||||
|
result.length = 1;
|
||||||
|
result.kind = CppEditor::SemanticHighlighter::TernaryIf;
|
||||||
|
insertResult(result);
|
||||||
|
Utils::Text::convertPosition(m_doc, absColonPos, &result.line, &result.column);
|
||||||
|
result.kind = CppEditor::SemanticHighlighter::TernaryElse;
|
||||||
|
insertResult(result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDeclaration && (node.kind() == "FunctionTemplate"
|
||||||
|
|| node.kind() == "ClassTemplate")) {
|
||||||
|
// The child nodes are the template parameters and and the function or class.
|
||||||
|
// The opening angle bracket is before the first child node, the closing angle
|
||||||
|
// bracket is before the function child node and after the last param node.
|
||||||
|
const QString classOrFunctionKind = QLatin1String(node.kind() == "FunctionTemplate"
|
||||||
|
? "Function" : "CXXRecord");
|
||||||
|
const auto functionOrClassIt = std::find_if(children.begin(), children.end(),
|
||||||
|
[&classOrFunctionKind](const ClangdAstNode &n) {
|
||||||
|
return n.role() == "declaration" && n.kind() == classOrFunctionKind;
|
||||||
|
});
|
||||||
|
if (functionOrClassIt == children.end() || functionOrClassIt == children.begin())
|
||||||
|
return;
|
||||||
|
const int firstTemplateParamStartPos = posForNodeStart(children.first());
|
||||||
|
const int lastTemplateParamEndPos = posForNodeEnd(*(functionOrClassIt - 1));
|
||||||
|
const int functionOrClassStartPos = posForNodeStart(*functionOrClassIt);
|
||||||
|
insertAngleBracketInfo(nodeStartPos, firstTemplateParamStartPos,
|
||||||
|
lastTemplateParamEndPos, functionOrClassStartPos);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto isTemplateParamDecl = [](const ClangdAstNode &node) {
|
||||||
|
return node.isTemplateParameterDeclaration();
|
||||||
|
};
|
||||||
|
if (isDeclaration && node.kind() == "TypeAliasTemplate") {
|
||||||
|
// Children are one node of type TypeAlias and the template parameters.
|
||||||
|
// The opening angle bracket is before the first parameter and the closing
|
||||||
|
// angle bracket is after the last parameter.
|
||||||
|
// The TypeAlias node seems to appear first in the AST, even though lexically
|
||||||
|
// is comes after the parameters. We don't rely on the order here.
|
||||||
|
// Note that there is a second pair of angle brackets. That one is part of
|
||||||
|
// a TemplateSpecialization, which is handled further below.
|
||||||
|
const auto firstTemplateParam = std::find_if(children.begin(), children.end(),
|
||||||
|
isTemplateParamDecl);
|
||||||
|
if (firstTemplateParam == children.end())
|
||||||
|
return;
|
||||||
|
const auto lastTemplateParam = std::find_if(children.rbegin(), children.rend(),
|
||||||
|
isTemplateParamDecl);
|
||||||
|
QTC_ASSERT(lastTemplateParam != children.rend(), return);
|
||||||
|
const auto typeAlias = std::find_if(children.begin(), children.end(),
|
||||||
|
[](const ClangdAstNode &n) { return n.kind() == "TypeAlias"; });
|
||||||
|
if (typeAlias == children.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const int firstTemplateParamStartPos = posForNodeStart(*firstTemplateParam);
|
||||||
|
const int lastTemplateParamEndPos = posForNodeEnd(*lastTemplateParam);
|
||||||
|
const int searchEndPos = posForNodeStart(*typeAlias);
|
||||||
|
insertAngleBracketInfo(nodeStartPos, firstTemplateParamStartPos,
|
||||||
|
lastTemplateParamEndPos, searchEndPos);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDeclaration && node.kind() == "ClassTemplateSpecialization") {
|
||||||
|
// There is one child of kind TemplateSpecialization. The first pair
|
||||||
|
// of angle brackets comes before that.
|
||||||
|
if (children.size() == 1) {
|
||||||
|
const int childNodePos = posForNodeStart(children.first());
|
||||||
|
insertAngleBracketInfo(nodeStartPos, childNodePos, nodeStartPos, childNodePos);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDeclaration && node.kind() == "TemplateTemplateParm") {
|
||||||
|
// The child nodes are template arguments and template parameters.
|
||||||
|
// Arguments seem to appear before parameters in the AST, even though they
|
||||||
|
// come after them in the source code. We don't rely on the order here.
|
||||||
|
const auto firstTemplateParam = std::find_if(children.begin(), children.end(),
|
||||||
|
isTemplateParamDecl);
|
||||||
|
if (firstTemplateParam == children.end())
|
||||||
|
return;
|
||||||
|
const auto lastTemplateParam = std::find_if(children.rbegin(), children.rend(),
|
||||||
|
isTemplateParamDecl);
|
||||||
|
QTC_ASSERT(lastTemplateParam != children.rend(), return);
|
||||||
|
const auto templateArg = std::find_if(children.begin(), children.end(),
|
||||||
|
[](const ClangdAstNode &n) { return n.role() == "template argument"; });
|
||||||
|
|
||||||
|
const int firstTemplateParamStartPos = posForNodeStart(*firstTemplateParam);
|
||||||
|
const int lastTemplateParamEndPos = posForNodeEnd(*lastTemplateParam);
|
||||||
|
const int searchEndPos = templateArg == children.end()
|
||||||
|
? nodeEndPos : posForNodeStart(*templateArg);
|
||||||
|
insertAngleBracketInfo(nodeStartPos, firstTemplateParamStartPos,
|
||||||
|
lastTemplateParamEndPos, searchEndPos);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// {static,dynamic,reinterpret}_cast<>().
|
||||||
|
if (isExpression && node.kind().startsWith("CXX") && node.kind().endsWith("Cast")) {
|
||||||
|
// First child is type, second child is expression.
|
||||||
|
// The opening angle bracket is before the first child, the closing angle bracket
|
||||||
|
// is between the two children.
|
||||||
|
if (children.size() == 2) {
|
||||||
|
insertAngleBracketInfo(nodeStartPos, posForNodeStart(children.first()),
|
||||||
|
posForNodeEnd(children.first()),
|
||||||
|
posForNodeStart(children.last()));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.kind() == "TemplateSpecialization") {
|
||||||
|
// First comes the template type, then the template arguments.
|
||||||
|
// The opening angle bracket is before the first template argument,
|
||||||
|
// the closing angle bracket is after the last template argument.
|
||||||
|
// The first child node has no range, so we start searching at the parent node.
|
||||||
|
if (children.size() >= 2) {
|
||||||
|
int searchStart2 = posForNodeEnd(children.last());
|
||||||
|
int searchEnd2 = nodeEndPos;
|
||||||
|
|
||||||
|
// There is a weird off-by-one error on the clang side: If there is a
|
||||||
|
// nested template instantiation *and* there is no space between
|
||||||
|
// the closing angle brackets, then the inner TemplateSpecialization node's range
|
||||||
|
// will extend one character too far, covering the outer's closing angle bracket.
|
||||||
|
// This is what we are correcting for here.
|
||||||
|
// This issue is tracked at https://github.com/clangd/clangd/issues/871.
|
||||||
|
if (searchStart2 == searchEnd2)
|
||||||
|
--searchStart2;
|
||||||
|
insertAngleBracketInfo(nodeStartPos, posForNodeStart(children.at(1)),
|
||||||
|
searchStart2, searchEnd2);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isExpression && !isDeclaration)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Operators, overloaded ones in particular.
|
||||||
|
static const QString operatorPrefix = "operator";
|
||||||
|
QString detail = node.detail().value_or(QString());
|
||||||
|
const bool isCallToNew = node.kind() == "CXXNew";
|
||||||
|
const bool isCallToDelete = node.kind() == "CXXDelete";
|
||||||
|
const auto isProperOperator = [&] {
|
||||||
|
if (isCallToNew || isCallToDelete)
|
||||||
|
return true;
|
||||||
|
if (!detail.startsWith(operatorPrefix))
|
||||||
|
return false;
|
||||||
|
if (detail == operatorPrefix)
|
||||||
|
return false;
|
||||||
|
const QChar nextChar = detail.at(operatorPrefix.length());
|
||||||
|
return !nextChar.isLetterOrNumber() && nextChar != '_';
|
||||||
|
};
|
||||||
|
if (!isProperOperator())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!isCallToNew && !isCallToDelete)
|
||||||
|
detail.remove(0, operatorPrefix.length());
|
||||||
|
|
||||||
|
HighlightingResult result;
|
||||||
|
result.useTextSyles = true;
|
||||||
|
const bool isConversionOp = node.kind() == "CXXConversion";
|
||||||
|
const bool isOverloaded = !isConversionOp
|
||||||
|
&& (isDeclaration || ((!isCallToNew && !isCallToDelete)
|
||||||
|
|| node.arcanaContains("CXXMethod")));
|
||||||
|
result.textStyles.mainStyle = isConversionOp
|
||||||
|
? C_PRIMITIVE_TYPE
|
||||||
|
: isCallToNew || isCallToDelete || detail.at(0).isSpace()
|
||||||
|
? C_KEYWORD : C_PUNCTUATION;
|
||||||
|
result.textStyles.mixinStyles.push_back(C_OPERATOR);
|
||||||
|
if (isOverloaded)
|
||||||
|
result.textStyles.mixinStyles.push_back(C_OVERLOADED_OPERATOR);
|
||||||
|
if (isDeclaration)
|
||||||
|
result.textStyles.mixinStyles.push_back(C_DECLARATION);
|
||||||
|
|
||||||
|
const QStringView nodeText = subViewEnd(m_docContent, nodeStartPos, nodeEndPos);
|
||||||
|
|
||||||
|
if (isCallToNew || isCallToDelete) {
|
||||||
|
result.line = node.range().start().line() + 1;
|
||||||
|
result.column = node.range().start().character() + 1;
|
||||||
|
result.length = isCallToNew ? 3 : 6;
|
||||||
|
insertResult(result);
|
||||||
|
if (node.arcanaContains("array")) {
|
||||||
|
const int openingBracketOffset = nodeText.indexOf('[');
|
||||||
|
if (openingBracketOffset == -1)
|
||||||
|
return;
|
||||||
|
const int closingBracketOffset = nodeText.lastIndexOf(']');
|
||||||
|
if (closingBracketOffset == -1 || closingBracketOffset < openingBracketOffset)
|
||||||
|
return;
|
||||||
|
|
||||||
|
result.textStyles.mainStyle = C_PUNCTUATION;
|
||||||
|
result.length = 1;
|
||||||
|
Utils::Text::convertPosition(m_doc,
|
||||||
|
nodeStartPos + openingBracketOffset,
|
||||||
|
&result.line, &result.column);
|
||||||
|
insertResult(result);
|
||||||
|
Utils::Text::convertPosition(m_doc,
|
||||||
|
nodeStartPos + closingBracketOffset,
|
||||||
|
&result.line, &result.column);
|
||||||
|
insertResult(result);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isExpression && (detail == QLatin1String("()") || detail == QLatin1String("[]"))) {
|
||||||
|
result.line = node.range().start().line() + 1;
|
||||||
|
result.column = node.range().start().character() + 1;
|
||||||
|
result.length = 1;
|
||||||
|
insertResult(result);
|
||||||
|
result.line = node.range().end().line() + 1;
|
||||||
|
result.column = node.range().end().character();
|
||||||
|
insertResult(result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int opStringLen = detail.at(0).isSpace() ? detail.length() - 1 : detail.length();
|
||||||
|
|
||||||
|
// The simple case: Call to operator+, +=, * etc.
|
||||||
|
if (nodeEndPos - nodeStartPos == opStringLen) {
|
||||||
|
setResultPosFromRange(result, node.range());
|
||||||
|
insertResult(result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int prefixOffset = nodeText.indexOf(operatorPrefix);
|
||||||
|
if (prefixOffset == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const bool isArray = detail == "[]";
|
||||||
|
const bool isCall = detail == "()";
|
||||||
|
const bool isArrayNew = detail == " new[]";
|
||||||
|
const bool isArrayDelete = detail == " delete[]";
|
||||||
|
const QStringView searchTerm = isArray || isCall
|
||||||
|
? QStringView(detail).chopped(1) : isArrayNew || isArrayDelete
|
||||||
|
? QStringView(detail).chopped(2) : detail;
|
||||||
|
const int opStringOffset = nodeText.indexOf(searchTerm, prefixOffset
|
||||||
|
+ operatorPrefix.length());
|
||||||
|
if (opStringOffset == -1 || nodeText.indexOf(operatorPrefix, opStringOffset) != -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const int opStringOffsetInDoc = nodeStartPos + opStringOffset
|
||||||
|
+ detail.length() - opStringLen;
|
||||||
|
Utils::Text::convertPosition(m_doc, opStringOffsetInDoc, &result.line, &result.column);
|
||||||
|
result.length = opStringLen;
|
||||||
|
if (isArray || isCall)
|
||||||
|
result.length = 1;
|
||||||
|
else if (isArrayNew || isArrayDelete)
|
||||||
|
result.length -= 2;
|
||||||
|
if (!isArray && !isCall)
|
||||||
|
insertResult(result);
|
||||||
|
if (!isArray && !isCall && !isArrayNew && !isArrayDelete)
|
||||||
|
return;
|
||||||
|
|
||||||
|
result.textStyles.mainStyle = C_PUNCTUATION;
|
||||||
|
result.length = 1;
|
||||||
|
const int openingParenOffset = nodeText.indexOf(
|
||||||
|
isCall ? '(' : '[', prefixOffset + operatorPrefix.length());
|
||||||
|
if (openingParenOffset == -1)
|
||||||
|
return;
|
||||||
|
const int closingParenOffset = nodeText.indexOf(isCall ? ')' : ']', openingParenOffset + 1);
|
||||||
|
if (closingParenOffset == -1 || closingParenOffset < openingParenOffset)
|
||||||
|
return;
|
||||||
|
Utils::Text::convertPosition(m_doc, nodeStartPos + openingParenOffset,
|
||||||
|
&result.line, &result.column);
|
||||||
|
insertResult(result);
|
||||||
|
Utils::Text::convertPosition(m_doc, nodeStartPos + closingParenOffset,
|
||||||
|
&result.line, &result.column);
|
||||||
|
insertResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExtraHighlightingResultsCollector::visitNode(const ClangdAstNode &node)
|
||||||
|
{
|
||||||
|
if (m_future.isCanceled())
|
||||||
|
return;
|
||||||
|
const ClangdAstNode::FileStatus prevFileStatus = m_currentFileStatus;
|
||||||
|
m_currentFileStatus = node.fileStatus(m_filePath);
|
||||||
|
if (m_currentFileStatus == ClangdAstNode::FileStatus::Unknown
|
||||||
|
&& prevFileStatus != ClangdAstNode::FileStatus::Ours) {
|
||||||
|
m_currentFileStatus = prevFileStatus;
|
||||||
|
}
|
||||||
|
switch (m_currentFileStatus) {
|
||||||
|
case ClangdAstNode::FileStatus::Ours:
|
||||||
|
case ClangdAstNode::FileStatus::Unknown:
|
||||||
|
collectFromNode(node);
|
||||||
|
[[fallthrough]];
|
||||||
|
case ClangdAstNode::FileStatus::Foreign:
|
||||||
|
case ClangCodeModel::Internal::ClangdAstNode::FileStatus::Mixed: {
|
||||||
|
const auto children = node.children();
|
||||||
|
if (!children)
|
||||||
|
return;
|
||||||
|
for (const ClangdAstNode &childNode : *children)
|
||||||
|
visitNode(childNode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_currentFileStatus = prevFileStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ClangCodeModel::Internal
|
@@ -1,6 +1,6 @@
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
**
|
**
|
||||||
** Copyright (C) 2017 The Qt Company Ltd.
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
** Contact: https://www.qt.io/licensing/
|
** Contact: https://www.qt.io/licensing/
|
||||||
**
|
**
|
||||||
** This file is part of Qt Creator.
|
** This file is part of Qt Creator.
|
||||||
@@ -25,34 +25,33 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "abstractremotelinuxdeployservice.h"
|
#include <QFutureInterface>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
#include <QPointer>
|
||||||
|
#include <QVersionNumber>
|
||||||
|
|
||||||
namespace RemoteLinux {
|
namespace LanguageClient { class ExpandedSemanticToken; }
|
||||||
namespace Internal { class RemoteLinuxKillAppServicePrivate; }
|
namespace TextEditor {
|
||||||
|
class HighlightingResult;
|
||||||
|
class TextDocument;
|
||||||
|
}
|
||||||
|
namespace Utils { class FilePath; }
|
||||||
|
|
||||||
class REMOTELINUX_EXPORT RemoteLinuxKillAppService : public AbstractRemoteLinuxDeployService
|
namespace ClangCodeModel::Internal {
|
||||||
{
|
class ClangdAstNode;
|
||||||
Q_OBJECT
|
class TaskTimer;
|
||||||
public:
|
Q_DECLARE_LOGGING_CATEGORY(clangdLogHighlight);
|
||||||
RemoteLinuxKillAppService();
|
|
||||||
~RemoteLinuxKillAppService() override;
|
|
||||||
|
|
||||||
void setRemoteExecutable(const QString &filePath);
|
void doSemanticHighlighting(
|
||||||
|
QFutureInterface<TextEditor::HighlightingResult> &future,
|
||||||
|
const Utils::FilePath &filePath,
|
||||||
|
const QList<LanguageClient::ExpandedSemanticToken> &tokens,
|
||||||
|
const QString &docContents,
|
||||||
|
const ClangdAstNode &ast,
|
||||||
|
const QPointer<TextEditor::TextDocument> &textDocument,
|
||||||
|
int docRevision,
|
||||||
|
const QVersionNumber &clangdVersion,
|
||||||
|
const TaskTimer &taskTimer
|
||||||
|
);
|
||||||
|
|
||||||
private:
|
} // namespace ClangCodeModel::Internal
|
||||||
void handleStdErr();
|
|
||||||
void handleProcessFinished();
|
|
||||||
|
|
||||||
bool isDeploymentNecessary() const override;
|
|
||||||
|
|
||||||
void doDeploy() override;
|
|
||||||
void stopDeployment() override;
|
|
||||||
|
|
||||||
void handleSignalOpFinished(const QString &errorMessage);
|
|
||||||
void cleanup();
|
|
||||||
void finishDeployment();
|
|
||||||
|
|
||||||
Internal::RemoteLinuxKillAppServicePrivate * const d;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace RemoteLinux
|
|
108
src/plugins/clangcodemodel/tasktimers.cpp
Normal file
108
src/plugins/clangcodemodel/tasktimers.cpp
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "tasktimers.h"
|
||||||
|
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
|
namespace ClangCodeModel::Internal {
|
||||||
|
|
||||||
|
Q_LOGGING_CATEGORY(clangdLogTiming, "qtc.clangcodemodel.clangd.timing", QtWarningMsg);
|
||||||
|
|
||||||
|
void ClangCodeModel::Internal::TaskTimer::stopTask()
|
||||||
|
{
|
||||||
|
// This can happen due to the RAII mechanism employed with SubtaskTimer.
|
||||||
|
// The subtask timers will expire immediately after, so this does not distort
|
||||||
|
// the timing data.
|
||||||
|
if (m_subtasks > 0) {
|
||||||
|
QTC_CHECK(m_timer.isValid());
|
||||||
|
m_elapsedMs += m_timer.elapsed();
|
||||||
|
m_timer.invalidate();
|
||||||
|
m_subtasks = 0;
|
||||||
|
}
|
||||||
|
m_started = false;
|
||||||
|
qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": took " << m_elapsedMs
|
||||||
|
<< " ms in UI thread";
|
||||||
|
m_elapsedMs = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskTimer::startSubtask()
|
||||||
|
{
|
||||||
|
// We have some callbacks that are either synchronous or asynchronous, depending on
|
||||||
|
// dynamic conditions. In the sync case, we will have nested subtasks, in which case
|
||||||
|
// the inner ones must not collect timing data, as their code blocks are already covered.
|
||||||
|
if (++m_subtasks > 1)
|
||||||
|
return;
|
||||||
|
if (!m_started) {
|
||||||
|
QTC_ASSERT(m_elapsedMs == 0, m_elapsedMs = 0);
|
||||||
|
m_started = true;
|
||||||
|
m_finalized = false;
|
||||||
|
qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": starting";
|
||||||
|
|
||||||
|
// Used by ThreadedSubtaskTimer to mark the end of the whole highlighting operation
|
||||||
|
m_startTimer.restart();
|
||||||
|
}
|
||||||
|
qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": subtask started at "
|
||||||
|
<< QDateTime::currentDateTime().time().toString("hh:mm:ss.zzz");
|
||||||
|
QTC_CHECK(!m_timer.isValid());
|
||||||
|
m_timer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskTimer::stopSubtask(bool isFinalizing)
|
||||||
|
{
|
||||||
|
if (m_subtasks == 0) // See stopTask().
|
||||||
|
return;
|
||||||
|
if (isFinalizing)
|
||||||
|
m_finalized = true;
|
||||||
|
if (--m_subtasks > 0) // See startSubtask().
|
||||||
|
return;
|
||||||
|
qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": subtask stopped at "
|
||||||
|
<< QDateTime::currentDateTime().time().toString("hh:mm:ss.zzz");
|
||||||
|
QTC_CHECK(m_timer.isValid());
|
||||||
|
m_elapsedMs += m_timer.elapsed();
|
||||||
|
m_timer.invalidate();
|
||||||
|
if (m_finalized)
|
||||||
|
stopTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadedSubtaskTimer::ThreadedSubtaskTimer(const QString &task, const TaskTimer &taskTimer)
|
||||||
|
: m_task(task), m_taskTimer(taskTimer)
|
||||||
|
{
|
||||||
|
qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": starting thread";
|
||||||
|
m_timer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadedSubtaskTimer::~ThreadedSubtaskTimer()
|
||||||
|
{
|
||||||
|
qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": took " << m_timer.elapsed()
|
||||||
|
<< " ms in dedicated thread";
|
||||||
|
|
||||||
|
qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": Start to end: "
|
||||||
|
<< m_taskTimer.startTimer().elapsed() << " ms";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ClangCodeModel::Internal
|
90
src/plugins/clangcodemodel/tasktimers.h
Normal file
90
src/plugins/clangcodemodel/tasktimers.h
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QElapsedTimer>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace ClangCodeModel::Internal {
|
||||||
|
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(clangdLogTiming);
|
||||||
|
|
||||||
|
// TODO: Generalize, document interface and move to Utils?
|
||||||
|
class TaskTimer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TaskTimer(const QString &task) : m_task(task) {}
|
||||||
|
|
||||||
|
void stopTask();
|
||||||
|
void startSubtask();
|
||||||
|
void stopSubtask(bool isFinalizing);
|
||||||
|
|
||||||
|
QElapsedTimer startTimer() const { return m_startTimer; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const QString m_task;
|
||||||
|
QElapsedTimer m_timer;
|
||||||
|
QElapsedTimer m_startTimer;
|
||||||
|
qint64 m_elapsedMs = 0;
|
||||||
|
int m_subtasks = 0;
|
||||||
|
bool m_started = false;
|
||||||
|
bool m_finalized = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SubtaskTimer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SubtaskTimer(TaskTimer &timer) : m_timer(timer) { m_timer.startSubtask(); }
|
||||||
|
~SubtaskTimer() { m_timer.stopSubtask(m_isFinalizing); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void makeFinalizing() { m_isFinalizing = true; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
TaskTimer &m_timer;
|
||||||
|
bool m_isFinalizing = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FinalizingSubtaskTimer : public SubtaskTimer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FinalizingSubtaskTimer(TaskTimer &timer) : SubtaskTimer(timer) { makeFinalizing(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class ThreadedSubtaskTimer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ThreadedSubtaskTimer(const QString &task, const TaskTimer &taskTimer);
|
||||||
|
~ThreadedSubtaskTimer();
|
||||||
|
|
||||||
|
private:
|
||||||
|
const QString m_task;
|
||||||
|
QElapsedTimer m_timer;
|
||||||
|
const TaskTimer &m_taskTimer;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ClangCodeModel::Internal
|
@@ -51,6 +51,7 @@
|
|||||||
|
|
||||||
#include <debugger/analyzer/analyzermanager.h>
|
#include <debugger/analyzer/analyzermanager.h>
|
||||||
|
|
||||||
|
#include <projectexplorer/buildconfiguration.h>
|
||||||
#include <projectexplorer/kitinformation.h>
|
#include <projectexplorer/kitinformation.h>
|
||||||
#include <projectexplorer/projectexplorer.h>
|
#include <projectexplorer/projectexplorer.h>
|
||||||
#include <projectexplorer/projectexplorericons.h>
|
#include <projectexplorer/projectexplorericons.h>
|
||||||
|
@@ -253,10 +253,10 @@ void ClangToolRunWorker::start()
|
|||||||
|
|
||||||
// Collect files
|
// Collect files
|
||||||
const auto clangIncludeDirAndVersion =
|
const auto clangIncludeDirAndVersion =
|
||||||
getClangIncludeDirAndVersion(runControl()->runnable().command.executable());
|
getClangIncludeDirAndVersion(runControl()->commandLine().executable());
|
||||||
const AnalyzeUnits unitsToProcess = unitsToAnalyze(clangIncludeDirAndVersion.first,
|
const AnalyzeUnits unitsToProcess = unitsToAnalyze(clangIncludeDirAndVersion.first,
|
||||||
clangIncludeDirAndVersion.second);
|
clangIncludeDirAndVersion.second);
|
||||||
qCDebug(LOG) << Q_FUNC_INFO << runControl()->runnable().command.executable()
|
qCDebug(LOG) << Q_FUNC_INFO << runControl()->commandLine().executable()
|
||||||
<< clangIncludeDirAndVersion.first << clangIncludeDirAndVersion.second;
|
<< clangIncludeDirAndVersion.first << clangIncludeDirAndVersion.second;
|
||||||
qCDebug(LOG) << "Files to process:" << unitsToProcess;
|
qCDebug(LOG) << "Files to process:" << unitsToProcess;
|
||||||
|
|
||||||
|
@@ -26,7 +26,6 @@
|
|||||||
#include "documentclangtoolrunner.h"
|
#include "documentclangtoolrunner.h"
|
||||||
|
|
||||||
#include "clangfileinfo.h"
|
#include "clangfileinfo.h"
|
||||||
#include "clangfixitsrefactoringchanges.h"
|
|
||||||
#include "clangtidyclazyrunner.h"
|
#include "clangtidyclazyrunner.h"
|
||||||
#include "clangtoolruncontrol.h"
|
#include "clangtoolruncontrol.h"
|
||||||
#include "clangtoolsconstants.h"
|
#include "clangtoolsconstants.h"
|
||||||
@@ -39,13 +38,18 @@
|
|||||||
#include <coreplugin/documentmanager.h>
|
#include <coreplugin/documentmanager.h>
|
||||||
#include <coreplugin/editormanager/editormanager.h>
|
#include <coreplugin/editormanager/editormanager.h>
|
||||||
#include <coreplugin/editormanager/ieditor.h>
|
#include <coreplugin/editormanager/ieditor.h>
|
||||||
|
|
||||||
#include <cppeditor/cppmodelmanager.h>
|
#include <cppeditor/cppmodelmanager.h>
|
||||||
|
|
||||||
|
#include <projectexplorer/buildconfiguration.h>
|
||||||
#include <projectexplorer/buildtargettype.h>
|
#include <projectexplorer/buildtargettype.h>
|
||||||
#include <projectexplorer/session.h>
|
#include <projectexplorer/session.h>
|
||||||
#include <projectexplorer/target.h>
|
#include <projectexplorer/target.h>
|
||||||
|
|
||||||
#include <texteditor/textdocument.h>
|
#include <texteditor/textdocument.h>
|
||||||
#include <texteditor/texteditor.h>
|
#include <texteditor/texteditor.h>
|
||||||
#include <texteditor/textmark.h>
|
#include <texteditor/textmark.h>
|
||||||
|
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
#include <utils/utilsicons.h>
|
#include <utils/utilsicons.h>
|
||||||
|
|
||||||
|
@@ -1196,7 +1196,7 @@ Tasks CMakeConfigurationKitAspect::validate(const Kit *k) const
|
|||||||
if (!version || !version->isValid()) {
|
if (!version || !version->isValid()) {
|
||||||
addWarning(tr("CMake configuration has a path to a qmake binary set, "
|
addWarning(tr("CMake configuration has a path to a qmake binary set, "
|
||||||
"even though the kit has no valid Qt version."));
|
"even though the kit has no valid Qt version."));
|
||||||
} else if (qmakePath != version->qmakeFilePath() && isQt4) {
|
} else if (qmakePath != version->queryToolFilePath() && isQt4) {
|
||||||
addWarning(tr("CMake configuration has a path to a qmake binary set "
|
addWarning(tr("CMake configuration has a path to a qmake binary set "
|
||||||
"that does not match the qmake binary path "
|
"that does not match the qmake binary path "
|
||||||
"configured in the Qt version."));
|
"configured in the Qt version."));
|
||||||
@@ -1219,7 +1219,7 @@ Tasks CMakeConfigurationKitAspect::validate(const Kit *k) const
|
|||||||
if (!tcC || !tcC->isValid()) {
|
if (!tcC || !tcC->isValid()) {
|
||||||
addWarning(tr("CMake configuration has a path to a C compiler set, "
|
addWarning(tr("CMake configuration has a path to a C compiler set, "
|
||||||
"even though the kit has no valid tool chain."));
|
"even though the kit has no valid tool chain."));
|
||||||
} else if (tcCPath != tcC->compilerCommand()) {
|
} else if (tcCPath != tcC->compilerCommand() && tcCPath != tcC->compilerCommand().onDevice(tcCPath)) {
|
||||||
addWarning(tr("CMake configuration has a path to a C compiler set "
|
addWarning(tr("CMake configuration has a path to a C compiler set "
|
||||||
"that does not match the compiler path "
|
"that does not match the compiler path "
|
||||||
"configured in the tool chain of the kit."));
|
"configured in the tool chain of the kit."));
|
||||||
@@ -1235,7 +1235,7 @@ Tasks CMakeConfigurationKitAspect::validate(const Kit *k) const
|
|||||||
if (!tcCxx || !tcCxx->isValid()) {
|
if (!tcCxx || !tcCxx->isValid()) {
|
||||||
addWarning(tr("CMake configuration has a path to a C++ compiler set, "
|
addWarning(tr("CMake configuration has a path to a C++ compiler set, "
|
||||||
"even though the kit has no valid tool chain."));
|
"even though the kit has no valid tool chain."));
|
||||||
} else if (tcCxxPath != tcCxx->compilerCommand()) {
|
} else if (tcCxxPath != tcCxx->compilerCommand() && tcCxxPath != tcCxx->compilerCommand().onDevice(tcCxxPath)) {
|
||||||
addWarning(tr("CMake configuration has a path to a C++ compiler set "
|
addWarning(tr("CMake configuration has a path to a C++ compiler set "
|
||||||
"that does not match the compiler path "
|
"that does not match the compiler path "
|
||||||
"configured in the tool chain of the kit."));
|
"configured in the tool chain of the kit."));
|
||||||
|
@@ -129,18 +129,16 @@ void StartRemoteDialog::validate()
|
|||||||
d->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(valid);
|
d->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(valid);
|
||||||
}
|
}
|
||||||
|
|
||||||
Runnable StartRemoteDialog::runnable() const
|
CommandLine StartRemoteDialog::commandLine() const
|
||||||
{
|
{
|
||||||
Kit *kit = d->kitChooser->currentKit();
|
const Kit *kit = d->kitChooser->currentKit();
|
||||||
IDevice::ConstPtr device = DeviceKitAspect::device(kit);
|
const FilePath filePath = DeviceKitAspect::deviceFilePath(kit, d->executable->text());
|
||||||
FilePath filePath = FilePath::fromString(d->executable->text());
|
return {filePath, d->arguments->text(), CommandLine::Raw};
|
||||||
if (device)
|
}
|
||||||
filePath = device->filePath(d->arguments->text());
|
|
||||||
|
|
||||||
Runnable r;
|
FilePath StartRemoteDialog::workingDirectory() const
|
||||||
r.command = {filePath, d->arguments->text(), CommandLine::Raw};
|
{
|
||||||
r.workingDirectory = FilePath::fromString(d->workingDirectory->text());
|
return FilePath::fromString(d->workingDirectory->text());
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Debugger
|
} // namespace Debugger
|
||||||
|
@@ -29,7 +29,10 @@
|
|||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
|
|
||||||
namespace ProjectExplorer { class Runnable; }
|
namespace Utils {
|
||||||
|
class CommandLine;
|
||||||
|
class FilePath;
|
||||||
|
} // Utils
|
||||||
|
|
||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
|
|
||||||
@@ -43,7 +46,8 @@ public:
|
|||||||
explicit StartRemoteDialog(QWidget *parent = nullptr);
|
explicit StartRemoteDialog(QWidget *parent = nullptr);
|
||||||
~StartRemoteDialog() override;
|
~StartRemoteDialog() override;
|
||||||
|
|
||||||
ProjectExplorer::Runnable runnable() const;
|
Utils::CommandLine commandLine() const;
|
||||||
|
Utils::FilePath workingDirectory() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void validate();
|
void validate();
|
||||||
|
@@ -36,14 +36,6 @@ const char MODE_DEBUG[] = "Mode.Debug";
|
|||||||
// Debug mode context
|
// Debug mode context
|
||||||
const char C_DEBUGMODE[] = "Debugger.DebugMode";
|
const char C_DEBUGMODE[] = "Debugger.DebugMode";
|
||||||
|
|
||||||
// Common debugger constants.
|
|
||||||
const char kPeripheralDescriptionFile[] = "PeripheralDescriptionFile";
|
|
||||||
|
|
||||||
// UVSC-specific debugger constants.
|
|
||||||
const char kUVisionProjectFilePath[] = "UVisionProjectFilePath";
|
|
||||||
const char kUVisionOptionsFilePath[] = "UVisionOptionsFilePath";
|
|
||||||
const char kUVisionSimulator[] = "UVisionSimulator";
|
|
||||||
|
|
||||||
} // namespace Constants
|
} // namespace Constants
|
||||||
|
|
||||||
// Keep in sync with dumper.py
|
// Keep in sync with dumper.py
|
||||||
|
@@ -206,6 +206,14 @@ public:
|
|||||||
|
|
||||||
Utils::FilePath dumperPath;
|
Utils::FilePath dumperPath;
|
||||||
int fallbackQtVersion = 0x50200;
|
int fallbackQtVersion = 0x50200;
|
||||||
|
|
||||||
|
// Common debugger constants.
|
||||||
|
Utils::FilePath peripheralDescriptionFile;
|
||||||
|
|
||||||
|
// UVSC-specific debugger constants.
|
||||||
|
Utils::FilePath uVisionProjectFilePath;
|
||||||
|
Utils::FilePath uVisionOptionsFilePath;
|
||||||
|
bool uVisionSimulator = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class UpdateParameters
|
class UpdateParameters
|
||||||
|
@@ -669,7 +669,9 @@ void DebuggerItemManagerPrivate::autoDetectCdbDebuggers()
|
|||||||
|
|
||||||
for (const QFileInfo &kitFolderFi : kitFolders) {
|
for (const QFileInfo &kitFolderFi : kitFolders) {
|
||||||
const QString path = kitFolderFi.absoluteFilePath();
|
const QString path = kitFolderFi.absoluteFilePath();
|
||||||
const QStringList abis = {"x86", "x64", "arm64"};
|
QStringList abis = {"x86", "x64"};
|
||||||
|
if (HostOsInfo::hostArchitecture() == HostOsInfo::HostArchitectureArm)
|
||||||
|
abis << "arm64";
|
||||||
for (const QString &abi: abis) {
|
for (const QString &abi: abis) {
|
||||||
const QFileInfo cdbBinary(path + "/Debuggers/" + abi + "/cdb.exe");
|
const QFileInfo cdbBinary(path + "/Debuggers/" + abi + "/cdb.exe");
|
||||||
if (cdbBinary.isExecutable())
|
if (cdbBinary.isExecutable())
|
||||||
|
@@ -1736,8 +1736,7 @@ RunControl *DebuggerPluginPrivate::attachToRunningProcess(Kit *kit,
|
|||||||
runControl->setDisplayName(tr("Process %1").arg(processInfo.processId));
|
runControl->setDisplayName(tr("Process %1").arg(processInfo.processId));
|
||||||
auto debugger = new DebuggerRunTool(runControl);
|
auto debugger = new DebuggerRunTool(runControl);
|
||||||
debugger->setAttachPid(ProcessHandle(processInfo.processId));
|
debugger->setAttachPid(ProcessHandle(processInfo.processId));
|
||||||
debugger->setInferiorExecutable(FilePath::fromString(processInfo.executable));
|
debugger->setInferiorExecutable(device->filePath(processInfo.executable));
|
||||||
debugger->setInferiorDevice(device);
|
|
||||||
debugger->setStartMode(AttachToLocalProcess);
|
debugger->setStartMode(AttachToLocalProcess);
|
||||||
debugger->setCloseMode(DetachAtClose);
|
debugger->setCloseMode(DetachAtClose);
|
||||||
debugger->setContinueAfterAttach(contAfterAttach);
|
debugger->setContinueAfterAttach(contAfterAttach);
|
||||||
|
@@ -395,11 +395,6 @@ void DebuggerRunTool::setInferiorEnvironment(const Utils::Environment &env)
|
|||||||
m_runParameters.inferior.environment = env;
|
m_runParameters.inferior.environment = env;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebuggerRunTool::setInferiorDevice(IDevice::ConstPtr device)
|
|
||||||
{
|
|
||||||
m_runParameters.inferior.device = device;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DebuggerRunTool::setRunControlName(const QString &name)
|
void DebuggerRunTool::setRunControlName(const QString &name)
|
||||||
{
|
{
|
||||||
m_runParameters.displayName = name;
|
m_runParameters.displayName = name;
|
||||||
@@ -910,7 +905,7 @@ DebuggerRunTool::DebuggerRunTool(RunControl *runControl, AllowTerminal allowTerm
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Runnable inferior = runnable();
|
Runnable inferior = runControl->runnable();
|
||||||
const FilePath &debuggerExecutable = m_runParameters.debugger.command.executable();
|
const FilePath &debuggerExecutable = m_runParameters.debugger.command.executable();
|
||||||
inferior.command.setExecutable(inferior.command.executable().onDevice(debuggerExecutable));
|
inferior.command.setExecutable(inferior.command.executable().onDevice(debuggerExecutable));
|
||||||
inferior.workingDirectory = inferior.workingDirectory.onDevice(debuggerExecutable);
|
inferior.workingDirectory = inferior.workingDirectory.onDevice(debuggerExecutable);
|
||||||
|
@@ -76,7 +76,6 @@ public:
|
|||||||
void setInferior(const ProjectExplorer::Runnable &runnable);
|
void setInferior(const ProjectExplorer::Runnable &runnable);
|
||||||
void setInferiorExecutable(const Utils::FilePath &executable);
|
void setInferiorExecutable(const Utils::FilePath &executable);
|
||||||
void setInferiorEnvironment(const Utils::Environment &env); // Used by GammaRay plugin
|
void setInferiorEnvironment(const Utils::Environment &env); // Used by GammaRay plugin
|
||||||
void setInferiorDevice(ProjectExplorer::IDeviceConstPtr device); // Used by cdbengine
|
|
||||||
void setRunControlName(const QString &name);
|
void setRunControlName(const QString &name);
|
||||||
void setStartMessage(const QString &msg);
|
void setStartMessage(const QString &msg);
|
||||||
void addQmlServerInferiorCommandLineArgumentIfNeeded();
|
void addQmlServerInferiorCommandLineArgumentIfNeeded();
|
||||||
@@ -132,6 +131,8 @@ public:
|
|||||||
Internal::TerminalRunner *terminalRunner() const;
|
Internal::TerminalRunner *terminalRunner() const;
|
||||||
DebuggerEngineType cppEngineType() const;
|
DebuggerEngineType cppEngineType() const;
|
||||||
|
|
||||||
|
Internal::DebuggerRunParameters &runParameters() { return m_runParameters; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool fixupParameters();
|
bool fixupParameters();
|
||||||
void handleEngineStarted(Internal::DebuggerEngine *engine);
|
void handleEngineStarted(Internal::DebuggerEngine *engine);
|
||||||
|
@@ -25,23 +25,21 @@
|
|||||||
|
|
||||||
#include "loadcoredialog.h"
|
#include "loadcoredialog.h"
|
||||||
|
|
||||||
#include "debuggerdialogs.h"
|
|
||||||
#include "debuggerkitinformation.h"
|
#include "debuggerkitinformation.h"
|
||||||
#include "gdb/gdbengine.h"
|
#include "gdb/gdbengine.h"
|
||||||
|
|
||||||
#include <projectexplorer/devicesupport/devicefilesystemmodel.h>
|
#include <projectexplorer/devicesupport/devicefilesystemmodel.h>
|
||||||
|
#include <projectexplorer/devicesupport/filetransfer.h>
|
||||||
#include <projectexplorer/devicesupport/idevice.h>
|
#include <projectexplorer/devicesupport/idevice.h>
|
||||||
#include <projectexplorer/kitinformation.h>
|
#include <projectexplorer/kitchooser.h>
|
||||||
#include <projectexplorer/projectexplorerconstants.h>
|
|
||||||
#include <utils/pathchooser.h>
|
#include <utils/pathchooser.h>
|
||||||
|
#include <utils/processinterface.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
#include <utils/runextensions.h>
|
|
||||||
#include <utils/temporaryfile.h>
|
#include <utils/temporaryfile.h>
|
||||||
|
|
||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
#include <QDialogButtonBox>
|
#include <QDialogButtonBox>
|
||||||
#include <QFormLayout>
|
#include <QFormLayout>
|
||||||
#include <QFutureWatcher>
|
|
||||||
#include <QHeaderView>
|
#include <QHeaderView>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
@@ -69,7 +67,6 @@ class SelectRemoteFileDialog : public QDialog
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
explicit SelectRemoteFileDialog(QWidget *parent);
|
explicit SelectRemoteFileDialog(QWidget *parent);
|
||||||
~SelectRemoteFileDialog();
|
|
||||||
|
|
||||||
void attachToDevice(Kit *k);
|
void attachToDevice(Kit *k);
|
||||||
FilePath localFile() const { return m_localFile; }
|
FilePath localFile() const { return m_localFile; }
|
||||||
@@ -77,7 +74,6 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void selectFile();
|
void selectFile();
|
||||||
void clearWatcher();
|
|
||||||
|
|
||||||
QSortFilterProxyModel m_model;
|
QSortFilterProxyModel m_model;
|
||||||
DeviceFileSystemModel m_fileSystemModel;
|
DeviceFileSystemModel m_fileSystemModel;
|
||||||
@@ -86,7 +82,7 @@ private:
|
|||||||
QDialogButtonBox *m_buttonBox;
|
QDialogButtonBox *m_buttonBox;
|
||||||
FilePath m_localFile;
|
FilePath m_localFile;
|
||||||
FilePath m_remoteFile;
|
FilePath m_remoteFile;
|
||||||
QFutureWatcher<bool> *m_watcher = nullptr;
|
FileTransfer m_fileTransfer;
|
||||||
};
|
};
|
||||||
|
|
||||||
SelectRemoteFileDialog::SelectRemoteFileDialog(QWidget *parent)
|
SelectRemoteFileDialog::SelectRemoteFileDialog(QWidget *parent)
|
||||||
@@ -119,11 +115,21 @@ SelectRemoteFileDialog::SelectRemoteFileDialog(QWidget *parent)
|
|||||||
|
|
||||||
connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||||
connect(m_buttonBox, &QDialogButtonBox::accepted, this, &SelectRemoteFileDialog::selectFile);
|
connect(m_buttonBox, &QDialogButtonBox::accepted, this, &SelectRemoteFileDialog::selectFile);
|
||||||
}
|
|
||||||
|
|
||||||
SelectRemoteFileDialog::~SelectRemoteFileDialog()
|
connect(&m_fileTransfer, &FileTransfer::done, this, [this](const ProcessResultData &result) {
|
||||||
{
|
const bool success = result.m_error == QProcess::UnknownError
|
||||||
clearWatcher();
|
&& result.m_exitStatus == QProcess::NormalExit
|
||||||
|
&& result.m_exitCode == 0;
|
||||||
|
if (success) {
|
||||||
|
m_textBrowser->append(tr("Download of remote file succeeded."));
|
||||||
|
accept();
|
||||||
|
} else {
|
||||||
|
m_textBrowser->append(tr("Download of remote file failed: %1")
|
||||||
|
.arg(result.m_errorString));
|
||||||
|
m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
|
||||||
|
m_fileSystemView->setEnabled(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void SelectRemoteFileDialog::attachToDevice(Kit *k)
|
void SelectRemoteFileDialog::attachToDevice(Kit *k)
|
||||||
@@ -135,22 +141,6 @@ void SelectRemoteFileDialog::attachToDevice(Kit *k)
|
|||||||
m_fileSystemModel.setDevice(device);
|
m_fileSystemModel.setDevice(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void copyFile(QFutureInterface<bool> &futureInterface, const FilePath &remoteSource,
|
|
||||||
const FilePath &localDestination)
|
|
||||||
{
|
|
||||||
// TODO: this should be implemented transparently in FilePath::copyFile()
|
|
||||||
// The code here should be just:
|
|
||||||
//
|
|
||||||
// futureInterface.reportResult(remoteSource.copyFile(localDestination));
|
|
||||||
//
|
|
||||||
// The implementation below won't handle really big files, like core files.
|
|
||||||
|
|
||||||
const QByteArray data = remoteSource.fileContents();
|
|
||||||
if (futureInterface.isCanceled())
|
|
||||||
return;
|
|
||||||
futureInterface.reportResult(localDestination.writeFileContents(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SelectRemoteFileDialog::selectFile()
|
void SelectRemoteFileDialog::selectFile()
|
||||||
{
|
{
|
||||||
QModelIndex idx = m_model.mapToSource(m_fileSystemView->currentIndex());
|
QModelIndex idx = m_model.mapToSource(m_fileSystemView->currentIndex());
|
||||||
@@ -161,7 +151,7 @@ void SelectRemoteFileDialog::selectFile()
|
|||||||
m_fileSystemView->setEnabled(false);
|
m_fileSystemView->setEnabled(false);
|
||||||
|
|
||||||
{
|
{
|
||||||
Utils::TemporaryFile localFile("remotecore-XXXXXX");
|
TemporaryFile localFile("remotecore-XXXXXX");
|
||||||
localFile.open();
|
localFile.open();
|
||||||
m_localFile = FilePath::fromString(localFile.fileName());
|
m_localFile = FilePath::fromString(localFile.fileName());
|
||||||
}
|
}
|
||||||
@@ -169,33 +159,8 @@ void SelectRemoteFileDialog::selectFile()
|
|||||||
idx = idx.sibling(idx.row(), 1);
|
idx = idx.sibling(idx.row(), 1);
|
||||||
m_remoteFile = FilePath::fromVariant(m_fileSystemModel.data(idx, DeviceFileSystemModel::PathRole));
|
m_remoteFile = FilePath::fromVariant(m_fileSystemModel.data(idx, DeviceFileSystemModel::PathRole));
|
||||||
|
|
||||||
clearWatcher();
|
m_fileTransfer.setFilesToTransfer({{m_remoteFile, m_localFile}});
|
||||||
m_watcher = new QFutureWatcher<bool>(this);
|
m_fileTransfer.start();
|
||||||
auto future = runAsync(copyFile, m_remoteFile, m_localFile);
|
|
||||||
connect(m_watcher, &QFutureWatcher<bool>::finished, this, [this] {
|
|
||||||
const bool success = m_watcher->result();
|
|
||||||
if (success) {
|
|
||||||
m_textBrowser->append(tr("Download of remote file succeeded."));
|
|
||||||
accept();
|
|
||||||
} else {
|
|
||||||
m_textBrowser->append(tr("Download of remote file failed."));
|
|
||||||
m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
|
|
||||||
m_fileSystemView->setEnabled(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
m_watcher->setFuture(future);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SelectRemoteFileDialog::clearWatcher()
|
|
||||||
{
|
|
||||||
if (!m_watcher)
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_watcher->disconnect();
|
|
||||||
m_watcher->future().cancel();
|
|
||||||
m_watcher->future().waitForFinished();
|
|
||||||
delete m_watcher;
|
|
||||||
m_watcher = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////
|
||||||
|
@@ -732,12 +732,9 @@ void PeripheralRegisterHandler::updateRegisterGroups()
|
|||||||
clear();
|
clear();
|
||||||
|
|
||||||
const DebuggerRunParameters &rp = m_engine->runParameters();
|
const DebuggerRunParameters &rp = m_engine->runParameters();
|
||||||
const FilePath peripheralDescriptionFile = FilePath::fromVariant(
|
if (!rp.peripheralDescriptionFile.exists())
|
||||||
rp.inferior.extraData.value(Debugger::Constants::kPeripheralDescriptionFile));
|
|
||||||
|
|
||||||
if (!peripheralDescriptionFile.exists())
|
|
||||||
return;
|
return;
|
||||||
m_peripheralRegisterGroups = availablePeripheralRegisterGroups(peripheralDescriptionFile);
|
m_peripheralRegisterGroups = availablePeripheralRegisterGroups(rp.peripheralDescriptionFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PeripheralRegisterHandler::updateRegister(quint64 address, quint64 value)
|
void PeripheralRegisterHandler::updateRegister(quint64 address, quint64 value)
|
||||||
|
@@ -536,10 +536,8 @@ void UvscEngine::updateAll()
|
|||||||
bool UvscEngine::configureProject(const DebuggerRunParameters &rp)
|
bool UvscEngine::configureProject(const DebuggerRunParameters &rp)
|
||||||
{
|
{
|
||||||
// Fetch patchs for the generated uVision project files.
|
// Fetch patchs for the generated uVision project files.
|
||||||
const FilePath optionsPath = FilePath::fromString(rp.inferior.extraData.value(
|
const FilePath optionsPath = rp.uVisionOptionsFilePath;
|
||||||
Constants::kUVisionOptionsFilePath).toString());
|
const FilePath projectPath = rp.uVisionProjectFilePath;
|
||||||
const FilePath projectPath = FilePath::fromString(rp.inferior.extraData.value(
|
|
||||||
Constants::kUVisionProjectFilePath).toString());
|
|
||||||
|
|
||||||
showMessage("UVSC: LOADING PROJECT...");
|
showMessage("UVSC: LOADING PROJECT...");
|
||||||
if (!optionsPath.exists()) {
|
if (!optionsPath.exists()) {
|
||||||
@@ -557,7 +555,7 @@ bool UvscEngine::configureProject(const DebuggerRunParameters &rp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
showMessage("UVSC: SETTING PROJECT DEBUG TARGET...");
|
showMessage("UVSC: SETTING PROJECT DEBUG TARGET...");
|
||||||
m_simulator = rp.inferior.extraData.value(Constants::kUVisionSimulator).toBool();
|
m_simulator = rp.uVisionSimulator;
|
||||||
if (!m_client->setProjectDebugTarget(m_simulator)) {
|
if (!m_client->setProjectDebugTarget(m_simulator)) {
|
||||||
handleSetupFailure(tr("Internal error: Unable to set the uVision debug target: %1.")
|
handleSetupFailure(tr("Internal error: Unable to set the uVision debug target: %1.")
|
||||||
.arg(m_client->errorString()));
|
.arg(m_client->errorString()));
|
||||||
@@ -606,8 +604,7 @@ void UvscEngine::handleProjectClosed()
|
|||||||
m_loadingRequired = false;
|
m_loadingRequired = false;
|
||||||
|
|
||||||
const DebuggerRunParameters &rp = runParameters();
|
const DebuggerRunParameters &rp = runParameters();
|
||||||
const FilePath projectPath = FilePath::fromString(rp.inferior.extraData.value(
|
const FilePath projectPath = rp.uVisionProjectFilePath;
|
||||||
Constants::kUVisionProjectFilePath).toString());
|
|
||||||
|
|
||||||
// This magic function removes specific files from the uVision
|
// This magic function removes specific files from the uVision
|
||||||
// project directory. Without of this we can't enumerate the local
|
// project directory. Without of this we can't enumerate the local
|
||||||
|
@@ -27,6 +27,7 @@
|
|||||||
|
|
||||||
#include <projectexplorer/devicesupport/idevice.h>
|
#include <projectexplorer/devicesupport/idevice.h>
|
||||||
#include <projectexplorer/devicesupport/idevicefactory.h>
|
#include <projectexplorer/devicesupport/idevicefactory.h>
|
||||||
|
#include <coreplugin/documentmanager.h>
|
||||||
|
|
||||||
#include <utils/aspects.h>
|
#include <utils/aspects.h>
|
||||||
|
|
||||||
@@ -44,7 +45,7 @@ public:
|
|||||||
QString tag;
|
QString tag;
|
||||||
QString size;
|
QString size;
|
||||||
bool useLocalUidGid = true;
|
bool useLocalUidGid = true;
|
||||||
QStringList mounts;
|
QStringList mounts = { Core::DocumentManager::projectsDirectory().toString() };
|
||||||
};
|
};
|
||||||
|
|
||||||
class DockerDevice : public ProjectExplorer::IDevice
|
class DockerDevice : public ProjectExplorer::IDevice
|
||||||
|
@@ -25,8 +25,6 @@
|
|||||||
|
|
||||||
#include "kitdetector.h"
|
#include "kitdetector.h"
|
||||||
|
|
||||||
#include "dockerconstants.h"
|
|
||||||
|
|
||||||
#include <cmakeprojectmanager/cmakeprojectconstants.h>
|
#include <cmakeprojectmanager/cmakeprojectconstants.h>
|
||||||
|
|
||||||
#include <extensionsystem/pluginmanager.h>
|
#include <extensionsystem/pluginmanager.h>
|
||||||
@@ -216,7 +214,7 @@ QtVersions KitDetectorPrivate::autoDetectQtVersions() const
|
|||||||
QString error;
|
QString error;
|
||||||
|
|
||||||
const auto handleQmake = [this, &qtVersions, &error](const FilePath &qmake) {
|
const auto handleQmake = [this, &qtVersions, &error](const FilePath &qmake) {
|
||||||
if (QtVersion *qtVersion = QtVersionFactory::createQtVersionFromQMakePath(qmake,
|
if (QtVersion *qtVersion = QtVersionFactory::createQtVersionFromQueryToolPath(qmake,
|
||||||
false,
|
false,
|
||||||
m_sharedId,
|
m_sharedId,
|
||||||
&error)) {
|
&error)) {
|
||||||
@@ -229,7 +227,7 @@ QtVersions KitDetectorPrivate::autoDetectQtVersions() const
|
|||||||
qtVersions.append(qtVersion);
|
qtVersions.append(qtVersion);
|
||||||
QtVersionManager::addVersion(qtVersion);
|
QtVersionManager::addVersion(qtVersion);
|
||||||
emit q->logOutput(
|
emit q->logOutput(
|
||||||
tr("Found \"%1\"").arg(qtVersion->qmakeFilePath().toUserOutput()));
|
tr("Found \"%1\"").arg(qtVersion->queryToolFilePath().toUserOutput()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -337,7 +335,7 @@ void KitDetectorPrivate::autoDetect()
|
|||||||
if (cmakeId.isValid())
|
if (cmakeId.isValid())
|
||||||
k->setValue(CMakeProjectManager::Constants::TOOL_ID, cmakeId.toSetting());
|
k->setValue(CMakeProjectManager::Constants::TOOL_ID, cmakeId.toSetting());
|
||||||
|
|
||||||
DeviceTypeKitAspect::setDeviceTypeId(k, Constants::DOCKER_DEVICE_TYPE);
|
DeviceTypeKitAspect::setDeviceTypeId(k, m_device->type());
|
||||||
DeviceKitAspect::setDevice(k, m_device);
|
DeviceKitAspect::setDevice(k, m_device);
|
||||||
BuildDeviceKitAspect::setDevice(k, m_device);
|
BuildDeviceKitAspect::setDevice(k, m_device);
|
||||||
|
|
||||||
|
@@ -3,6 +3,8 @@ add_qtc_plugin(GitLab
|
|||||||
PLUGIN_DEPENDS Core ProjectExplorer Git VcsBase
|
PLUGIN_DEPENDS Core ProjectExplorer Git VcsBase
|
||||||
DEPENDS Utils
|
DEPENDS Utils
|
||||||
SOURCES
|
SOURCES
|
||||||
|
gitlabclonedialog.cpp gitlabclonedialog.h
|
||||||
|
gitlabdialog.cpp gitlabdialog.h gitlabdialog.ui
|
||||||
gitlaboptionspage.cpp gitlaboptionspage.h
|
gitlaboptionspage.cpp gitlaboptionspage.h
|
||||||
gitlabparameters.cpp gitlabparameters.h
|
gitlabparameters.cpp gitlabparameters.h
|
||||||
gitlabplugin.cpp gitlabplugin.h
|
gitlabplugin.cpp gitlabplugin.h
|
||||||
|
@@ -10,6 +10,11 @@ QtcPlugin {
|
|||||||
Depends { name: "Utils" }
|
Depends { name: "Utils" }
|
||||||
|
|
||||||
files: [
|
files: [
|
||||||
|
"gitlabclonedialog.cpp",
|
||||||
|
"gitlabclonedialog.h",
|
||||||
|
"gitlabdialog.cpp",
|
||||||
|
"gitlabdialog.h",
|
||||||
|
"gitlabdialog.ui",
|
||||||
"gitlaboptionspage.cpp",
|
"gitlaboptionspage.cpp",
|
||||||
"gitlaboptionspage.h",
|
"gitlaboptionspage.h",
|
||||||
"gitlabparameters.cpp",
|
"gitlabparameters.cpp",
|
||||||
|
263
src/plugins/gitlab/gitlabclonedialog.cpp
Normal file
263
src/plugins/gitlab/gitlabclonedialog.cpp
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "gitlabclonedialog.h"
|
||||||
|
|
||||||
|
#include "gitlabprojectsettings.h"
|
||||||
|
#include "resultparser.h"
|
||||||
|
|
||||||
|
#include <coreplugin/documentmanager.h>
|
||||||
|
#include <coreplugin/shellcommand.h>
|
||||||
|
#include <coreplugin/vcsmanager.h>
|
||||||
|
#include <git/gitclient.h>
|
||||||
|
#include <projectexplorer/projectexplorer.h>
|
||||||
|
#include <projectexplorer/projectmanager.h>
|
||||||
|
#include <utils/algorithm.h>
|
||||||
|
#include <utils/commandline.h>
|
||||||
|
#include <utils/environment.h>
|
||||||
|
#include <utils/fancylineedit.h>
|
||||||
|
#include <utils/filepath.h>
|
||||||
|
#include <utils/infolabel.h>
|
||||||
|
#include <utils/mimeutils.h>
|
||||||
|
#include <utils/pathchooser.h>
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
#include <utils/qtcprocess.h>
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QCheckBox>
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QDialogButtonBox>
|
||||||
|
#include <QFormLayout>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QInputDialog>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QPlainTextEdit>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
|
namespace GitLab {
|
||||||
|
|
||||||
|
GitLabCloneDialog::GitLabCloneDialog(const Project &project, QWidget *parent)
|
||||||
|
: QDialog(parent)
|
||||||
|
{
|
||||||
|
setWindowTitle(tr("Clone Repository"));
|
||||||
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||||
|
layout->addWidget(new QLabel(tr("Specify repository URL, checkout path and directory.")));
|
||||||
|
QHBoxLayout *centerLayout = new QHBoxLayout;
|
||||||
|
QFormLayout *form = new QFormLayout;
|
||||||
|
m_repositoryCB = new QComboBox(this);
|
||||||
|
m_repositoryCB->addItems({project.sshUrl, project.httpUrl});
|
||||||
|
form->addRow(tr("Repository"), m_repositoryCB);
|
||||||
|
m_pathChooser = new Utils::PathChooser(this);
|
||||||
|
m_pathChooser->setExpectedKind(Utils::PathChooser::ExistingDirectory);
|
||||||
|
form->addRow(tr("Path"), m_pathChooser);
|
||||||
|
m_directoryLE = new Utils::FancyLineEdit(this);
|
||||||
|
m_directoryLE->setValidationFunction([this](Utils::FancyLineEdit *e, QString *msg) {
|
||||||
|
const Utils::FilePath fullPath = m_pathChooser->filePath().pathAppended(e->text());
|
||||||
|
bool alreadyExists = fullPath.exists();
|
||||||
|
if (alreadyExists && msg)
|
||||||
|
*msg = tr("Path \"%1\" already exists.").arg(fullPath.toUserOutput());
|
||||||
|
return !alreadyExists;
|
||||||
|
});
|
||||||
|
form->addRow(tr("Directory"), m_directoryLE);
|
||||||
|
m_submodulesCB = new QCheckBox(this);
|
||||||
|
form->addRow(tr("Recursive"), m_submodulesCB);
|
||||||
|
centerLayout->addLayout(form);
|
||||||
|
m_cloneOutput = new QPlainTextEdit(this);
|
||||||
|
m_cloneOutput->setReadOnly(true);
|
||||||
|
centerLayout->addWidget(m_cloneOutput);
|
||||||
|
layout->addLayout(centerLayout);
|
||||||
|
m_infoLabel = new Utils::InfoLabel(this);
|
||||||
|
layout->addWidget(m_infoLabel);
|
||||||
|
layout->addStretch(1);
|
||||||
|
auto buttons = new QDialogButtonBox(QDialogButtonBox::Cancel, this);
|
||||||
|
m_cloneButton = new QPushButton(tr("Clone"), this);
|
||||||
|
buttons->addButton(m_cloneButton, QDialogButtonBox::ActionRole);
|
||||||
|
m_cancelButton = buttons->button(QDialogButtonBox::Cancel);
|
||||||
|
layout->addWidget(buttons);
|
||||||
|
setLayout(layout);
|
||||||
|
|
||||||
|
m_pathChooser->setFilePath(Core::DocumentManager::projectsDirectory());
|
||||||
|
auto [host, path, port]
|
||||||
|
= GitLabProjectSettings::remotePartsFromRemote(m_repositoryCB->currentText());
|
||||||
|
int slashIndex = path.indexOf('/');
|
||||||
|
QTC_ASSERT(slashIndex > 0, return);
|
||||||
|
m_directoryLE->setText(path.mid(slashIndex + 1));
|
||||||
|
|
||||||
|
connect(m_pathChooser, &Utils::PathChooser::pathChanged, this, [this]() {
|
||||||
|
m_directoryLE->validate();
|
||||||
|
GitLabCloneDialog::updateUi();
|
||||||
|
});
|
||||||
|
connect(m_directoryLE, &Utils::FancyLineEdit::textChanged, this, &GitLabCloneDialog::updateUi);
|
||||||
|
connect(m_cloneButton, &QPushButton::clicked, this, &GitLabCloneDialog::cloneProject);
|
||||||
|
connect(m_cancelButton, &QPushButton::clicked,
|
||||||
|
this, &GitLabCloneDialog::cancel);
|
||||||
|
connect(this, &QDialog::rejected, this, [this]() {
|
||||||
|
if (m_commandRunning) {
|
||||||
|
cancel();
|
||||||
|
QApplication::restoreOverrideCursor();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
updateUi();
|
||||||
|
resize(575, 265);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitLabCloneDialog::updateUi()
|
||||||
|
{
|
||||||
|
bool pathValid = m_pathChooser->isValid();
|
||||||
|
bool directoryValid = m_directoryLE->isValid();
|
||||||
|
m_cloneButton->setEnabled(pathValid && directoryValid);
|
||||||
|
if (!pathValid) {
|
||||||
|
m_infoLabel->setText(m_pathChooser->errorMessage());
|
||||||
|
m_infoLabel->setType(Utils::InfoLabel::Error);
|
||||||
|
} else if (!directoryValid) {
|
||||||
|
m_infoLabel->setText(m_directoryLE->errorMessage());
|
||||||
|
m_infoLabel->setType(Utils::InfoLabel::Error);
|
||||||
|
}
|
||||||
|
m_infoLabel->setVisible(!pathValid || !directoryValid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitLabCloneDialog::cloneProject()
|
||||||
|
{
|
||||||
|
Core::IVersionControl *vc = Core::VcsManager::versionControl(Utils::Id::fromString("G.Git"));
|
||||||
|
QTC_ASSERT(vc, return);
|
||||||
|
const QStringList extraArgs = m_submodulesCB->isChecked() ? QStringList{ "--recursive" }
|
||||||
|
: QStringList{};
|
||||||
|
m_command = vc->createInitialCheckoutCommand(m_repositoryCB->currentText(),
|
||||||
|
m_pathChooser->absoluteFilePath(),
|
||||||
|
m_directoryLE->text(), extraArgs);
|
||||||
|
const Utils::FilePath workingDirectory = m_pathChooser->absoluteFilePath();
|
||||||
|
m_command->setProgressiveOutput(true);
|
||||||
|
connect(m_command, &Utils::ShellCommand::stdOutText, this, [this](const QString &text) {
|
||||||
|
m_cloneOutput->appendPlainText(text);
|
||||||
|
});
|
||||||
|
connect(m_command, &Utils::ShellCommand::stdErrText, this, [this](const QString &text) {
|
||||||
|
m_cloneOutput->appendPlainText(text);
|
||||||
|
});
|
||||||
|
connect(m_command, &Utils::ShellCommand::finished, this, &GitLabCloneDialog::cloneFinished);
|
||||||
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||||
|
|
||||||
|
m_cloneOutput->clear();
|
||||||
|
m_cloneButton->setEnabled(false);
|
||||||
|
m_pathChooser->setReadOnly(true);
|
||||||
|
m_directoryLE->setReadOnly(true);
|
||||||
|
m_commandRunning = true;
|
||||||
|
m_command->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitLabCloneDialog::cancel()
|
||||||
|
{
|
||||||
|
if (m_commandRunning) {
|
||||||
|
m_cloneOutput->appendPlainText(tr("User canceled process."));
|
||||||
|
m_cancelButton->setEnabled(false);
|
||||||
|
m_command->cancel(); // FIXME does not cancel the git processes... QTCREATORBUG-27567
|
||||||
|
} else {
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Utils::FilePaths scanDirectoryForFiles(const Utils::FilePath &directory)
|
||||||
|
{
|
||||||
|
Utils::FilePaths result;
|
||||||
|
const Utils::FilePaths entries = directory.dirEntries(QDir::AllEntries | QDir::NoDotAndDotDot);
|
||||||
|
|
||||||
|
for (const Utils::FilePath &entry : entries) {
|
||||||
|
if (entry.isDir())
|
||||||
|
result.append(scanDirectoryForFiles(entry));
|
||||||
|
else
|
||||||
|
result.append(entry);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitLabCloneDialog::cloneFinished(bool ok, int exitCode)
|
||||||
|
{
|
||||||
|
const bool success = (ok && exitCode == 0);
|
||||||
|
m_commandRunning = false;
|
||||||
|
delete m_command;
|
||||||
|
m_command = nullptr;
|
||||||
|
|
||||||
|
const QString emptyLine("\n\n");
|
||||||
|
m_cloneOutput->appendPlainText(emptyLine);
|
||||||
|
QApplication::restoreOverrideCursor();
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
m_cloneOutput->appendPlainText(tr("Cloning succeeded.") + emptyLine);
|
||||||
|
m_cloneButton->setEnabled(false);
|
||||||
|
|
||||||
|
const Utils::FilePath base = m_pathChooser->filePath().pathAppended(m_directoryLE->text());
|
||||||
|
Utils::FilePaths filesWeMayOpen
|
||||||
|
= Utils::filtered(scanDirectoryForFiles(base), [](const Utils::FilePath &f) {
|
||||||
|
return ProjectExplorer::ProjectManager::canOpenProjectForMimeType(
|
||||||
|
Utils::mimeTypeForFile(f));
|
||||||
|
});
|
||||||
|
|
||||||
|
// limit the files to the most top-level item(s)
|
||||||
|
int minimum = std::numeric_limits<int>::max();
|
||||||
|
for (const Utils::FilePath &f : filesWeMayOpen) {
|
||||||
|
int parentCount = f.toString().count('/');
|
||||||
|
if (parentCount < minimum)
|
||||||
|
minimum = parentCount;
|
||||||
|
}
|
||||||
|
filesWeMayOpen = Utils::filtered(filesWeMayOpen, [minimum](const Utils::FilePath &f) {
|
||||||
|
return f.toString().count('/') == minimum;
|
||||||
|
});
|
||||||
|
|
||||||
|
hide(); // avoid to many dialogs.. FIXME: maybe change to some wizard approach?
|
||||||
|
if (filesWeMayOpen.isEmpty()) {
|
||||||
|
QMessageBox::warning(this, tr("Warning"),
|
||||||
|
tr("Cloned project does not have a project file that can be "
|
||||||
|
"opened. Try importing the project as a generic project."));
|
||||||
|
accept();
|
||||||
|
} else {
|
||||||
|
const QStringList pFiles = Utils::transform(filesWeMayOpen,
|
||||||
|
[base](const Utils::FilePath &f) {
|
||||||
|
return f.relativePath(base).toUserOutput();
|
||||||
|
});
|
||||||
|
bool ok = false;
|
||||||
|
const QString fileToOpen
|
||||||
|
= QInputDialog::getItem(this, tr("Open Project"),
|
||||||
|
tr("Choose the project file to be opened."),
|
||||||
|
pFiles, 0, false, &ok);
|
||||||
|
accept();
|
||||||
|
if (ok && !fileToOpen.isEmpty())
|
||||||
|
ProjectExplorer::ProjectExplorerPlugin::openProject(base.pathAppended(fileToOpen));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_cloneOutput->appendPlainText(tr("Cloning failed.") + emptyLine);
|
||||||
|
const Utils::FilePath fullPath = m_pathChooser->filePath()
|
||||||
|
.pathAppended(m_directoryLE->text());
|
||||||
|
fullPath.removeRecursively();
|
||||||
|
m_cloneButton->setEnabled(true);
|
||||||
|
m_cancelButton->setEnabled(true);
|
||||||
|
m_pathChooser->setReadOnly(false);
|
||||||
|
m_directoryLE->setReadOnly(false);
|
||||||
|
m_directoryLE->validate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace GitLab
|
74
src/plugins/gitlab/gitlabclonedialog.h
Normal file
74
src/plugins/gitlab/gitlabclonedialog.h
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QCheckBox;
|
||||||
|
class QComboBox;
|
||||||
|
class QPlainTextEdit;
|
||||||
|
class QPushButton;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
namespace Core { class ShellCommand; }
|
||||||
|
|
||||||
|
namespace Utils {
|
||||||
|
class FancyLineEdit;
|
||||||
|
class InfoLabel;
|
||||||
|
class PathChooser;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace GitLab {
|
||||||
|
|
||||||
|
class Project;
|
||||||
|
|
||||||
|
class GitLabCloneDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_DECLARE_TR_FUNCTIONS(GitLab::GitLabCloneDialog)
|
||||||
|
public:
|
||||||
|
explicit GitLabCloneDialog(const Project &project, QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateUi();
|
||||||
|
void cloneProject();
|
||||||
|
void cancel();
|
||||||
|
void cloneFinished(bool ok, int exitCode);
|
||||||
|
|
||||||
|
QComboBox * m_repositoryCB = nullptr;
|
||||||
|
QCheckBox *m_submodulesCB = nullptr;
|
||||||
|
QPushButton *m_cloneButton = nullptr;
|
||||||
|
QPushButton *m_cancelButton = nullptr;
|
||||||
|
QPlainTextEdit *m_cloneOutput = nullptr;
|
||||||
|
Utils::PathChooser *m_pathChooser = nullptr;
|
||||||
|
Utils::FancyLineEdit *m_directoryLE = nullptr;
|
||||||
|
Utils::InfoLabel *m_infoLabel = nullptr;
|
||||||
|
Core::ShellCommand *m_command = nullptr;
|
||||||
|
bool m_commandRunning = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace GitLab
|
294
src/plugins/gitlab/gitlabdialog.cpp
Normal file
294
src/plugins/gitlab/gitlabdialog.cpp
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "gitlabdialog.h"
|
||||||
|
|
||||||
|
#include "gitlabclonedialog.h"
|
||||||
|
#include "gitlabparameters.h"
|
||||||
|
#include "gitlabplugin.h"
|
||||||
|
#include "gitlabprojectsettings.h"
|
||||||
|
|
||||||
|
#include <projectexplorer/session.h>
|
||||||
|
#include <texteditor/fontsettings.h>
|
||||||
|
#include <texteditor/texteditorsettings.h>
|
||||||
|
#include <utils/listmodel.h>
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
#include <utils/utilsicons.h>
|
||||||
|
|
||||||
|
#include <QKeyEvent>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QSyntaxHighlighter>
|
||||||
|
|
||||||
|
namespace GitLab {
|
||||||
|
|
||||||
|
GitLabDialog::GitLabDialog(QWidget *parent)
|
||||||
|
: QDialog(parent)
|
||||||
|
, m_lastTreeViewQuery(Query::NoQuery)
|
||||||
|
{
|
||||||
|
m_ui.setupUi(this);
|
||||||
|
m_clonePB = new QPushButton(Utils::Icons::DOWNLOAD.icon(), tr("Clone..."), this);
|
||||||
|
m_ui.buttonBox->addButton(m_clonePB, QDialogButtonBox::ActionRole);
|
||||||
|
m_clonePB->setEnabled(false);
|
||||||
|
|
||||||
|
updateRemotes();
|
||||||
|
|
||||||
|
connect(m_ui.remoteCB, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||||
|
this, &GitLabDialog::requestMainViewUpdate);
|
||||||
|
connect(m_ui.searchLE, &QLineEdit::returnPressed, this, &GitLabDialog::querySearch);
|
||||||
|
connect(m_ui.searchPB, &QPushButton::clicked, this, &GitLabDialog::querySearch);
|
||||||
|
connect(m_clonePB, &QPushButton::clicked, this, &GitLabDialog::cloneSelected);
|
||||||
|
connect(m_ui.firstTB, &QToolButton::clicked, this, &GitLabDialog::queryFirstPage);
|
||||||
|
connect(m_ui.previousTB, &QToolButton::clicked, this, &GitLabDialog::queryPreviousPage);
|
||||||
|
connect(m_ui.nextTB, &QToolButton::clicked, this, &GitLabDialog::queryNextPage);
|
||||||
|
connect(m_ui.lastTB, &QToolButton::clicked, this, &GitLabDialog::queryLastPage);
|
||||||
|
requestMainViewUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitLabDialog::resetTreeView(QTreeView *treeView, QAbstractItemModel *model)
|
||||||
|
{
|
||||||
|
auto oldModel = treeView->model();
|
||||||
|
treeView->setModel(model);
|
||||||
|
delete oldModel;
|
||||||
|
if (model) {
|
||||||
|
connect(treeView->selectionModel(), &QItemSelectionModel::selectionChanged,
|
||||||
|
this, [this](const QItemSelection &selected, const QItemSelection &) {
|
||||||
|
m_clonePB->setEnabled(!selected.isEmpty());
|
||||||
|
});
|
||||||
|
m_clonePB->setEnabled(!treeView->selectionModel()->selectedIndexes().isEmpty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitLabDialog::updateRemotes()
|
||||||
|
{
|
||||||
|
m_ui.remoteCB->clear();
|
||||||
|
const GitLabParameters *global = GitLabPlugin::globalParameters();
|
||||||
|
for (const GitLabServer &server : qAsConst(global->gitLabServers))
|
||||||
|
m_ui.remoteCB->addItem(server.displayString(), QVariant::fromValue(server));
|
||||||
|
|
||||||
|
m_ui.remoteCB->setCurrentIndex(m_ui.remoteCB->findData(
|
||||||
|
QVariant::fromValue(global->currentDefaultServer())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitLabDialog::keyPressEvent(QKeyEvent *event)
|
||||||
|
{
|
||||||
|
if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return)
|
||||||
|
return;
|
||||||
|
QDialog::keyPressEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitLabDialog::requestMainViewUpdate()
|
||||||
|
{
|
||||||
|
m_lastPageInformation = PageInformation();
|
||||||
|
m_lastTreeViewQuery = Query(Query::NoQuery);
|
||||||
|
|
||||||
|
m_ui.mainLabel->setText({});
|
||||||
|
m_ui.detailsLabel->setText({});
|
||||||
|
m_ui.treeViewTitle->setText({});
|
||||||
|
m_ui.searchLE->setText({});
|
||||||
|
resetTreeView(m_ui.treeView, nullptr);
|
||||||
|
updatePageButtons();
|
||||||
|
|
||||||
|
bool linked = false;
|
||||||
|
m_currentServerId = Utils::Id();
|
||||||
|
if (auto project = ProjectExplorer::SessionManager::startupProject()) {
|
||||||
|
GitLabProjectSettings *projSettings = GitLabPlugin::projectSettings(project);
|
||||||
|
if (projSettings->isLinked()) {
|
||||||
|
m_currentServerId = projSettings->currentServer();
|
||||||
|
linked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!m_currentServerId.isValid())
|
||||||
|
m_currentServerId = m_ui.remoteCB->currentData().value<GitLabServer>().id;
|
||||||
|
if (m_currentServerId.isValid()) {
|
||||||
|
const GitLabParameters *global = GitLabPlugin::globalParameters();
|
||||||
|
const GitLabServer server = global->serverForId(m_currentServerId);
|
||||||
|
m_ui.remoteCB->setCurrentIndex(m_ui.remoteCB->findData(QVariant::fromValue(server)));
|
||||||
|
}
|
||||||
|
m_ui.remoteCB->setEnabled(!linked);
|
||||||
|
|
||||||
|
const Query query(Query::User);
|
||||||
|
QueryRunner *runner = new QueryRunner(query, m_currentServerId, this);
|
||||||
|
connect(runner, &QueryRunner::resultRetrieved, this, [this](const QByteArray &result) {
|
||||||
|
handleUser(ResultParser::parseUser(result));
|
||||||
|
});
|
||||||
|
connect(runner, &QueryRunner::finished, [runner]() { runner->deleteLater(); });
|
||||||
|
runner->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitLabDialog::updatePageButtons()
|
||||||
|
{
|
||||||
|
if (m_lastPageInformation.currentPage == -1) {
|
||||||
|
m_ui.currentPage->setVisible(false);
|
||||||
|
m_ui.firstTB->setVisible(false);
|
||||||
|
m_ui.lastTB->setVisible(false);
|
||||||
|
m_ui.previousTB->setVisible(false);
|
||||||
|
m_ui.nextTB->setVisible(false);
|
||||||
|
} else {
|
||||||
|
m_ui.currentPage->setText(QString::number(m_lastPageInformation.currentPage));
|
||||||
|
m_ui.currentPage->setVisible(true);
|
||||||
|
m_ui.firstTB->setVisible(true);
|
||||||
|
m_ui.lastTB->setVisible(true);
|
||||||
|
}
|
||||||
|
if (m_lastPageInformation.currentPage > 1) {
|
||||||
|
m_ui.firstTB->setEnabled(true);
|
||||||
|
m_ui.previousTB->setText(QString::number(m_lastPageInformation.currentPage - 1));
|
||||||
|
m_ui.previousTB->setVisible(true);
|
||||||
|
} else {
|
||||||
|
m_ui.firstTB->setEnabled(false);
|
||||||
|
m_ui.previousTB->setVisible(false);
|
||||||
|
}
|
||||||
|
if (m_lastPageInformation.currentPage < m_lastPageInformation.totalPages) {
|
||||||
|
m_ui.lastTB->setEnabled(true);
|
||||||
|
m_ui.nextTB->setText(QString::number(m_lastPageInformation.currentPage + 1));
|
||||||
|
m_ui.nextTB->setVisible(true);
|
||||||
|
} else {
|
||||||
|
m_ui.lastTB->setEnabled(false);
|
||||||
|
m_ui.nextTB->setVisible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitLabDialog::queryFirstPage()
|
||||||
|
{
|
||||||
|
QTC_ASSERT(m_lastTreeViewQuery.type() != Query::NoQuery, return);
|
||||||
|
QTC_ASSERT(m_lastPageInformation.currentPage != -1, return);
|
||||||
|
m_lastTreeViewQuery.setPageParameter(1);
|
||||||
|
fetchProjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitLabDialog::queryPreviousPage()
|
||||||
|
{
|
||||||
|
QTC_ASSERT(m_lastTreeViewQuery.type() != Query::NoQuery, return);
|
||||||
|
QTC_ASSERT(m_lastPageInformation.currentPage != -1, return);
|
||||||
|
m_lastTreeViewQuery.setPageParameter(m_lastPageInformation.currentPage - 1);
|
||||||
|
fetchProjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitLabDialog::queryNextPage()
|
||||||
|
{
|
||||||
|
QTC_ASSERT(m_lastTreeViewQuery.type() != Query::NoQuery, return);
|
||||||
|
QTC_ASSERT(m_lastPageInformation.currentPage != -1, return);
|
||||||
|
m_lastTreeViewQuery.setPageParameter(m_lastPageInformation.currentPage + 1);
|
||||||
|
fetchProjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitLabDialog::queryLastPage()
|
||||||
|
{
|
||||||
|
QTC_ASSERT(m_lastTreeViewQuery.type() != Query::NoQuery, return);
|
||||||
|
QTC_ASSERT(m_lastPageInformation.currentPage != -1, return);
|
||||||
|
m_lastTreeViewQuery.setPageParameter(m_lastPageInformation.totalPages);
|
||||||
|
fetchProjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitLabDialog::querySearch()
|
||||||
|
{
|
||||||
|
QTC_ASSERT(m_lastTreeViewQuery.type() != Query::NoQuery, return);
|
||||||
|
m_lastTreeViewQuery.setPageParameter(-1);
|
||||||
|
m_lastTreeViewQuery.setAdditionalParameters({"search=" + m_ui.searchLE->text()});
|
||||||
|
fetchProjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitLabDialog::handleUser(const User &user)
|
||||||
|
{
|
||||||
|
m_lastPageInformation = {};
|
||||||
|
m_currentUserId = user.id;
|
||||||
|
|
||||||
|
if (!user.error.message.isEmpty()) {
|
||||||
|
// TODO
|
||||||
|
if (user.error.code == 1) {
|
||||||
|
m_ui.mainLabel->setText(tr("Not logged in."));
|
||||||
|
m_ui.detailsLabel->setText(tr("Insufficient access token."));
|
||||||
|
m_ui.detailsLabel->setToolTip(user.error.message + QLatin1Char('\n')
|
||||||
|
+ tr("Permission scope read_api or api needed."));
|
||||||
|
updatePageButtons();
|
||||||
|
m_ui.treeViewTitle->setText(tr("Projects (%1)").arg(0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.id != -1) {
|
||||||
|
if (user.bot) {
|
||||||
|
m_ui.mainLabel->setText(tr("Using project access token."));
|
||||||
|
m_ui.detailsLabel->setText({});
|
||||||
|
} else {
|
||||||
|
m_ui.mainLabel->setText(tr("Logged in as %1").arg(user.name));
|
||||||
|
m_ui.detailsLabel->setText(tr("Id: %1 (%2)").arg(user.id).arg(user.email));
|
||||||
|
}
|
||||||
|
m_ui.detailsLabel->setToolTip({});
|
||||||
|
} else {
|
||||||
|
m_ui.mainLabel->setText(tr("Not logged in."));
|
||||||
|
m_ui.detailsLabel->setText({});
|
||||||
|
m_ui.detailsLabel->setToolTip({});
|
||||||
|
}
|
||||||
|
m_lastTreeViewQuery = Query(Query::Projects);
|
||||||
|
fetchProjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitLabDialog::handleProjects(const Projects &projects)
|
||||||
|
{
|
||||||
|
Utils::ListModel<Project *> *listModel = new Utils::ListModel<Project *>(this);
|
||||||
|
for (const Project &project : projects.projects)
|
||||||
|
listModel->appendItem(new Project(project));
|
||||||
|
|
||||||
|
// TODO use a real model / delegate..?
|
||||||
|
listModel->setDataAccessor([](Project *data, int /*column*/, int role) -> QVariant {
|
||||||
|
if (role == Qt::DisplayRole)
|
||||||
|
return QString(data->displayName + " (" + data->visibility + ')');
|
||||||
|
if (role == Qt::UserRole)
|
||||||
|
return QVariant::fromValue(*data);
|
||||||
|
return QVariant();
|
||||||
|
});
|
||||||
|
resetTreeView(m_ui.treeView, listModel);
|
||||||
|
int count = projects.error.message.isEmpty() ? projects.pageInfo.total : 0;
|
||||||
|
m_ui.treeViewTitle->setText(tr("Projects (%1)").arg(count));
|
||||||
|
|
||||||
|
m_lastPageInformation = projects.pageInfo;
|
||||||
|
updatePageButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitLabDialog::fetchProjects()
|
||||||
|
{
|
||||||
|
QueryRunner *runner = new QueryRunner(m_lastTreeViewQuery, m_currentServerId, this);
|
||||||
|
connect(runner, &QueryRunner::resultRetrieved, this, [this](const QByteArray &result) {
|
||||||
|
handleProjects(ResultParser::parseProjects(result));
|
||||||
|
});
|
||||||
|
connect(runner, &QueryRunner::finished, [runner]() { runner->deleteLater(); });
|
||||||
|
runner->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitLabDialog::cloneSelected()
|
||||||
|
{
|
||||||
|
const QModelIndexList indexes = m_ui.treeView->selectionModel()->selectedIndexes();
|
||||||
|
QTC_ASSERT(indexes.size() == 1, return);
|
||||||
|
const Project project = indexes.first().data(Qt::UserRole).value<Project>();
|
||||||
|
QTC_ASSERT(!project.sshUrl.isEmpty() && !project.httpUrl.isEmpty(), return);
|
||||||
|
GitLabCloneDialog dialog(project, this);
|
||||||
|
if (dialog.exec() == QDialog::Accepted)
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace GitLab
|
@@ -1,6 +1,6 @@
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
**
|
**
|
||||||
** Copyright (C) 2016 The Qt Company Ltd.
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
** Contact: https://www.qt.io/licensing/
|
** Contact: https://www.qt.io/licensing/
|
||||||
**
|
**
|
||||||
** This file is part of Qt Creator.
|
** This file is part of Qt Creator.
|
||||||
@@ -22,49 +22,60 @@
|
|||||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
**
|
**
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "abstractremotelinuxdeployservice.h"
|
#include "ui_gitlabdialog.h"
|
||||||
#include "remotelinux_export.h"
|
|
||||||
|
|
||||||
namespace Utils {
|
#include "queryrunner.h"
|
||||||
class FilePath;
|
#include "resultparser.h"
|
||||||
class ProcessResultData;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace RemoteLinux {
|
#include <utils/filepath.h>
|
||||||
class AbstractRemoteLinuxPackageInstaller;
|
#include <utils/id.h>
|
||||||
|
|
||||||
namespace Internal { class AbstractUploadAndInstallPackageServicePrivate; }
|
#include <QDialog>
|
||||||
|
|
||||||
class REMOTELINUX_EXPORT AbstractUploadAndInstallPackageService : public AbstractRemoteLinuxDeployService
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QPushButton;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
namespace GitLab {
|
||||||
|
|
||||||
|
class GitLabParameters;
|
||||||
|
class GitLabDialog : public QDialog
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void setPackageFilePath(const Utils::FilePath &filePath);
|
explicit GitLabDialog(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
void updateRemotes();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
AbstractUploadAndInstallPackageService();
|
void keyPressEvent(QKeyEvent *event) override;
|
||||||
~AbstractUploadAndInstallPackageService() override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void handleUploadFinished(const Utils::ProcessResultData &resultData);
|
void resetTreeView(QTreeView *treeView, QAbstractItemModel *model);
|
||||||
void handleInstallationFinished(const QString &errorMsg);
|
void requestMainViewUpdate();
|
||||||
|
void updatePageButtons();
|
||||||
|
|
||||||
virtual AbstractRemoteLinuxPackageInstaller *packageInstaller() const = 0;
|
void queryFirstPage();
|
||||||
virtual QString uploadDir() const; // Defaults to remote user's home directory.
|
void queryPreviousPage();
|
||||||
|
void queryNextPage();
|
||||||
|
void queryLastPage();
|
||||||
|
void querySearch();
|
||||||
|
void continuePageUpdate();
|
||||||
|
|
||||||
bool isDeploymentNecessary() const override;
|
void handleUser(const User &user);
|
||||||
void doDeviceSetup() override;
|
void handleProjects(const Projects &projects);
|
||||||
void stopDeviceSetup() override;
|
void fetchProjects();
|
||||||
void doDeploy() override;
|
|
||||||
void stopDeployment() override;
|
|
||||||
|
|
||||||
void setFinished();
|
void cloneSelected();
|
||||||
|
|
||||||
Internal::AbstractUploadAndInstallPackageServicePrivate * const d;
|
Ui::GitLabDialog m_ui;
|
||||||
|
QPushButton *m_clonePB = nullptr;
|
||||||
|
Utils::Id m_currentServerId;
|
||||||
|
Query m_lastTreeViewQuery;
|
||||||
|
PageInformation m_lastPageInformation;
|
||||||
|
int m_currentUserId = -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace RemoteLinux
|
} // namespace GitLab
|
268
src/plugins/gitlab/gitlabdialog.ui
Normal file
268
src/plugins/gitlab/gitlabdialog.ui
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>GitLab::GitLabDialog</class>
|
||||||
|
<widget class="QDialog" name="GitLab::GitLabDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>665</width>
|
||||||
|
<height>530</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>GitLab</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_0">
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_1">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_0">
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="mainLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Login</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="detailsLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Details</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_0">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Remote:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="remoteCB">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>200</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Fixed</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="treeViewTitle">
|
||||||
|
<property name="text">
|
||||||
|
<string>Projects</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_1">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="searchLE">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="placeholderText">
|
||||||
|
<string>Search</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="searchPB">
|
||||||
|
<property name="text">
|
||||||
|
<string>Search</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||||
|
<item>
|
||||||
|
<widget class="QTreeView" name="treeView">
|
||||||
|
<property name="rootIsDecorated">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="uniformRowHeights">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="itemsExpandable">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="expandsOnDoubleClick">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<attribute name="headerVisible">
|
||||||
|
<bool>false</bool>
|
||||||
|
</attribute>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>200</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="firstTB">
|
||||||
|
<property name="text">
|
||||||
|
<string notr="true">|<</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="previousTB">
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="currentPage">
|
||||||
|
<property name="text">
|
||||||
|
<string>0</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="nextTB">
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="lastTB">
|
||||||
|
<property name="text">
|
||||||
|
<string notr="true">>|</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_3">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>200</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Close</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>GitLab::GitLabDialog</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>316</x>
|
||||||
|
<y>260</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
@@ -25,23 +25,52 @@
|
|||||||
|
|
||||||
#include "gitlabplugin.h"
|
#include "gitlabplugin.h"
|
||||||
|
|
||||||
|
#include "gitlabdialog.h"
|
||||||
#include "gitlaboptionspage.h"
|
#include "gitlaboptionspage.h"
|
||||||
#include "gitlabparameters.h"
|
#include "gitlabparameters.h"
|
||||||
#include "gitlabprojectsettings.h"
|
#include "gitlabprojectsettings.h"
|
||||||
|
#include "queryrunner.h"
|
||||||
|
#include "resultparser.h"
|
||||||
|
|
||||||
|
#include <coreplugin/actionmanager/actioncontainer.h>
|
||||||
|
#include <coreplugin/actionmanager/actionmanager.h>
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
|
#include <git/gitplugin.h>
|
||||||
#include <projectexplorer/project.h>
|
#include <projectexplorer/project.h>
|
||||||
#include <projectexplorer/projectpanelfactory.h>
|
#include <projectexplorer/projectpanelfactory.h>
|
||||||
|
#include <projectexplorer/session.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
|
#include <vcsbase/vcsoutputwindow.h>
|
||||||
|
|
||||||
|
#include <QAction>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QPointer>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
namespace GitLab {
|
namespace GitLab {
|
||||||
|
namespace Constants {
|
||||||
|
const char GITLAB_OPEN_VIEW[] = "GitLab.OpenView";
|
||||||
|
} // namespace Constants
|
||||||
|
|
||||||
class GitLabPluginPrivate
|
class GitLabPluginPrivate : public QObject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GitLabParameters parameters;
|
GitLabParameters parameters;
|
||||||
GitLabOptionsPage optionsPage{¶meters};
|
GitLabOptionsPage optionsPage{¶meters};
|
||||||
QHash<ProjectExplorer::Project *, GitLabProjectSettings *> projectSettings;
|
QHash<ProjectExplorer::Project *, GitLabProjectSettings *> projectSettings;
|
||||||
|
QPointer<GitLabDialog> dialog;
|
||||||
|
|
||||||
|
QTimer notificationTimer;
|
||||||
|
QString projectName;
|
||||||
|
Utils::Id serverId;
|
||||||
|
bool runningQuery = false;
|
||||||
|
|
||||||
|
void setupNotificationTimer();
|
||||||
|
void fetchEvents();
|
||||||
|
void fetchUser();
|
||||||
|
void createAndSendEventsRequest(const QDateTime timeStamp, int page = -1);
|
||||||
|
void handleUser(const User &user);
|
||||||
|
void handleEvents(const Events &events, const QDateTime &timeStamp);
|
||||||
};
|
};
|
||||||
|
|
||||||
static GitLabPluginPrivate *dd = nullptr;
|
static GitLabPluginPrivate *dd = nullptr;
|
||||||
@@ -71,9 +100,180 @@ bool GitLabPlugin::initialize(const QStringList & /*arguments*/, QString * /*err
|
|||||||
return new GitLabProjectSettingsWidget(project);
|
return new GitLabProjectSettingsWidget(project);
|
||||||
});
|
});
|
||||||
ProjectExplorer::ProjectPanelFactory::registerFactory(panelFactory);
|
ProjectExplorer::ProjectPanelFactory::registerFactory(panelFactory);
|
||||||
|
QAction *openViewAction = new QAction(tr("GitLab..."), this);
|
||||||
|
auto gitlabCommand = Core::ActionManager::registerAction(openViewAction,
|
||||||
|
Constants::GITLAB_OPEN_VIEW);
|
||||||
|
connect(openViewAction, &QAction::triggered, this, &GitLabPlugin::openView);
|
||||||
|
Core::ActionContainer *ac = Core::ActionManager::actionContainer(Core::Constants::M_TOOLS);
|
||||||
|
ac->addAction(gitlabCommand);
|
||||||
|
connect(&dd->optionsPage, &GitLabOptionsPage::settingsChanged, this, [this] {
|
||||||
|
if (dd->dialog)
|
||||||
|
dd->dialog->updateRemotes();
|
||||||
|
});
|
||||||
|
connect(ProjectExplorer::SessionManager::instance(),
|
||||||
|
&ProjectExplorer::SessionManager::startupProjectChanged,
|
||||||
|
this, &GitLabPlugin::onStartupProjectChanged);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GitLabPlugin::openView()
|
||||||
|
{
|
||||||
|
if (dd->dialog.isNull()) {
|
||||||
|
while (!dd->parameters.isValid()) {
|
||||||
|
QMessageBox::warning(Core::ICore::dialogParent(), tr("Error"),
|
||||||
|
tr("Invalid GitLab configuration. For a fully functional "
|
||||||
|
"configuration, you need to set up host name or address and "
|
||||||
|
"an access token. Providing the path to curl is mandatory."));
|
||||||
|
if (!Core::ICore::showOptionsDialog("GitLab"))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GitLabDialog *gitlabD = new GitLabDialog(Core::ICore::dialogParent());
|
||||||
|
gitlabD->setModal(true);
|
||||||
|
Core::ICore::registerWindow(gitlabD, Core::Context("Git.GitLab"));
|
||||||
|
dd->dialog = gitlabD;
|
||||||
|
}
|
||||||
|
const Qt::WindowStates state = dd->dialog->windowState();
|
||||||
|
if (state & Qt::WindowMinimized)
|
||||||
|
dd->dialog->setWindowState(state & ~Qt::WindowMinimized);
|
||||||
|
dd->dialog->show();
|
||||||
|
dd->dialog->raise();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitLabPlugin::onStartupProjectChanged()
|
||||||
|
{
|
||||||
|
QTC_ASSERT(dd, return);
|
||||||
|
disconnect(&dd->notificationTimer);
|
||||||
|
ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
|
||||||
|
if (!project) {
|
||||||
|
dd->notificationTimer.stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GitLabProjectSettings *projSettings = projectSettings(project);
|
||||||
|
if (!projSettings->isLinked()) {
|
||||||
|
dd->notificationTimer.stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd->fetchEvents();
|
||||||
|
dd->setupNotificationTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitLabPluginPrivate::setupNotificationTimer()
|
||||||
|
{
|
||||||
|
// make interval configurable?
|
||||||
|
notificationTimer.setInterval(15 * 60 * 1000);
|
||||||
|
QObject::connect(¬ificationTimer, &QTimer::timeout, this, &GitLabPluginPrivate::fetchEvents);
|
||||||
|
notificationTimer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitLabPluginPrivate::fetchEvents()
|
||||||
|
{
|
||||||
|
ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
|
||||||
|
QTC_ASSERT(project, return);
|
||||||
|
|
||||||
|
if (runningQuery)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const GitLabProjectSettings *projSettings = GitLabPlugin::projectSettings(project);
|
||||||
|
projectName = projSettings->currentProject();
|
||||||
|
serverId = projSettings->currentServer();
|
||||||
|
|
||||||
|
const QDateTime lastRequest = projSettings->lastRequest();
|
||||||
|
if (!lastRequest.isValid()) { // we haven't queried events for this project yet
|
||||||
|
fetchUser();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
createAndSendEventsRequest(lastRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitLabPluginPrivate::fetchUser()
|
||||||
|
{
|
||||||
|
if (runningQuery)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const Query query(Query::User);
|
||||||
|
QueryRunner *runner = new QueryRunner(query, serverId, this);
|
||||||
|
QObject::connect(runner, &QueryRunner::resultRetrieved, this, [this](const QByteArray &result) {
|
||||||
|
handleUser(ResultParser::parseUser(result));
|
||||||
|
});
|
||||||
|
QObject::connect(runner, &QueryRunner::finished, [runner]() { runner->deleteLater(); });
|
||||||
|
runningQuery = true;
|
||||||
|
runner->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitLabPluginPrivate::createAndSendEventsRequest(const QDateTime timeStamp, int page)
|
||||||
|
{
|
||||||
|
if (runningQuery)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Query query(Query::Events, {projectName});
|
||||||
|
QStringList additional = {"sort=asc"};
|
||||||
|
|
||||||
|
QDateTime after = timeStamp.addDays(-1);
|
||||||
|
additional.append(QLatin1String("after=%1").arg(after.toString("yyyy-MM-dd")));
|
||||||
|
query.setAdditionalParameters(additional);
|
||||||
|
|
||||||
|
if (page > 1)
|
||||||
|
query.setPageParameter(page);
|
||||||
|
|
||||||
|
QueryRunner *runner = new QueryRunner(query, serverId, this);
|
||||||
|
QObject::connect(runner, &QueryRunner::resultRetrieved, this,
|
||||||
|
[this, timeStamp](const QByteArray &result) {
|
||||||
|
handleEvents(ResultParser::parseEvents(result), timeStamp);
|
||||||
|
});
|
||||||
|
QObject::connect(runner, &QueryRunner::finished, [runner]() { runner->deleteLater(); });
|
||||||
|
runningQuery = true;
|
||||||
|
runner->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitLabPluginPrivate::handleUser(const User &user)
|
||||||
|
{
|
||||||
|
runningQuery = false;
|
||||||
|
|
||||||
|
QTC_ASSERT(user.error.message.isEmpty(), return);
|
||||||
|
const QDateTime timeStamp = QDateTime::fromString(user.lastLogin, Qt::ISODateWithMs);
|
||||||
|
createAndSendEventsRequest(timeStamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitLabPluginPrivate::handleEvents(const Events &events, const QDateTime &timeStamp)
|
||||||
|
{
|
||||||
|
runningQuery = false;
|
||||||
|
|
||||||
|
ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
|
||||||
|
QTC_ASSERT(project, return);
|
||||||
|
|
||||||
|
GitLabProjectSettings *projSettings = GitLabPlugin::projectSettings(project);
|
||||||
|
QTC_ASSERT(projSettings->currentProject() == projectName, return);
|
||||||
|
|
||||||
|
if (!projSettings->isLinked()) // link state has changed meanwhile - ignore the request
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!events.error.message.isEmpty()) {
|
||||||
|
VcsBase::VcsOutputWindow::appendError("GitLab: Error while fetching events. "
|
||||||
|
+ events.error.message + '\n');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDateTime lastTimeStamp;
|
||||||
|
for (const Event &event : events.events) {
|
||||||
|
const QDateTime eventTimeStamp = QDateTime::fromString(event.timeStamp, Qt::ISODateWithMs);
|
||||||
|
if (!timeStamp.isValid() || timeStamp < eventTimeStamp) {
|
||||||
|
VcsBase::VcsOutputWindow::appendMessage("GitLab: " + event.toMessage());
|
||||||
|
if (!lastTimeStamp.isValid() || lastTimeStamp < eventTimeStamp)
|
||||||
|
lastTimeStamp = eventTimeStamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lastTimeStamp.isValid()) {
|
||||||
|
if (auto outputWindow = VcsBase::VcsOutputWindow::instance())
|
||||||
|
outputWindow->flash();
|
||||||
|
projSettings->setLastRequest(lastTimeStamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (events.pageInfo.currentPage < events.pageInfo.totalPages)
|
||||||
|
createAndSendEventsRequest(timeStamp, events.pageInfo.currentPage + 1);
|
||||||
|
}
|
||||||
|
|
||||||
QList<GitLabServer> GitLabPlugin::allGitLabServers()
|
QList<GitLabServer> GitLabPlugin::allGitLabServers()
|
||||||
{
|
{
|
||||||
QTC_ASSERT(dd, return {});
|
QTC_ASSERT(dd, return {});
|
||||||
@@ -107,4 +307,28 @@ GitLabOptionsPage *GitLabPlugin::optionsPage()
|
|||||||
return &dd->optionsPage;
|
return &dd->optionsPage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GitLabPlugin::linkedStateChanged(bool enabled)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(dd, return);
|
||||||
|
|
||||||
|
ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
|
||||||
|
if (project) {
|
||||||
|
const GitLabProjectSettings *pSettings = projectSettings(project);
|
||||||
|
dd->serverId = pSettings->currentServer();
|
||||||
|
dd->projectName = pSettings->currentProject();
|
||||||
|
} else {
|
||||||
|
dd->serverId = Utils::Id();
|
||||||
|
dd->projectName = QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enabled) {
|
||||||
|
dd->fetchEvents();
|
||||||
|
dd->setupNotificationTimer();
|
||||||
|
} else {
|
||||||
|
QObject::disconnect(&dd->notificationTimer, &QTimer::timeout,
|
||||||
|
dd, &GitLabPluginPrivate::fetchEvents);
|
||||||
|
dd->notificationTimer.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace GitLab
|
} // namespace GitLab
|
||||||
|
@@ -33,6 +33,7 @@ namespace ProjectExplorer { class Project; }
|
|||||||
|
|
||||||
namespace GitLab {
|
namespace GitLab {
|
||||||
|
|
||||||
|
class Events;
|
||||||
class GitLabProjectSettings;
|
class GitLabProjectSettings;
|
||||||
class GitLabOptionsPage;
|
class GitLabOptionsPage;
|
||||||
|
|
||||||
@@ -52,6 +53,11 @@ public:
|
|||||||
static GitLabParameters *globalParameters();
|
static GitLabParameters *globalParameters();
|
||||||
static GitLabProjectSettings *projectSettings(ProjectExplorer::Project *project);
|
static GitLabProjectSettings *projectSettings(ProjectExplorer::Project *project);
|
||||||
static GitLabOptionsPage *optionsPage();
|
static GitLabOptionsPage *optionsPage();
|
||||||
|
|
||||||
|
static void linkedStateChanged(bool enabled);
|
||||||
|
private:
|
||||||
|
void openView();
|
||||||
|
void onStartupProjectChanged();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace GitLab
|
} // namespace GitLab
|
||||||
|
@@ -49,6 +49,7 @@ namespace GitLab {
|
|||||||
const char PSK_LINKED_ID[] = "GitLab.LinkedId";
|
const char PSK_LINKED_ID[] = "GitLab.LinkedId";
|
||||||
const char PSK_SERVER[] = "GitLab.Server";
|
const char PSK_SERVER[] = "GitLab.Server";
|
||||||
const char PSK_PROJECT[] = "GitLab.Project";
|
const char PSK_PROJECT[] = "GitLab.Project";
|
||||||
|
const char PSK_LAST_REQ[] = "GitLab.LastRequest";
|
||||||
|
|
||||||
static QString accessLevelString(int accessLevel)
|
static QString accessLevelString(int accessLevel)
|
||||||
{
|
{
|
||||||
@@ -106,6 +107,7 @@ void GitLabProjectSettings::load()
|
|||||||
m_id = Utils::Id::fromSetting(m_project->namedSettings(PSK_LINKED_ID));
|
m_id = Utils::Id::fromSetting(m_project->namedSettings(PSK_LINKED_ID));
|
||||||
m_host = m_project->namedSettings(PSK_SERVER).toString();
|
m_host = m_project->namedSettings(PSK_SERVER).toString();
|
||||||
m_currentProject = m_project->namedSettings(PSK_PROJECT).toString();
|
m_currentProject = m_project->namedSettings(PSK_PROJECT).toString();
|
||||||
|
m_lastRequest = m_project->namedSettings(PSK_LAST_REQ).toDateTime();
|
||||||
|
|
||||||
// may still be wrong, but we avoid an additional request by just doing sanity check here
|
// may still be wrong, but we avoid an additional request by just doing sanity check here
|
||||||
if (!m_id.isValid() || m_host.isEmpty())
|
if (!m_id.isValid() || m_host.isEmpty())
|
||||||
@@ -124,6 +126,7 @@ void GitLabProjectSettings::save()
|
|||||||
m_project->setNamedSettings(PSK_SERVER, QString());
|
m_project->setNamedSettings(PSK_SERVER, QString());
|
||||||
}
|
}
|
||||||
m_project->setNamedSettings(PSK_PROJECT, m_currentProject);
|
m_project->setNamedSettings(PSK_PROJECT, m_currentProject);
|
||||||
|
m_project->setNamedSettings(PSK_LAST_REQ, m_lastRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
GitLabProjectSettingsWidget::GitLabProjectSettingsWidget(ProjectExplorer::Project *project,
|
GitLabProjectSettingsWidget::GitLabProjectSettingsWidget(ProjectExplorer::Project *project,
|
||||||
@@ -184,6 +187,7 @@ void GitLabProjectSettingsWidget::unlink()
|
|||||||
m_projectSettings->setLinked(false);
|
m_projectSettings->setLinked(false);
|
||||||
m_projectSettings->setCurrentProject({});
|
m_projectSettings->setCurrentProject({});
|
||||||
updateEnabledStates();
|
updateEnabledStates();
|
||||||
|
GitLabPlugin::linkedStateChanged(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GitLabProjectSettingsWidget::checkConnection(CheckMode mode)
|
void GitLabProjectSettingsWidget::checkConnection(CheckMode mode)
|
||||||
@@ -245,6 +249,7 @@ void GitLabProjectSettingsWidget::onConnectionChecked(const Project &project,
|
|||||||
m_projectSettings->setCurrentServerHost(remote);
|
m_projectSettings->setCurrentServerHost(remote);
|
||||||
m_projectSettings->setLinked(true);
|
m_projectSettings->setLinked(true);
|
||||||
m_projectSettings->setCurrentProject(projectName);
|
m_projectSettings->setCurrentProject(projectName);
|
||||||
|
GitLabPlugin::linkedStateChanged(true);
|
||||||
}
|
}
|
||||||
updateEnabledStates();
|
updateEnabledStates();
|
||||||
}
|
}
|
||||||
@@ -282,8 +287,10 @@ void GitLabProjectSettingsWidget::updateUi()
|
|||||||
m_hostCB->setCurrentIndex(m_hostCB->findData(QVariant::fromValue(serverHost)));
|
m_hostCB->setCurrentIndex(m_hostCB->findData(QVariant::fromValue(serverHost)));
|
||||||
m_linkedGitLabServer->setCurrentIndex(
|
m_linkedGitLabServer->setCurrentIndex(
|
||||||
m_linkedGitLabServer->findData(QVariant::fromValue(server)));
|
m_linkedGitLabServer->findData(QVariant::fromValue(server)));
|
||||||
|
GitLabPlugin::linkedStateChanged(true);
|
||||||
} else {
|
} else {
|
||||||
m_projectSettings->setLinked(false);
|
m_projectSettings->setLinked(false);
|
||||||
|
GitLabPlugin::linkedStateChanged(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateEnabledStates();
|
updateEnabledStates();
|
||||||
@@ -294,6 +301,7 @@ void GitLabProjectSettingsWidget::updateEnabledStates()
|
|||||||
const bool isGitRepository = m_hostCB->count() > 0;
|
const bool isGitRepository = m_hostCB->count() > 0;
|
||||||
const bool hasGitLabServers = m_linkedGitLabServer->count();
|
const bool hasGitLabServers = m_linkedGitLabServer->count();
|
||||||
const bool linked = m_projectSettings->isLinked();
|
const bool linked = m_projectSettings->isLinked();
|
||||||
|
|
||||||
m_linkedGitLabServer->setEnabled(isGitRepository && !linked);
|
m_linkedGitLabServer->setEnabled(isGitRepository && !linked);
|
||||||
m_hostCB->setEnabled(isGitRepository && !linked);
|
m_hostCB->setEnabled(isGitRepository && !linked);
|
||||||
m_linkWithGitLab->setEnabled(isGitRepository && !linked && hasGitLabServers);
|
m_linkWithGitLab->setEnabled(isGitRepository && !linked && hasGitLabServers);
|
||||||
|
@@ -28,6 +28,7 @@
|
|||||||
#include <projectexplorer/projectsettingswidget.h>
|
#include <projectexplorer/projectsettingswidget.h>
|
||||||
#include <utils/id.h>
|
#include <utils/id.h>
|
||||||
|
|
||||||
|
#include <QDateTime>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
@@ -59,9 +60,12 @@ public:
|
|||||||
QString currentProject() const { return m_currentProject; }
|
QString currentProject() const { return m_currentProject; }
|
||||||
bool isLinked() const { return m_linked; }
|
bool isLinked() const { return m_linked; }
|
||||||
void setLinked(bool linked);
|
void setLinked(bool linked);
|
||||||
|
QDateTime lastRequest() const { return m_lastRequest; }
|
||||||
|
void setLastRequest(const QDateTime &lastRequest) { m_lastRequest = lastRequest; }
|
||||||
ProjectExplorer::Project *project() const { return m_project; }
|
ProjectExplorer::Project *project() const { return m_project; }
|
||||||
|
|
||||||
static std::tuple<QString, QString, int> remotePartsFromRemote(const QString &remote);
|
static std::tuple<QString, QString, int> remotePartsFromRemote(const QString &remote);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void load();
|
void load();
|
||||||
void save();
|
void save();
|
||||||
@@ -69,6 +73,7 @@ private:
|
|||||||
ProjectExplorer::Project *m_project = nullptr;
|
ProjectExplorer::Project *m_project = nullptr;
|
||||||
QString m_host;
|
QString m_host;
|
||||||
Utils::Id m_id;
|
Utils::Id m_id;
|
||||||
|
QDateTime m_lastRequest;
|
||||||
QString m_currentProject;
|
QString m_currentProject;
|
||||||
bool m_linked = false;
|
bool m_linked = false;
|
||||||
};
|
};
|
||||||
|
@@ -41,6 +41,9 @@ namespace GitLab {
|
|||||||
|
|
||||||
const char API_PREFIX[] = "/api/v4";
|
const char API_PREFIX[] = "/api/v4";
|
||||||
const char QUERY_PROJECT[] = "/projects/%1";
|
const char QUERY_PROJECT[] = "/projects/%1";
|
||||||
|
const char QUERY_PROJECTS[] = "/projects?simple=true";
|
||||||
|
const char QUERY_USER[] = "/user";
|
||||||
|
const char QUERY_EVENTS[] = "/projects/%1/events";
|
||||||
|
|
||||||
Query::Query(Type type, const QStringList ¶meter)
|
Query::Query(Type type, const QStringList ¶meter)
|
||||||
: m_type(type)
|
: m_type(type)
|
||||||
@@ -48,6 +51,21 @@ Query::Query(Type type, const QStringList ¶meter)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Query::setPageParameter(int page)
|
||||||
|
{
|
||||||
|
m_pageParameter = page;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Query::setAdditionalParameters(const QStringList &additional)
|
||||||
|
{
|
||||||
|
m_additionalParameters = additional;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Query::hasPaginatedResults() const
|
||||||
|
{
|
||||||
|
return m_type == Query::Projects || m_type == Query::Events;
|
||||||
|
}
|
||||||
|
|
||||||
QString Query::toString() const
|
QString Query::toString() const
|
||||||
{
|
{
|
||||||
QString query = API_PREFIX;
|
QString query = API_PREFIX;
|
||||||
@@ -59,6 +77,25 @@ QString Query::toString() const
|
|||||||
query += QLatin1String(QUERY_PROJECT).arg(QLatin1String(
|
query += QLatin1String(QUERY_PROJECT).arg(QLatin1String(
|
||||||
QUrl::toPercentEncoding(m_parameter.at(0))));
|
QUrl::toPercentEncoding(m_parameter.at(0))));
|
||||||
break;
|
break;
|
||||||
|
case Query::Projects:
|
||||||
|
query += QLatin1String(QUERY_PROJECTS);
|
||||||
|
break;
|
||||||
|
case Query::User:
|
||||||
|
query += QUERY_USER;
|
||||||
|
break;
|
||||||
|
case Query::Events:
|
||||||
|
QTC_ASSERT(!m_parameter.isEmpty(), return {});
|
||||||
|
query += QLatin1String(QUERY_EVENTS).arg(QLatin1String(
|
||||||
|
QUrl::toPercentEncoding(m_parameter.at(0))));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (m_pageParameter > 0) {
|
||||||
|
query.append(m_type == Query::Projects ? '&' : '?');
|
||||||
|
query.append("page=").append(QString::number(m_pageParameter));
|
||||||
|
}
|
||||||
|
if (!m_additionalParameters.isEmpty()) {
|
||||||
|
query.append((m_type == Query::Projects || m_pageParameter > 0) ? '&' : '?');
|
||||||
|
query.append(m_additionalParameters.join('&'));
|
||||||
}
|
}
|
||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
@@ -69,6 +106,9 @@ QueryRunner::QueryRunner(const Query &query, const Utils::Id &id, QObject *paren
|
|||||||
const GitLabParameters *p = GitLabPlugin::globalParameters();
|
const GitLabParameters *p = GitLabPlugin::globalParameters();
|
||||||
const auto server = p->serverForId(id);
|
const auto server = p->serverForId(id);
|
||||||
QStringList args = server.curlArguments();
|
QStringList args = server.curlArguments();
|
||||||
|
m_paginated = query.hasPaginatedResults();
|
||||||
|
if (m_paginated)
|
||||||
|
args << "-i";
|
||||||
if (!server.token.isEmpty())
|
if (!server.token.isEmpty())
|
||||||
args << "--header" << "PRIVATE-TOKEN: " + server.token;
|
args << "--header" << "PRIVATE-TOKEN: " + server.token;
|
||||||
QString url = "https://" + server.host;
|
QString url = "https://" + server.host;
|
||||||
|
@@ -38,15 +38,24 @@ class Query
|
|||||||
public:
|
public:
|
||||||
enum Type {
|
enum Type {
|
||||||
NoQuery,
|
NoQuery,
|
||||||
Project
|
User,
|
||||||
|
Project,
|
||||||
|
Projects,
|
||||||
|
Events
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit Query(Type type, const QStringList ¶meters = {});
|
explicit Query(Type type, const QStringList ¶meters = {});
|
||||||
|
void setPageParameter(int page);
|
||||||
|
void setAdditionalParameters(const QStringList &additional);
|
||||||
|
bool hasPaginatedResults() const;
|
||||||
|
Type type() const { return m_type; }
|
||||||
QString toString() const;
|
QString toString() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Type m_type = NoQuery;
|
Type m_type = NoQuery;
|
||||||
QStringList m_parameter;
|
QStringList m_parameter;
|
||||||
|
QStringList m_additionalParameters;
|
||||||
|
int m_pageParameter = -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
class QueryRunner : public QObject
|
class QueryRunner : public QObject
|
||||||
@@ -70,6 +79,7 @@ private:
|
|||||||
|
|
||||||
Utils::QtcProcess m_process;
|
Utils::QtcProcess m_process;
|
||||||
bool m_running = false;
|
bool m_running = false;
|
||||||
|
bool m_paginated = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace GitLab
|
} // namespace GitLab
|
||||||
|
@@ -32,8 +32,59 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace GitLab {
|
namespace GitLab {
|
||||||
|
|
||||||
|
|
||||||
|
QString Event::toMessage() const
|
||||||
|
{
|
||||||
|
QString message;
|
||||||
|
if (author.realname.isEmpty())
|
||||||
|
message.append(author.name);
|
||||||
|
else
|
||||||
|
message.append(author.realname + " (" + author.name + ')');
|
||||||
|
message.append(' ');
|
||||||
|
if (!pushData.isEmpty())
|
||||||
|
message.append(pushData);
|
||||||
|
else if (!targetTitle.isEmpty())
|
||||||
|
message.append(action + ' ' + targetType + " '" + targetTitle +'\'');
|
||||||
|
else
|
||||||
|
message.append(action + ' ' + targetType);
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
namespace ResultParser {
|
namespace ResultParser {
|
||||||
|
|
||||||
|
static PageInformation paginationInformation(const QByteArray &header)
|
||||||
|
{
|
||||||
|
PageInformation result;
|
||||||
|
const QByteArrayList lines = header.split('\n');
|
||||||
|
for (const QByteArray &line : lines) {
|
||||||
|
const QByteArray lower = line.toLower(); // depending on OS this may be capitalized
|
||||||
|
if (lower.startsWith("x-page: "))
|
||||||
|
result.currentPage = line.mid(8).toInt();
|
||||||
|
else if (lower.startsWith("x-per-page: "))
|
||||||
|
result.perPage = line.mid(12).toInt();
|
||||||
|
else if (lower.startsWith("x-total: "))
|
||||||
|
result.total = line.mid(9).toInt();
|
||||||
|
else if (lower.startsWith("x-total-pages: "))
|
||||||
|
result.totalPages = line.mid(15).toInt();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::pair<QByteArray, QByteArray> splitHeaderAndBody(const QByteArray &input)
|
||||||
|
{
|
||||||
|
QByteArray header;
|
||||||
|
QByteArray json;
|
||||||
|
int emptyLine = input.indexOf("\r\n\r\n"); // we always get \r\n as line separator?
|
||||||
|
if (emptyLine != -1) {
|
||||||
|
header = input.left(emptyLine);
|
||||||
|
json = input.mid(emptyLine + 4);
|
||||||
|
} else {
|
||||||
|
json = input;
|
||||||
|
}
|
||||||
|
return std::make_pair(header, json);
|
||||||
|
}
|
||||||
|
|
||||||
static std::pair<Error, QJsonObject> preHandleSingle(const QByteArray &json)
|
static std::pair<Error, QJsonObject> preHandleSingle(const QByteArray &json)
|
||||||
{
|
{
|
||||||
Error result;
|
Error result;
|
||||||
@@ -59,6 +110,53 @@ static std::pair<Error, QJsonObject> preHandleSingle(const QByteArray &json)
|
|||||||
return std::make_pair(result, object);
|
return std::make_pair(result, object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::pair<Error, QJsonDocument> preHandleHeaderAndBody(const QByteArray &header,
|
||||||
|
const QByteArray &json)
|
||||||
|
{
|
||||||
|
Error result;
|
||||||
|
if (header.isEmpty()) {
|
||||||
|
result.message = "Missing Expected Header";
|
||||||
|
return std::make_pair(result, QJsonDocument());
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonParseError error;
|
||||||
|
const QJsonDocument doc = QJsonDocument::fromJson(json, &error);
|
||||||
|
if (error.error != QJsonParseError::NoError) {
|
||||||
|
result.message = error.errorString();
|
||||||
|
return std::make_pair(result, doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doc.isObject()) {
|
||||||
|
const QJsonObject obj = doc.object();
|
||||||
|
if (obj.contains("message")) {
|
||||||
|
result = parseErrorMessage(obj.value("message").toString());
|
||||||
|
return std::make_pair(result, doc);
|
||||||
|
} else if (obj.contains("error")) {
|
||||||
|
if (obj.value("error").toString() == "insufficient_scope")
|
||||||
|
result.code = 1;
|
||||||
|
result.message = obj.value("error_description").toString();
|
||||||
|
return std::make_pair(result, doc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!doc.isArray())
|
||||||
|
result.message = "Not an Array";
|
||||||
|
|
||||||
|
return std::make_pair(result, doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static User userFromJson(const QJsonObject &jsonObj)
|
||||||
|
{
|
||||||
|
User user;
|
||||||
|
user.name = jsonObj.value("username").toString();
|
||||||
|
user.realname = jsonObj.value("name").toString();
|
||||||
|
user.id = jsonObj.value("id").toInt(-1);
|
||||||
|
user.email = jsonObj.value("email").toString();
|
||||||
|
user.lastLogin = jsonObj.value("last_sign_in_at").toString();
|
||||||
|
user.bot = jsonObj.value("bot").toBool();
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
static Project projectFromJson(const QJsonObject &jsonObj)
|
static Project projectFromJson(const QJsonObject &jsonObj)
|
||||||
{
|
{
|
||||||
Project project;
|
Project project;
|
||||||
@@ -67,6 +165,8 @@ static Project projectFromJson(const QJsonObject &jsonObj)
|
|||||||
project.pathName = jsonObj.value("path_with_namespace").toString();
|
project.pathName = jsonObj.value("path_with_namespace").toString();
|
||||||
project.id = jsonObj.value("id").toInt(-1);
|
project.id = jsonObj.value("id").toInt(-1);
|
||||||
project.visibility = jsonObj.value("visibility").toString("public");
|
project.visibility = jsonObj.value("visibility").toString("public");
|
||||||
|
project.httpUrl = jsonObj.value("http_url_to_repo").toString();
|
||||||
|
project.sshUrl = jsonObj.value("ssh_url_to_repo").toString();
|
||||||
if (jsonObj.contains("forks_count"))
|
if (jsonObj.contains("forks_count"))
|
||||||
project.forkCount = jsonObj.value("forks_count").toInt();
|
project.forkCount = jsonObj.value("forks_count").toInt();
|
||||||
if (jsonObj.contains("star_count"))
|
if (jsonObj.contains("star_count"))
|
||||||
@@ -82,6 +182,42 @@ static Project projectFromJson(const QJsonObject &jsonObj)
|
|||||||
return project;
|
return project;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Event eventFromJson(const QJsonObject &jsonObj)
|
||||||
|
{
|
||||||
|
Event event;
|
||||||
|
event.action = jsonObj.value("action_name").toString();
|
||||||
|
const QJsonValue value = jsonObj.value("target_type");
|
||||||
|
event.targetType = value.isNull() ? "project" : jsonObj.value("target_type").toString();
|
||||||
|
if (event.targetType == "DiffNote") {
|
||||||
|
const QJsonObject noteObject = jsonObj.value("note").toObject();
|
||||||
|
event.targetType = noteObject.value("noteable_type").toString();
|
||||||
|
}
|
||||||
|
event.targetTitle = jsonObj.value("target_title").toString();
|
||||||
|
event.author = userFromJson(jsonObj.value("author").toObject());
|
||||||
|
event.timeStamp = jsonObj.value("created_at").toString();
|
||||||
|
if (jsonObj.contains("push_data")) {
|
||||||
|
const QJsonObject pushDataObj = jsonObj.value("push_data").toObject();
|
||||||
|
if (!pushDataObj.isEmpty()) {
|
||||||
|
const QString action = pushDataObj.value("action").toString();
|
||||||
|
const QString ref = pushDataObj.value("ref").toString();
|
||||||
|
const QString refType = pushDataObj.value("ref_type").toString();
|
||||||
|
event.pushData = action + ' ' + refType + " '" + ref + '\'';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
User parseUser(const QByteArray &input)
|
||||||
|
{
|
||||||
|
auto [error, userObj] = preHandleSingle(input);
|
||||||
|
if (!error.message.isEmpty()) {
|
||||||
|
User result;
|
||||||
|
result.error = error;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return userFromJson(userObj);
|
||||||
|
}
|
||||||
|
|
||||||
Project parseProject(const QByteArray &input)
|
Project parseProject(const QByteArray &input)
|
||||||
{
|
{
|
||||||
auto [error, projectObj] = preHandleSingle(input);
|
auto [error, projectObj] = preHandleSingle(input);
|
||||||
@@ -93,6 +229,47 @@ Project parseProject(const QByteArray &input)
|
|||||||
return projectFromJson(projectObj);
|
return projectFromJson(projectObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Projects parseProjects(const QByteArray &input)
|
||||||
|
{
|
||||||
|
auto [header, json] = splitHeaderAndBody(input);
|
||||||
|
auto [error, jsonDoc] = preHandleHeaderAndBody(header, json);
|
||||||
|
Projects result;
|
||||||
|
if (!error.message.isEmpty()) {
|
||||||
|
result.error = error;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
result.pageInfo = paginationInformation(header);
|
||||||
|
const QJsonArray projectsArray = jsonDoc.array();
|
||||||
|
for (const QJsonValue &value : projectsArray) {
|
||||||
|
if (!value.isObject())
|
||||||
|
continue;
|
||||||
|
const QJsonObject projectObj = value.toObject();
|
||||||
|
result.projects.append(projectFromJson(projectObj));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Events parseEvents(const QByteArray &input)
|
||||||
|
{
|
||||||
|
auto [header, json] = splitHeaderAndBody(input);
|
||||||
|
auto [error, jsonDoc] = preHandleHeaderAndBody(header, json);
|
||||||
|
Events result;
|
||||||
|
if (!error.message.isEmpty()) {
|
||||||
|
result.error = error;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
result.pageInfo = paginationInformation(header);
|
||||||
|
const QJsonArray eventsArray = jsonDoc.array();
|
||||||
|
for (const QJsonValue &value : eventsArray) {
|
||||||
|
if (!value.isObject())
|
||||||
|
continue;
|
||||||
|
const QJsonObject eventObj = value.toObject();
|
||||||
|
result.events.append(eventFromJson(eventObj));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Error parseErrorMessage(const QString &message)
|
Error parseErrorMessage(const QString &message)
|
||||||
{
|
{
|
||||||
Error error;
|
Error error;
|
||||||
|
@@ -25,6 +25,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <QList>
|
||||||
|
#include <QMetaType>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
namespace GitLab {
|
namespace GitLab {
|
||||||
@@ -35,6 +37,27 @@ struct Error
|
|||||||
QString message;
|
QString message;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PageInformation
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int currentPage = -1;
|
||||||
|
int totalPages = -1;
|
||||||
|
int perPage = -1;
|
||||||
|
int total = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
class User
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QString name;
|
||||||
|
QString realname;
|
||||||
|
QString email;
|
||||||
|
QString lastLogin;
|
||||||
|
Error error;
|
||||||
|
int id = -1;
|
||||||
|
bool bot = false;
|
||||||
|
};
|
||||||
|
|
||||||
class Project
|
class Project
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -42,18 +65,55 @@ public:
|
|||||||
QString displayName;
|
QString displayName;
|
||||||
QString pathName;
|
QString pathName;
|
||||||
QString visibility;
|
QString visibility;
|
||||||
|
QString httpUrl;
|
||||||
|
QString sshUrl;
|
||||||
Error error;
|
Error error;
|
||||||
int id = -1;
|
int id = -1;
|
||||||
int starCount = -1;
|
int starCount = -1;
|
||||||
int forkCount = -1;
|
int forkCount = -1;
|
||||||
int issuesCount = -1;
|
int issuesCount = -1;
|
||||||
int accessLevel = -1; // 40 maintainer, 30 developer, 20 reporter, 10 guest
|
int accessLevel = -1; // 50 owner, 40 maintainer, 30 developer, 20 reporter, 10 guest
|
||||||
|
};
|
||||||
|
|
||||||
|
class Projects
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QList<Project> projects;
|
||||||
|
Error error;
|
||||||
|
PageInformation pageInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Event
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QString action;
|
||||||
|
QString targetType;
|
||||||
|
QString targetTitle;
|
||||||
|
QString timeStamp;
|
||||||
|
QString pushData;
|
||||||
|
User author;
|
||||||
|
Error error;
|
||||||
|
|
||||||
|
QString toMessage() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Events
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QList<Event> events;
|
||||||
|
Error error;
|
||||||
|
PageInformation pageInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace ResultParser {
|
namespace ResultParser {
|
||||||
|
|
||||||
|
User parseUser(const QByteArray &input);
|
||||||
Project parseProject(const QByteArray &input);
|
Project parseProject(const QByteArray &input);
|
||||||
|
Projects parseProjects(const QByteArray &input);
|
||||||
|
Events parseEvents(const QByteArray &input);
|
||||||
Error parseErrorMessage(const QString &message);
|
Error parseErrorMessage(const QString &message);
|
||||||
|
|
||||||
} // namespace ResultParser
|
} // namespace ResultParser
|
||||||
} // namespace GitLab
|
} // namespace GitLab
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(GitLab::Project)
|
||||||
|
@@ -197,8 +197,8 @@ void IosRunner::start()
|
|||||||
connect(m_toolHandler, &IosToolHandler::finished,
|
connect(m_toolHandler, &IosToolHandler::finished,
|
||||||
this, &IosRunner::handleFinished);
|
this, &IosRunner::handleFinished);
|
||||||
|
|
||||||
const Runnable runnable = runControl()->runnable();
|
const CommandLine command = runControl()->commandLine();
|
||||||
QStringList args = ProcessArgs::splitArgs(runnable.command.arguments(), OsTypeMac);
|
QStringList args = ProcessArgs::splitArgs(command.arguments(), OsTypeMac);
|
||||||
if (m_qmlServerPort.isValid()) {
|
if (m_qmlServerPort.isValid()) {
|
||||||
QUrl qmlServer;
|
QUrl qmlServer;
|
||||||
qmlServer.setPort(m_qmlServerPort.number());
|
qmlServer.setPort(m_qmlServerPort.number());
|
||||||
|
@@ -918,6 +918,7 @@ void Client::documentContentsChanged(TextEditor::TextDocument *document,
|
|||||||
{
|
{
|
||||||
if (!d->m_openedDocument.contains(document) || !reachable())
|
if (!d->m_openedDocument.contains(document) || !reachable())
|
||||||
return;
|
return;
|
||||||
|
d->m_diagnosticManager->disableDiagnostics(document);
|
||||||
const QString method(DidChangeTextDocumentNotification::methodName);
|
const QString method(DidChangeTextDocumentNotification::methodName);
|
||||||
TextDocumentSyncKind syncKind = d->m_serverCapabilities.textDocumentSyncKindHelper();
|
TextDocumentSyncKind syncKind = d->m_serverCapabilities.textDocumentSyncKindHelper();
|
||||||
if (Utils::optional<bool> registered = d->m_dynamicCapabilities.isRegistered(method)) {
|
if (Utils::optional<bool> registered = d->m_dynamicCapabilities.isRegistered(method)) {
|
||||||
|
@@ -90,7 +90,7 @@ void DiagnosticManager::hideDiagnostics(const Utils::FilePath &filePath)
|
|||||||
for (BaseTextEditor *editor : BaseTextEditor::textEditorsForDocument(doc))
|
for (BaseTextEditor *editor : BaseTextEditor::textEditorsForDocument(doc))
|
||||||
editor->editorWidget()->setExtraSelections(m_extraSelectionsId, {});
|
editor->editorWidget()->setExtraSelections(m_extraSelectionsId, {});
|
||||||
}
|
}
|
||||||
qDeleteAll(m_marks.take(filePath));
|
m_marks.remove(filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<Diagnostic> DiagnosticManager::filteredDiagnostics(const QList<Diagnostic> &diagnostics) const
|
QList<Diagnostic> DiagnosticManager::filteredDiagnostics(const QList<Diagnostic> &diagnostics) const
|
||||||
@@ -98,6 +98,17 @@ QList<Diagnostic> DiagnosticManager::filteredDiagnostics(const QList<Diagnostic>
|
|||||||
return diagnostics;
|
return diagnostics;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DiagnosticManager::disableDiagnostics(TextEditor::TextDocument *document)
|
||||||
|
{
|
||||||
|
|
||||||
|
Marks &marks = m_marks[document->filePath()];
|
||||||
|
if (!marks.enabled)
|
||||||
|
return;
|
||||||
|
for (TextEditor::TextMark *mark : marks.marks)
|
||||||
|
mark->setColor(Utils::Theme::Color::IconsDisabledColor);
|
||||||
|
marks.enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
void DiagnosticManager::showDiagnostics(const DocumentUri &uri, int version)
|
void DiagnosticManager::showDiagnostics(const DocumentUri &uri, int version)
|
||||||
{
|
{
|
||||||
const FilePath &filePath = uri.toFilePath();
|
const FilePath &filePath = uri.toFilePath();
|
||||||
@@ -106,7 +117,7 @@ void DiagnosticManager::showDiagnostics(const DocumentUri &uri, int version)
|
|||||||
const VersionedDiagnostics &versionedDiagnostics = m_diagnostics.value(uri);
|
const VersionedDiagnostics &versionedDiagnostics = m_diagnostics.value(uri);
|
||||||
if (versionedDiagnostics.version.value_or(version) == version
|
if (versionedDiagnostics.version.value_or(version) == version
|
||||||
&& !versionedDiagnostics.diagnostics.isEmpty()) {
|
&& !versionedDiagnostics.diagnostics.isEmpty()) {
|
||||||
QList<TextEditor::TextMark *> &marks = m_marks[filePath];
|
Marks &marks = m_marks[filePath];
|
||||||
const bool isProjectFile = m_client->project()
|
const bool isProjectFile = m_client->project()
|
||||||
&& m_client->project()->isKnownFile(filePath);
|
&& m_client->project()->isKnownFile(filePath);
|
||||||
for (const Diagnostic &diagnostic : versionedDiagnostics.diagnostics) {
|
for (const Diagnostic &diagnostic : versionedDiagnostics.diagnostics) {
|
||||||
@@ -115,9 +126,9 @@ void DiagnosticManager::showDiagnostics(const DocumentUri &uri, int version)
|
|||||||
if (!selection.cursor.isNull())
|
if (!selection.cursor.isNull())
|
||||||
extraSelections << selection;
|
extraSelections << selection;
|
||||||
if (TextEditor::TextMark *mark = createTextMark(filePath, diagnostic, isProjectFile))
|
if (TextEditor::TextMark *mark = createTextMark(filePath, diagnostic, isProjectFile))
|
||||||
marks.append(mark);
|
marks.marks.append(mark);
|
||||||
}
|
}
|
||||||
if (!marks.isEmpty())
|
if (!marks.marks.isEmpty())
|
||||||
emit textMarkCreated(filePath);
|
emit textMarkCreated(filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,11 +181,7 @@ void DiagnosticManager::clearDiagnostics()
|
|||||||
for (const DocumentUri &uri : m_diagnostics.keys())
|
for (const DocumentUri &uri : m_diagnostics.keys())
|
||||||
hideDiagnostics(uri.toFilePath());
|
hideDiagnostics(uri.toFilePath());
|
||||||
m_diagnostics.clear();
|
m_diagnostics.clear();
|
||||||
if (!QTC_GUARD(m_marks.isEmpty())) {
|
QTC_ASSERT(m_marks.isEmpty(), m_marks.clear());
|
||||||
for (const QList<TextEditor::TextMark *> &marks : qAsConst(m_marks))
|
|
||||||
qDeleteAll(marks);
|
|
||||||
m_marks.clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<Diagnostic> DiagnosticManager::diagnosticsAt(const DocumentUri &uri,
|
QList<Diagnostic> DiagnosticManager::diagnosticsAt(const DocumentUri &uri,
|
||||||
@@ -218,4 +225,9 @@ bool DiagnosticManager::hasDiagnostics(const TextDocument *doc) const
|
|||||||
return !it->diagnostics.isEmpty();
|
return !it->diagnostics.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DiagnosticManager::Marks::~Marks()
|
||||||
|
{
|
||||||
|
qDeleteAll(marks);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace LanguageClient
|
} // namespace LanguageClient
|
||||||
|
@@ -61,6 +61,7 @@ public:
|
|||||||
virtual QList<LanguageServerProtocol::Diagnostic> filteredDiagnostics(
|
virtual QList<LanguageServerProtocol::Diagnostic> filteredDiagnostics(
|
||||||
const QList<LanguageServerProtocol::Diagnostic> &diagnostics) const;
|
const QList<LanguageServerProtocol::Diagnostic> &diagnostics) const;
|
||||||
|
|
||||||
|
void disableDiagnostics(TextEditor::TextDocument *document);
|
||||||
void clearDiagnostics();
|
void clearDiagnostics();
|
||||||
|
|
||||||
QList<LanguageServerProtocol::Diagnostic> diagnosticsAt(
|
QList<LanguageServerProtocol::Diagnostic> diagnosticsAt(
|
||||||
@@ -91,7 +92,14 @@ private:
|
|||||||
QList<LanguageServerProtocol::Diagnostic> diagnostics;
|
QList<LanguageServerProtocol::Diagnostic> diagnostics;
|
||||||
};
|
};
|
||||||
QMap<LanguageServerProtocol::DocumentUri, VersionedDiagnostics> m_diagnostics;
|
QMap<LanguageServerProtocol::DocumentUri, VersionedDiagnostics> m_diagnostics;
|
||||||
QMap<Utils::FilePath, QList<TextEditor::TextMark *>> m_marks;
|
class Marks
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
~Marks();
|
||||||
|
bool enabled = true;
|
||||||
|
QList<TextEditor::TextMark *> marks;
|
||||||
|
};
|
||||||
|
QMap<Utils::FilePath, Marks> m_marks;
|
||||||
Client *m_client;
|
Client *m_client;
|
||||||
Utils::Id m_extraSelectionsId;
|
Utils::Id m_extraSelectionsId;
|
||||||
};
|
};
|
||||||
|
@@ -46,7 +46,7 @@ MesonRunConfiguration::MesonRunConfiguration(ProjectExplorer::Target *target, Ut
|
|||||||
|
|
||||||
addAspect<ProjectExplorer::ExecutableAspect>(target);
|
addAspect<ProjectExplorer::ExecutableAspect>(target);
|
||||||
addAspect<ProjectExplorer::ArgumentsAspect>(macroExpander());
|
addAspect<ProjectExplorer::ArgumentsAspect>(macroExpander());
|
||||||
addAspect<ProjectExplorer::WorkingDirectoryAspect>(envAspect);
|
addAspect<ProjectExplorer::WorkingDirectoryAspect>(macroExpander(), envAspect);
|
||||||
addAspect<ProjectExplorer::TerminalAspect>();
|
addAspect<ProjectExplorer::TerminalAspect>();
|
||||||
|
|
||||||
auto libAspect = addAspect<ProjectExplorer::UseLibraryPathsAspect>();
|
auto libAspect = addAspect<ProjectExplorer::UseLibraryPathsAspect>();
|
||||||
|
@@ -54,7 +54,7 @@ public:
|
|||||||
auto envAspect = addAspect<LocalEnvironmentAspect>(target);
|
auto envAspect = addAspect<LocalEnvironmentAspect>(target);
|
||||||
addAspect<ExecutableAspect>(target);
|
addAspect<ExecutableAspect>(target);
|
||||||
addAspect<ArgumentsAspect>(macroExpander());
|
addAspect<ArgumentsAspect>(macroExpander());
|
||||||
addAspect<WorkingDirectoryAspect>(envAspect);
|
addAspect<WorkingDirectoryAspect>(macroExpander(), envAspect);
|
||||||
addAspect<TerminalAspect>();
|
addAspect<TerminalAspect>();
|
||||||
|
|
||||||
setUpdater([this] {
|
setUpdater([this] {
|
||||||
@@ -91,7 +91,8 @@ public:
|
|||||||
{
|
{
|
||||||
addAspect<ExecutableAspect>(target)->setExecutable(Nim::nimblePathFromKit(target->kit()));
|
addAspect<ExecutableAspect>(target)->setExecutable(Nim::nimblePathFromKit(target->kit()));
|
||||||
addAspect<ArgumentsAspect>(macroExpander())->setArguments("test");
|
addAspect<ArgumentsAspect>(macroExpander())->setArguments("test");
|
||||||
addAspect<WorkingDirectoryAspect>(nullptr)->setDefaultWorkingDirectory(project()->projectDirectory());
|
addAspect<WorkingDirectoryAspect>(macroExpander(), nullptr)
|
||||||
|
->setDefaultWorkingDirectory(project()->projectDirectory());
|
||||||
addAspect<TerminalAspect>();
|
addAspect<TerminalAspect>();
|
||||||
|
|
||||||
setDisplayName(tr("Nimble Test"));
|
setDisplayName(tr("Nimble Test"));
|
||||||
|
@@ -52,7 +52,7 @@ public:
|
|||||||
auto envAspect = addAspect<LocalEnvironmentAspect>(target);
|
auto envAspect = addAspect<LocalEnvironmentAspect>(target);
|
||||||
addAspect<ExecutableAspect>(target);
|
addAspect<ExecutableAspect>(target);
|
||||||
addAspect<ArgumentsAspect>(macroExpander());
|
addAspect<ArgumentsAspect>(macroExpander());
|
||||||
addAspect<WorkingDirectoryAspect>(envAspect);
|
addAspect<WorkingDirectoryAspect>(macroExpander(), envAspect);
|
||||||
addAspect<TerminalAspect>();
|
addAspect<TerminalAspect>();
|
||||||
|
|
||||||
setDisplayName(tr("Current Build Target"));
|
setDisplayName(tr("Current Build Target"));
|
||||||
|
@@ -23,9 +23,9 @@
|
|||||||
**
|
**
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#include "perfdatareader.h"
|
|
||||||
#include "perfprofilerconstants.h"
|
|
||||||
#include "perfprofilerruncontrol.h"
|
#include "perfprofilerruncontrol.h"
|
||||||
|
|
||||||
|
#include "perfdatareader.h"
|
||||||
#include "perfprofilertool.h"
|
#include "perfprofilertool.h"
|
||||||
#include "perfrunconfigurationaspect.h"
|
#include "perfrunconfigurationaspect.h"
|
||||||
#include "perfsettings.h"
|
#include "perfsettings.h"
|
||||||
@@ -124,10 +124,6 @@ public:
|
|||||||
void start() override
|
void start() override
|
||||||
{
|
{
|
||||||
m_process = new QtcProcess(this);
|
m_process = new QtcProcess(this);
|
||||||
if (!m_process) {
|
|
||||||
reportFailure(tr("Could not start device process."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
connect(m_process, &QtcProcess::started, this, &RunWorker::reportStarted);
|
connect(m_process, &QtcProcess::started, this, &RunWorker::reportStarted);
|
||||||
connect(m_process, &QtcProcess::done, this, [this] {
|
connect(m_process, &QtcProcess::done, this, [this] {
|
||||||
@@ -144,12 +140,10 @@ public:
|
|||||||
reportStopped();
|
reportStopped();
|
||||||
});
|
});
|
||||||
|
|
||||||
Runnable perfRunnable = runnable();
|
|
||||||
|
|
||||||
CommandLine cmd({device()->filePath("perf"), {"record"}});
|
CommandLine cmd({device()->filePath("perf"), {"record"}});
|
||||||
cmd.addArgs(m_perfRecordArguments);
|
cmd.addArgs(m_perfRecordArguments);
|
||||||
cmd.addArgs({"-o", "-", "--"});
|
cmd.addArgs({"-o", "-", "--"});
|
||||||
cmd.addCommandLineAsArgs(perfRunnable.command, CommandLine::Raw);
|
cmd.addCommandLineAsArgs(runControl()->commandLine(), CommandLine::Raw);
|
||||||
|
|
||||||
m_process->setCommand(cmd);
|
m_process->setCommand(cmd);
|
||||||
m_process->start();
|
m_process->start();
|
||||||
|
@@ -60,6 +60,8 @@ add_qtc_plugin(ProjectExplorer
|
|||||||
devicesupport/devicesettingswidget.cpp devicesupport/devicesettingswidget.h devicesupport/devicesettingswidget.ui
|
devicesupport/devicesettingswidget.cpp devicesupport/devicesettingswidget.h devicesupport/devicesettingswidget.ui
|
||||||
devicesupport/devicetestdialog.cpp devicesupport/devicetestdialog.h devicesupport/devicetestdialog.ui
|
devicesupport/devicetestdialog.cpp devicesupport/devicetestdialog.h devicesupport/devicetestdialog.ui
|
||||||
devicesupport/deviceusedportsgatherer.cpp devicesupport/deviceusedportsgatherer.h
|
devicesupport/deviceusedportsgatherer.cpp devicesupport/deviceusedportsgatherer.h
|
||||||
|
devicesupport/filetransfer.cpp devicesupport/filetransfer.h
|
||||||
|
devicesupport/filetransferinterface.h
|
||||||
devicesupport/idevice.cpp devicesupport/idevice.h
|
devicesupport/idevice.cpp devicesupport/idevice.h
|
||||||
devicesupport/idevicefactory.cpp devicesupport/idevicefactory.h
|
devicesupport/idevicefactory.cpp devicesupport/idevicefactory.h
|
||||||
devicesupport/idevicefwd.h
|
devicesupport/idevicefwd.h
|
||||||
|
@@ -44,6 +44,7 @@
|
|||||||
|
|
||||||
#include <extensionsystem/invoker.h>
|
#include <extensionsystem/invoker.h>
|
||||||
#include <extensionsystem/pluginmanager.h>
|
#include <extensionsystem/pluginmanager.h>
|
||||||
|
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
#include <utils/outputformatter.h>
|
#include <utils/outputformatter.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
@@ -66,6 +67,8 @@
|
|||||||
|
|
||||||
static Q_LOGGING_CATEGORY(appOutputLog, "qtc.projectexplorer.appoutput", QtWarningMsg);
|
static Q_LOGGING_CATEGORY(appOutputLog, "qtc.projectexplorer.appoutput", QtWarningMsg);
|
||||||
|
|
||||||
|
using namespace Utils;
|
||||||
|
|
||||||
namespace ProjectExplorer {
|
namespace ProjectExplorer {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
@@ -398,19 +401,20 @@ void AppOutputPane::createNewOutputWindow(RunControl *rc)
|
|||||||
connect(rc, &RunControl::applicationProcessHandleChanged,
|
connect(rc, &RunControl::applicationProcessHandleChanged,
|
||||||
this, &AppOutputPane::enableDefaultButtons);
|
this, &AppOutputPane::enableDefaultButtons);
|
||||||
connect(rc, &RunControl::appendMessage,
|
connect(rc, &RunControl::appendMessage,
|
||||||
this, [this, rc](const QString &out, Utils::OutputFormat format) {
|
this, [this, rc](const QString &out, OutputFormat format) {
|
||||||
appendMessage(rc, out, format);
|
appendMessage(rc, out, format);
|
||||||
});
|
});
|
||||||
|
|
||||||
// First look if we can reuse a tab
|
// First look if we can reuse a tab
|
||||||
const Runnable thisRunnable = rc->runnable();
|
const CommandLine thisCommand = rc->commandLine();
|
||||||
|
const FilePath thisWorkingDirectory = rc->workingDirectory();
|
||||||
|
const Environment thisEnvironment = rc->environment();
|
||||||
const int tabIndex = Utils::indexOf(m_runControlTabs, [&](const RunControlTab &tab) {
|
const int tabIndex = Utils::indexOf(m_runControlTabs, [&](const RunControlTab &tab) {
|
||||||
if (!tab.runControl || tab.runControl->isRunning())
|
if (!tab.runControl || tab.runControl->isRunning())
|
||||||
return false;
|
return false;
|
||||||
const Runnable otherRunnable = tab.runControl->runnable();
|
return thisCommand == tab.runControl->commandLine()
|
||||||
return thisRunnable.command == otherRunnable.command
|
&& thisWorkingDirectory == tab.runControl->workingDirectory()
|
||||||
&& thisRunnable.workingDirectory == otherRunnable.workingDirectory
|
&& thisEnvironment == tab.runControl->environment();
|
||||||
&& thisRunnable.environment == otherRunnable.environment;
|
|
||||||
});
|
});
|
||||||
if (tabIndex != -1) {
|
if (tabIndex != -1) {
|
||||||
RunControlTab &tab = m_runControlTabs[tabIndex];
|
RunControlTab &tab = m_runControlTabs[tabIndex];
|
||||||
@@ -433,7 +437,7 @@ void AppOutputPane::createNewOutputWindow(RunControl *rc)
|
|||||||
}
|
}
|
||||||
// Create new
|
// Create new
|
||||||
static int counter = 0;
|
static int counter = 0;
|
||||||
Utils::Id contextId = Utils::Id(C_APP_OUTPUT).withSuffix(counter++);
|
Id contextId = Id(C_APP_OUTPUT).withSuffix(counter++);
|
||||||
Core::Context context(contextId);
|
Core::Context context(contextId);
|
||||||
Core::OutputWindow *ow = new Core::OutputWindow(context, SETTINGS_KEY, m_tabWidget);
|
Core::OutputWindow *ow = new Core::OutputWindow(context, SETTINGS_KEY, m_tabWidget);
|
||||||
ow->setWindowTitle(tr("Application Output Window"));
|
ow->setWindowTitle(tr("Application Output Window"));
|
||||||
@@ -486,19 +490,19 @@ void AppOutputPane::updateFromSettings()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppOutputPane::appendMessage(RunControl *rc, const QString &out, Utils::OutputFormat format)
|
void AppOutputPane::appendMessage(RunControl *rc, const QString &out, OutputFormat format)
|
||||||
{
|
{
|
||||||
const int index = indexOf(rc);
|
const int index = indexOf(rc);
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
Core::OutputWindow *window = m_runControlTabs.at(index).window;
|
Core::OutputWindow *window = m_runControlTabs.at(index).window;
|
||||||
QString stringToWrite;
|
QString stringToWrite;
|
||||||
if (format == Utils::NormalMessageFormat || format == Utils::ErrorMessageFormat) {
|
if (format == NormalMessageFormat || format == ErrorMessageFormat) {
|
||||||
stringToWrite = QTime::currentTime().toString();
|
stringToWrite = QTime::currentTime().toString();
|
||||||
stringToWrite += ": ";
|
stringToWrite += ": ";
|
||||||
}
|
}
|
||||||
stringToWrite += out;
|
stringToWrite += out;
|
||||||
window->appendMessage(stringToWrite, format);
|
window->appendMessage(stringToWrite, format);
|
||||||
if (format != Utils::NormalMessageFormat) {
|
if (format != NormalMessageFormat) {
|
||||||
RunControlTab &tab = m_runControlTabs[index];
|
RunControlTab &tab = m_runControlTabs[index];
|
||||||
switch (tab.behaviorOnOutput) {
|
switch (tab.behaviorOnOutput) {
|
||||||
case AppOutputPaneMode::FlashOnOutput:
|
case AppOutputPaneMode::FlashOnOutput:
|
||||||
@@ -530,7 +534,7 @@ const bool kWrapOutputDefault = true;
|
|||||||
|
|
||||||
void AppOutputPane::storeSettings() const
|
void AppOutputPane::storeSettings() const
|
||||||
{
|
{
|
||||||
Utils::QtcSettings *const s = Core::ICore::settings();
|
QtcSettings *const s = Core::ICore::settings();
|
||||||
s->setValueWithDefault(POP_UP_FOR_RUN_OUTPUT_KEY,
|
s->setValueWithDefault(POP_UP_FOR_RUN_OUTPUT_KEY,
|
||||||
int(m_settings.runOutputMode),
|
int(m_settings.runOutputMode),
|
||||||
int(kRunOutputModeDefault));
|
int(kRunOutputModeDefault));
|
||||||
@@ -706,7 +710,7 @@ void AppOutputPane::enableButtons(const RunControl *rc)
|
|||||||
m_stopAction->setEnabled(isRunning);
|
m_stopAction->setEnabled(isRunning);
|
||||||
if (isRunning && debuggerPlugin() && rc->applicationProcessHandle().isValid()) {
|
if (isRunning && debuggerPlugin() && rc->applicationProcessHandle().isValid()) {
|
||||||
m_attachButton->setEnabled(true);
|
m_attachButton->setEnabled(true);
|
||||||
Utils::ProcessHandle h = rc->applicationProcessHandle();
|
ProcessHandle h = rc->applicationProcessHandle();
|
||||||
QString tip = h.isValid() ? RunControl::tr("PID %1").arg(h.pid())
|
QString tip = h.isValid() ? RunControl::tr("PID %1").arg(h.pid())
|
||||||
: RunControl::tr("Invalid");
|
: RunControl::tr("Invalid");
|
||||||
m_attachButton->setToolTip(msgAttachDebuggerTooltip(tip));
|
m_attachButton->setToolTip(msgAttachDebuggerTooltip(tip));
|
||||||
|
@@ -30,6 +30,7 @@
|
|||||||
#include "buildsystem.h"
|
#include "buildsystem.h"
|
||||||
#include "compileoutputwindow.h"
|
#include "compileoutputwindow.h"
|
||||||
#include "deployconfiguration.h"
|
#include "deployconfiguration.h"
|
||||||
|
#include "devicesupport/devicemanager.h"
|
||||||
#include "kit.h"
|
#include "kit.h"
|
||||||
#include "kitinformation.h"
|
#include "kitinformation.h"
|
||||||
#include "project.h"
|
#include "project.h"
|
||||||
@@ -46,8 +47,9 @@
|
|||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
#include <coreplugin/progressmanager/futureprogress.h>
|
#include <coreplugin/progressmanager/futureprogress.h>
|
||||||
#include <coreplugin/progressmanager/progressmanager.h>
|
#include <coreplugin/progressmanager/progressmanager.h>
|
||||||
|
|
||||||
#include <extensionsystem/pluginmanager.h>
|
#include <extensionsystem/pluginmanager.h>
|
||||||
#include <projectexplorer/devicesupport/idevice.h>
|
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
#include <utils/outputformatter.h>
|
#include <utils/outputformatter.h>
|
||||||
#include <utils/runextensions.h>
|
#include <utils/runextensions.h>
|
||||||
@@ -118,7 +120,8 @@ static int queue(const QList<Project *> &projects, const QList<Id> &stepIds,
|
|||||||
return projects.contains(rc->project());
|
return projects.contains(rc->project());
|
||||||
case StopBeforeBuild::SameBuildDir:
|
case StopBeforeBuild::SameBuildDir:
|
||||||
return Utils::contains(projects, [rc, configSelection](Project *p) {
|
return Utils::contains(projects, [rc, configSelection](Project *p) {
|
||||||
IDevice::ConstPtr device = rc->runnable().device;
|
const FilePath executable = rc->commandLine().executable();
|
||||||
|
IDevice::ConstPtr device = DeviceManager::deviceForPath(executable);
|
||||||
for (const Target * const t : targetsForSelection(p, configSelection)) {
|
for (const Target * const t : targetsForSelection(p, configSelection)) {
|
||||||
if (device.isNull())
|
if (device.isNull())
|
||||||
device = DeviceKitAspect::device(t->kit());
|
device = DeviceKitAspect::device(t->kit());
|
||||||
@@ -126,7 +129,7 @@ static int queue(const QList<Project *> &projects, const QList<Id> &stepIds,
|
|||||||
continue;
|
continue;
|
||||||
for (const BuildConfiguration * const bc
|
for (const BuildConfiguration * const bc
|
||||||
: buildConfigsForSelection(t, configSelection)) {
|
: buildConfigsForSelection(t, configSelection)) {
|
||||||
if (rc->runnable().command.executable().isChildOf(bc->buildDirectory()))
|
if (executable.isChildOf(bc->buildDirectory()))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -53,7 +53,7 @@ CustomExecutableRunConfiguration::CustomExecutableRunConfiguration(Target *targe
|
|||||||
exeAspect->setEnvironmentChange(EnvironmentChange::fromFixedEnvironment(envAspect->environment()));
|
exeAspect->setEnvironmentChange(EnvironmentChange::fromFixedEnvironment(envAspect->environment()));
|
||||||
|
|
||||||
addAspect<ArgumentsAspect>(macroExpander());
|
addAspect<ArgumentsAspect>(macroExpander());
|
||||||
addAspect<WorkingDirectoryAspect>(envAspect);
|
addAspect<WorkingDirectoryAspect>(macroExpander(), envAspect);
|
||||||
addAspect<TerminalAspect>();
|
addAspect<TerminalAspect>();
|
||||||
|
|
||||||
connect(envAspect, &EnvironmentAspect::environmentChanged, this, [exeAspect, envAspect] {
|
connect(envAspect, &EnvironmentAspect::environmentChanged, this, [exeAspect, envAspect] {
|
||||||
|
@@ -70,7 +70,7 @@ DesktopRunConfiguration::DesktopRunConfiguration(Target *target, Id id, Kind kin
|
|||||||
|
|
||||||
addAspect<ExecutableAspect>(target);
|
addAspect<ExecutableAspect>(target);
|
||||||
addAspect<ArgumentsAspect>(macroExpander());
|
addAspect<ArgumentsAspect>(macroExpander());
|
||||||
addAspect<WorkingDirectoryAspect>(envAspect);
|
addAspect<WorkingDirectoryAspect>(macroExpander(), envAspect);
|
||||||
addAspect<TerminalAspect>();
|
addAspect<TerminalAspect>();
|
||||||
|
|
||||||
auto libAspect = addAspect<UseLibraryPathsAspect>();
|
auto libAspect = addAspect<UseLibraryPathsAspect>();
|
||||||
|
227
src/plugins/projectexplorer/devicesupport/filetransfer.cpp
Normal file
227
src/plugins/projectexplorer/devicesupport/filetransfer.cpp
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "filetransfer.h"
|
||||||
|
|
||||||
|
#include "devicemanager.h"
|
||||||
|
#include "idevice.h"
|
||||||
|
|
||||||
|
#include <utils/processinterface.h>
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
|
#include <QProcess>
|
||||||
|
|
||||||
|
using namespace Utils;
|
||||||
|
|
||||||
|
namespace ProjectExplorer {
|
||||||
|
|
||||||
|
FileTransferDirection FileToTransfer::direction() const
|
||||||
|
{
|
||||||
|
if (m_source.needsDevice() == m_target.needsDevice())
|
||||||
|
return FileTransferDirection::Invalid;
|
||||||
|
return m_source.needsDevice() ? FileTransferDirection::Download : FileTransferDirection::Upload;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileTransferSetupData::defaultRsyncFlags()
|
||||||
|
{
|
||||||
|
return "-av";
|
||||||
|
}
|
||||||
|
|
||||||
|
static FileTransferDirection transferDirection(const FilesToTransfer &files)
|
||||||
|
{
|
||||||
|
if (files.isEmpty())
|
||||||
|
return FileTransferDirection::Invalid;
|
||||||
|
|
||||||
|
const FileTransferDirection direction = files.first().direction();
|
||||||
|
for (const FileToTransfer &file : files) {
|
||||||
|
if (file.direction() != direction)
|
||||||
|
return FileTransferDirection::Invalid;
|
||||||
|
}
|
||||||
|
return direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const FilePath &remoteFile(FileTransferDirection direction, const FileToTransfer &file)
|
||||||
|
{
|
||||||
|
return direction == FileTransferDirection::Upload ? file.m_target : file.m_source;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isSameDevice(const FilePath &first, const FilePath &second)
|
||||||
|
{
|
||||||
|
return (first.scheme() == second.scheme()) && (first.host() == second.host());
|
||||||
|
}
|
||||||
|
|
||||||
|
static IDeviceConstPtr matchedDevice(FileTransferDirection direction, const FilesToTransfer &files)
|
||||||
|
{
|
||||||
|
if (files.isEmpty())
|
||||||
|
return {};
|
||||||
|
const FilePath &filePath = remoteFile(direction, files.first());
|
||||||
|
for (const FileToTransfer &file : files) {
|
||||||
|
if (!isSameDevice(filePath, remoteFile(direction, file)))
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return DeviceManager::deviceForPath(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransferInterface::startFailed(const QString &errorString)
|
||||||
|
{
|
||||||
|
emit done({0, QProcess::NormalExit, QProcess::FailedToStart, errorString});
|
||||||
|
}
|
||||||
|
|
||||||
|
class FileTransferPrivate : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
void test(const ProjectExplorer::IDeviceConstPtr &onDevice);
|
||||||
|
void start();
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
FileTransferSetupData m_setup;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void progress(const QString &progressMessage);
|
||||||
|
void done(const ProcessResultData &resultData);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void startFailed(const QString &errorString);
|
||||||
|
void run(const FileTransferSetupData &setup, const IDeviceConstPtr &device);
|
||||||
|
|
||||||
|
std::unique_ptr<FileTransferInterface> m_transfer;
|
||||||
|
};
|
||||||
|
|
||||||
|
void FileTransferPrivate::test(const IDeviceConstPtr &onDevice)
|
||||||
|
{
|
||||||
|
if (!onDevice)
|
||||||
|
return startFailed(tr("No device set for test transfer."));
|
||||||
|
|
||||||
|
run({{}, m_setup.m_method, m_setup.m_rsyncFlags}, onDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransferPrivate::start()
|
||||||
|
{
|
||||||
|
if (m_setup.m_files.isEmpty())
|
||||||
|
return startFailed(tr("No files to transfer."));
|
||||||
|
|
||||||
|
const FileTransferDirection direction = transferDirection(m_setup.m_files);
|
||||||
|
if (direction == FileTransferDirection::Invalid)
|
||||||
|
return startFailed(tr("Mixing different types of transfer in one go."));
|
||||||
|
|
||||||
|
const IDeviceConstPtr device = matchedDevice(direction, m_setup.m_files);
|
||||||
|
if (!device)
|
||||||
|
return startFailed(tr("Trying to transfer into / from not matching device."));
|
||||||
|
|
||||||
|
run(m_setup, device);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransferPrivate::stop()
|
||||||
|
{
|
||||||
|
if (!m_transfer)
|
||||||
|
return;
|
||||||
|
m_transfer->disconnect();
|
||||||
|
m_transfer.release()->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransferPrivate::run(const FileTransferSetupData &setup, const IDeviceConstPtr &device)
|
||||||
|
{
|
||||||
|
stop();
|
||||||
|
|
||||||
|
m_transfer.reset(device->createFileTransferInterface(setup));
|
||||||
|
QTC_ASSERT(m_transfer, startFailed(tr("Missing transfer implementation.")); return);
|
||||||
|
|
||||||
|
m_transfer->setParent(this);
|
||||||
|
connect(m_transfer.get(), &FileTransferInterface::progress,
|
||||||
|
this, &FileTransferPrivate::progress);
|
||||||
|
connect(m_transfer.get(), &FileTransferInterface::done,
|
||||||
|
this, &FileTransferPrivate::done);
|
||||||
|
m_transfer->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransferPrivate::startFailed(const QString &errorString)
|
||||||
|
{
|
||||||
|
emit done({0, QProcess::NormalExit, QProcess::FailedToStart, errorString});
|
||||||
|
}
|
||||||
|
|
||||||
|
FileTransfer::FileTransfer()
|
||||||
|
: d(new FileTransferPrivate)
|
||||||
|
{
|
||||||
|
d->setParent(this);
|
||||||
|
connect(d, &FileTransferPrivate::progress, this, &FileTransfer::progress);
|
||||||
|
connect(d, &FileTransferPrivate::done, this, &FileTransfer::done);
|
||||||
|
}
|
||||||
|
|
||||||
|
FileTransfer::~FileTransfer()
|
||||||
|
{
|
||||||
|
stop();
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransfer::setFilesToTransfer(const FilesToTransfer &files)
|
||||||
|
{
|
||||||
|
d->m_setup.m_files = files;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransfer::setTransferMethod(FileTransferMethod method)
|
||||||
|
{
|
||||||
|
d->m_setup.m_method = method;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransfer::setRsyncFlags(const QString &flags)
|
||||||
|
{
|
||||||
|
d->m_setup.m_rsyncFlags = flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransfer::test(const ProjectExplorer::IDeviceConstPtr &onDevice)
|
||||||
|
{
|
||||||
|
d->test(onDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
FileTransferMethod FileTransfer::transferMethod() const
|
||||||
|
{
|
||||||
|
return d->m_setup.m_method;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransfer::start()
|
||||||
|
{
|
||||||
|
d->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransfer::stop()
|
||||||
|
{
|
||||||
|
d->stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileTransfer::transferMethodName(FileTransferMethod method)
|
||||||
|
{
|
||||||
|
switch (method) {
|
||||||
|
case FileTransferMethod::Sftp: return FileTransfer::tr("sftp");
|
||||||
|
case FileTransferMethod::Rsync: return FileTransfer::tr("rsync");
|
||||||
|
}
|
||||||
|
QTC_CHECK(false);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ProjectExplorer
|
||||||
|
|
||||||
|
#include "filetransfer.moc"
|
@@ -25,40 +25,17 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "remotelinux_export.h"
|
#include "../projectexplorer_export.h"
|
||||||
|
#include "filetransferinterface.h"
|
||||||
#include <projectexplorer/devicesupport/idevicefwd.h>
|
#include "idevicefwd.h"
|
||||||
|
|
||||||
#include <utils/filepath.h>
|
|
||||||
|
|
||||||
namespace Utils { class ProcessResultData; }
|
namespace Utils { class ProcessResultData; }
|
||||||
|
|
||||||
namespace RemoteLinux {
|
namespace ProjectExplorer {
|
||||||
|
|
||||||
enum class TransferDirection {
|
|
||||||
Upload,
|
|
||||||
Download,
|
|
||||||
Invalid
|
|
||||||
};
|
|
||||||
|
|
||||||
class REMOTELINUX_EXPORT FileToTransfer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Utils::FilePath m_source;
|
|
||||||
Utils::FilePath m_target;
|
|
||||||
TransferDirection transferDirection() const;
|
|
||||||
};
|
|
||||||
using FilesToTransfer = QList<FileToTransfer>;
|
|
||||||
|
|
||||||
enum class FileTransferMethod {
|
|
||||||
Sftp,
|
|
||||||
Rsync,
|
|
||||||
Default = Sftp
|
|
||||||
};
|
|
||||||
|
|
||||||
class FileTransferPrivate;
|
class FileTransferPrivate;
|
||||||
|
|
||||||
class REMOTELINUX_EXPORT FileTransfer : public QObject
|
class PROJECTEXPLORER_EXPORT FileTransfer : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@@ -66,19 +43,17 @@ public:
|
|||||||
FileTransfer();
|
FileTransfer();
|
||||||
~FileTransfer();
|
~FileTransfer();
|
||||||
|
|
||||||
void setDevice(const ProjectExplorer::IDeviceConstPtr &device);
|
|
||||||
void setTransferMethod(FileTransferMethod method);
|
|
||||||
void setFilesToTransfer(const FilesToTransfer &files);
|
void setFilesToTransfer(const FilesToTransfer &files);
|
||||||
|
void setTransferMethod(FileTransferMethod method);
|
||||||
void setRsyncFlags(const QString &flags);
|
void setRsyncFlags(const QString &flags);
|
||||||
|
|
||||||
FileTransferMethod transferMethod() const;
|
FileTransferMethod transferMethod() const;
|
||||||
|
|
||||||
void test();
|
void test(const ProjectExplorer::IDeviceConstPtr &onDevice);
|
||||||
void start();
|
void start();
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
static QString transferMethodName(FileTransferMethod method);
|
static QString transferMethodName(FileTransferMethod method);
|
||||||
static QString defaultRsyncFlags();
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void progress(const QString &progressMessage);
|
void progress(const QString &progressMessage);
|
||||||
@@ -88,6 +63,4 @@ private:
|
|||||||
FileTransferPrivate *d;
|
FileTransferPrivate *d;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace RemoteLinux
|
} // namespace ProjectExplorer
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(RemoteLinux::FileTransferMethod)
|
|
@@ -0,0 +1,93 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../projectexplorer_export.h"
|
||||||
|
|
||||||
|
#include <utils/filepath.h>
|
||||||
|
|
||||||
|
namespace Utils { class ProcessResultData; }
|
||||||
|
|
||||||
|
namespace ProjectExplorer {
|
||||||
|
|
||||||
|
enum class FileTransferDirection {
|
||||||
|
Invalid,
|
||||||
|
Upload,
|
||||||
|
Download
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class FileTransferMethod {
|
||||||
|
Sftp,
|
||||||
|
Rsync,
|
||||||
|
Default = Sftp
|
||||||
|
};
|
||||||
|
|
||||||
|
class PROJECTEXPLORER_EXPORT FileToTransfer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Utils::FilePath m_source;
|
||||||
|
Utils::FilePath m_target;
|
||||||
|
|
||||||
|
FileTransferDirection direction() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
using FilesToTransfer = QList<FileToTransfer>;
|
||||||
|
|
||||||
|
class PROJECTEXPLORER_EXPORT FileTransferSetupData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FilesToTransfer m_files; // When empty, do test instead of a real transfer
|
||||||
|
FileTransferMethod m_method = FileTransferMethod::Default;
|
||||||
|
QString m_rsyncFlags = defaultRsyncFlags();
|
||||||
|
|
||||||
|
static QString defaultRsyncFlags();
|
||||||
|
};
|
||||||
|
|
||||||
|
class PROJECTEXPLORER_EXPORT FileTransferInterface : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void progress(const QString &progressMessage);
|
||||||
|
void done(const Utils::ProcessResultData &resultData);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
FileTransferInterface(const FileTransferSetupData &setupData)
|
||||||
|
: m_setup(setupData) {}
|
||||||
|
|
||||||
|
void startFailed(const QString &errorString);
|
||||||
|
|
||||||
|
const FileTransferSetupData m_setup;
|
||||||
|
|
||||||
|
private:
|
||||||
|
FileTransferInterface() = delete;
|
||||||
|
|
||||||
|
virtual void start() = 0;
|
||||||
|
|
||||||
|
friend class FileTransferPrivate;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ProjectExplorer
|
@@ -434,6 +434,14 @@ ProcessInterface *IDevice::createProcessInterface() const
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FileTransferInterface *IDevice::createFileTransferInterface(
|
||||||
|
const FileTransferSetupData &setup) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(setup)
|
||||||
|
QTC_CHECK(false);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
Environment IDevice::systemEnvironment() const
|
Environment IDevice::systemEnvironment() const
|
||||||
{
|
{
|
||||||
QTC_CHECK(false);
|
QTC_CHECK(false);
|
||||||
|
@@ -60,6 +60,8 @@ class QtcProcess;
|
|||||||
namespace ProjectExplorer {
|
namespace ProjectExplorer {
|
||||||
|
|
||||||
class DeviceProcessList;
|
class DeviceProcessList;
|
||||||
|
class FileTransferInterface;
|
||||||
|
class FileTransferSetupData;
|
||||||
class Kit;
|
class Kit;
|
||||||
class SshParameters;
|
class SshParameters;
|
||||||
class Task;
|
class Task;
|
||||||
@@ -265,6 +267,8 @@ public:
|
|||||||
virtual QFile::Permissions permissions(const Utils::FilePath &filePath) const;
|
virtual QFile::Permissions permissions(const Utils::FilePath &filePath) const;
|
||||||
virtual bool setPermissions(const Utils::FilePath &filePath, QFile::Permissions) const;
|
virtual bool setPermissions(const Utils::FilePath &filePath, QFile::Permissions) const;
|
||||||
virtual Utils::ProcessInterface *createProcessInterface() const;
|
virtual Utils::ProcessInterface *createProcessInterface() const;
|
||||||
|
virtual FileTransferInterface *createFileTransferInterface(
|
||||||
|
const FileTransferSetupData &setup) const;
|
||||||
virtual Utils::Environment systemEnvironment() const;
|
virtual Utils::Environment systemEnvironment() const;
|
||||||
virtual qint64 fileSize(const Utils::FilePath &filePath) const;
|
virtual qint64 fileSize(const Utils::FilePath &filePath) const;
|
||||||
virtual qint64 bytesAvailable(const Utils::FilePath &filePath) const;
|
virtual qint64 bytesAvailable(const Utils::FilePath &filePath) const;
|
||||||
|
@@ -64,24 +64,21 @@ QStringList SshParameters::connectionOptions(const FilePath &binary) const
|
|||||||
"-o", "Port=" + QString::number(port())};
|
"-o", "Port=" + QString::number(port())};
|
||||||
|
|
||||||
if (!userName().isEmpty())
|
if (!userName().isEmpty())
|
||||||
args.append({"-o", "User=" + userName()});
|
args << "-o" << "User=" + userName();
|
||||||
|
|
||||||
const bool keyOnly = authenticationType ==
|
const bool keyOnly = authenticationType == SshParameters::AuthenticationTypeSpecificKey;
|
||||||
SshParameters::AuthenticationTypeSpecificKey;
|
if (keyOnly)
|
||||||
if (keyOnly) {
|
args << "-o" << "IdentitiesOnly=yes" << "-i" << privateKeyFile.path();
|
||||||
args << "-o" << "IdentitiesOnly=yes";
|
|
||||||
args << "-i" << privateKeyFile.path();
|
|
||||||
}
|
|
||||||
if (keyOnly || SshSettings::askpassFilePath().isEmpty())
|
|
||||||
args << "-o" << "BatchMode=yes";
|
|
||||||
|
|
||||||
bool useTimeout = timeout != 0;
|
const QString batchModeEnabled = (keyOnly || SshSettings::askpassFilePath().isEmpty())
|
||||||
if (useTimeout && HostOsInfo::isWindowsHost()
|
? QLatin1String("yes") : QLatin1String("no");
|
||||||
&& binary.toString().toLower().contains("/system32/")) {
|
args << "-o" << "BatchMode=" + batchModeEnabled;
|
||||||
useTimeout = false;
|
|
||||||
}
|
const bool isWindows = HostOsInfo::isWindowsHost()
|
||||||
|
&& binary.toString().toLower().contains("/system32/");
|
||||||
|
const bool useTimeout = (timeout != 0) && !isWindows;
|
||||||
if (useTimeout)
|
if (useTimeout)
|
||||||
args << "-o" << ("ConnectTimeout=" + QString::number(timeout));
|
args << "-o" << "ConnectTimeout=" + QString::number(timeout);
|
||||||
|
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
@@ -1110,6 +1110,13 @@ void DeviceKitAspect::setDeviceId(Kit *k, Utils::Id id)
|
|||||||
k->setValue(DeviceKitAspect::id(), id.toSetting());
|
k->setValue(DeviceKitAspect::id(), id.toSetting());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FilePath DeviceKitAspect::deviceFilePath(const Kit *k, const QString &pathOnDevice)
|
||||||
|
{
|
||||||
|
if (IDevice::ConstPtr dev = device(k))
|
||||||
|
return dev->filePath(pathOnDevice);
|
||||||
|
return FilePath::fromString(pathOnDevice);
|
||||||
|
}
|
||||||
|
|
||||||
void DeviceKitAspect::kitsWereLoaded()
|
void DeviceKitAspect::kitsWereLoaded()
|
||||||
{
|
{
|
||||||
const QList<Kit *> kits = KitManager::kits();
|
const QList<Kit *> kits = KitManager::kits();
|
||||||
|
@@ -161,6 +161,7 @@ public:
|
|||||||
static Utils::Id deviceId(const Kit *k);
|
static Utils::Id deviceId(const Kit *k);
|
||||||
static void setDevice(Kit *k, IDeviceConstPtr dev);
|
static void setDevice(Kit *k, IDeviceConstPtr dev);
|
||||||
static void setDeviceId(Kit *k, Utils::Id dataId);
|
static void setDeviceId(Kit *k, Utils::Id dataId);
|
||||||
|
static Utils::FilePath deviceFilePath(const Kit *k, const QString &pathOnDevice);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QVariant defaultValue(const Kit *k) const;
|
QVariant defaultValue(const Kit *k) const;
|
||||||
|
@@ -158,12 +158,23 @@ static bool hostSupportsPlatform(MsvcToolChain::Platform platform)
|
|||||||
{
|
{
|
||||||
if (hostPrefersPlatform(platform))
|
if (hostPrefersPlatform(platform))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
switch (HostOsInfo::hostArchitecture()) {
|
||||||
// The x86 host toolchains are not the preferred toolchains on amd64 but they are still
|
// The x86 host toolchains are not the preferred toolchains on amd64 but they are still
|
||||||
// supported by that host
|
// supported by that host
|
||||||
return HostOsInfo::hostArchitecture() == HostOsInfo::HostArchitectureAMD64
|
case HostOsInfo::HostArchitectureAMD64:
|
||||||
&& (platform == MsvcToolChain::x86 || platform == MsvcToolChain::x86_amd64
|
return platform == MsvcToolChain::x86 || platform == MsvcToolChain::x86_amd64
|
||||||
|| platform == MsvcToolChain::x86_ia64 || platform == MsvcToolChain::x86_arm
|
|| platform == MsvcToolChain::x86_ia64 || platform == MsvcToolChain::x86_arm
|
||||||
|| platform == MsvcToolChain::x86_arm64);
|
|| platform == MsvcToolChain::x86_arm64;
|
||||||
|
// The Arm64 host can run the cross-compilers via emulation of x86 and amd64
|
||||||
|
case HostOsInfo::HostArchitectureArm:
|
||||||
|
return platform == MsvcToolChain::x86_arm || platform == MsvcToolChain::x86_arm64
|
||||||
|
|| platform == MsvcToolChain::amd64_arm || platform == MsvcToolChain::amd64_arm64
|
||||||
|
|| platform == MsvcToolChain::x86 || platform == MsvcToolChain::x86_amd64
|
||||||
|
|| platform == MsvcToolChain::amd64 || platform == MsvcToolChain::amd64_x86;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString fixRegistryPath(const QString &path)
|
static QString fixRegistryPath(const QString &path)
|
||||||
|
@@ -333,7 +333,8 @@ static bool canOpenTerminalWithRunEnv(const Project *project, const ProjectNode
|
|||||||
const RunConfiguration * const runConfig = runConfigForNode(target, node);
|
const RunConfiguration * const runConfig = runConfigForNode(target, node);
|
||||||
if (!runConfig)
|
if (!runConfig)
|
||||||
return false;
|
return false;
|
||||||
IDevice::ConstPtr device = runConfig->runnable().device;
|
IDevice::ConstPtr device
|
||||||
|
= DeviceManager::deviceForPath(runConfig->runnable().command.executable());
|
||||||
if (!device)
|
if (!device)
|
||||||
device = DeviceKitAspect::device(target->kit());
|
device = DeviceKitAspect::device(target->kit());
|
||||||
return device && device->canOpenTerminal();
|
return device && device->canOpenTerminal();
|
||||||
@@ -3245,13 +3246,13 @@ void ProjectExplorerPlugin::runRunConfiguration(RunConfiguration *rc,
|
|||||||
dd->doUpdateRunActions();
|
dd->doUpdateRunActions();
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QPair<Runnable, ProcessHandle>> ProjectExplorerPlugin::runningRunControlProcesses()
|
QList<QPair<CommandLine, ProcessHandle>> ProjectExplorerPlugin::runningRunControlProcesses()
|
||||||
{
|
{
|
||||||
QList<QPair<Runnable, ProcessHandle>> processes;
|
QList<QPair<CommandLine, ProcessHandle>> processes;
|
||||||
const QList<RunControl *> runControls = allRunControls();
|
const QList<RunControl *> runControls = allRunControls();
|
||||||
for (RunControl *rc : runControls) {
|
for (RunControl *rc : runControls) {
|
||||||
if (rc->isRunning())
|
if (rc->isRunning())
|
||||||
processes << qMakePair(rc->runnable(), rc->applicationProcessHandle());
|
processes << qMakePair(rc->commandLine(), rc->applicationProcessHandle());
|
||||||
}
|
}
|
||||||
return processes;
|
return processes;
|
||||||
}
|
}
|
||||||
@@ -3935,7 +3936,7 @@ void ProjectExplorerPluginPrivate::openTerminalHereWithRunEnv()
|
|||||||
QTC_ASSERT(runConfig, return);
|
QTC_ASSERT(runConfig, return);
|
||||||
|
|
||||||
const Runnable runnable = runConfig->runnable();
|
const Runnable runnable = runConfig->runnable();
|
||||||
IDevice::ConstPtr device = runnable.device;
|
IDevice::ConstPtr device = DeviceManager::deviceForPath(runnable.command.executable());
|
||||||
if (!device)
|
if (!device)
|
||||||
device = DeviceKitAspect::device(target->kit());
|
device = DeviceKitAspect::device(target->kit());
|
||||||
QTC_ASSERT(device && device->canOpenTerminal(), return);
|
QTC_ASSERT(device && device->canOpenTerminal(), return);
|
||||||
|
@@ -164,7 +164,7 @@ public:
|
|||||||
static void runStartupProject(Utils::Id runMode, bool forceSkipDeploy = false);
|
static void runStartupProject(Utils::Id runMode, bool forceSkipDeploy = false);
|
||||||
static void runRunConfiguration(RunConfiguration *rc, Utils::Id runMode,
|
static void runRunConfiguration(RunConfiguration *rc, Utils::Id runMode,
|
||||||
const bool forceSkipDeploy = false);
|
const bool forceSkipDeploy = false);
|
||||||
static QList<QPair<Runnable, Utils::ProcessHandle>> runningRunControlProcesses();
|
static QList<QPair<Utils::CommandLine, Utils::ProcessHandle>> runningRunControlProcesses();
|
||||||
static QList<RunControl *> allRunControls();
|
static QList<RunControl *> allRunControls();
|
||||||
|
|
||||||
static void addExistingFiles(FolderNode *folderNode, const Utils::FilePaths &filePaths);
|
static void addExistingFiles(FolderNode *folderNode, const Utils::FilePaths &filePaths);
|
||||||
|
@@ -219,6 +219,8 @@ Project {
|
|||||||
"devicesettingswidget.cpp", "devicesettingswidget.h", "devicesettingswidget.ui",
|
"devicesettingswidget.cpp", "devicesettingswidget.h", "devicesettingswidget.ui",
|
||||||
"devicetestdialog.cpp", "devicetestdialog.h", "devicetestdialog.ui",
|
"devicetestdialog.cpp", "devicetestdialog.h", "devicetestdialog.ui",
|
||||||
"deviceusedportsgatherer.cpp", "deviceusedportsgatherer.h",
|
"deviceusedportsgatherer.cpp", "deviceusedportsgatherer.h",
|
||||||
|
"filetransfer.cpp", "filetransfer.h",
|
||||||
|
"filetransferinterface.h",
|
||||||
"idevice.cpp", "idevice.h",
|
"idevice.cpp", "idevice.h",
|
||||||
"idevicefactory.cpp", "idevicefactory.h",
|
"idevicefactory.cpp", "idevicefactory.h",
|
||||||
"idevicefwd.h",
|
"idevicefwd.h",
|
||||||
|
@@ -171,8 +171,9 @@ bool TerminalAspect::isUserSet() const
|
|||||||
working directory for running the executable.
|
working directory for running the executable.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
WorkingDirectoryAspect::WorkingDirectoryAspect(EnvironmentAspect *envAspect)
|
WorkingDirectoryAspect::WorkingDirectoryAspect(const MacroExpander *expander,
|
||||||
: m_envAspect(envAspect)
|
EnvironmentAspect *envAspect)
|
||||||
|
: m_envAspect(envAspect), m_macroExpander(expander)
|
||||||
{
|
{
|
||||||
setDisplayName(tr("Working Directory"));
|
setDisplayName(tr("Working Directory"));
|
||||||
setId("WorkingDirectoryAspect");
|
setId("WorkingDirectoryAspect");
|
||||||
|
@@ -81,7 +81,8 @@ class PROJECTEXPLORER_EXPORT WorkingDirectoryAspect : public Utils::BaseAspect
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit WorkingDirectoryAspect(EnvironmentAspect *envAspect);
|
explicit WorkingDirectoryAspect(const Utils::MacroExpander *expander,
|
||||||
|
EnvironmentAspect *envAspect);
|
||||||
|
|
||||||
void addToLayout(Utils::LayoutBuilder &builder) override;
|
void addToLayout(Utils::LayoutBuilder &builder) override;
|
||||||
|
|
||||||
@@ -102,7 +103,7 @@ private:
|
|||||||
Utils::FilePath m_defaultWorkingDirectory;
|
Utils::FilePath m_defaultWorkingDirectory;
|
||||||
QPointer<Utils::PathChooser> m_chooser;
|
QPointer<Utils::PathChooser> m_chooser;
|
||||||
QPointer<QToolButton> m_resetButton;
|
QPointer<QToolButton> m_resetButton;
|
||||||
Utils::MacroExpander *m_macroExpander = nullptr;
|
const Utils::MacroExpander *m_macroExpander = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PROJECTEXPLORER_EXPORT ArgumentsAspect : public Utils::BaseAspect
|
class PROJECTEXPLORER_EXPORT ArgumentsAspect : public Utils::BaseAspect
|
||||||
|
@@ -28,6 +28,7 @@
|
|||||||
#include "buildconfiguration.h"
|
#include "buildconfiguration.h"
|
||||||
#include "customparser.h"
|
#include "customparser.h"
|
||||||
#include "devicesupport/desktopdevice.h"
|
#include "devicesupport/desktopdevice.h"
|
||||||
|
#include "devicesupport/devicemanager.h"
|
||||||
#include "devicesupport/idevice.h"
|
#include "devicesupport/idevice.h"
|
||||||
#include "devicesupport/sshsettings.h"
|
#include "devicesupport/sshsettings.h"
|
||||||
#include "kitinformation.h"
|
#include "kitinformation.h"
|
||||||
@@ -323,7 +324,6 @@ public:
|
|||||||
QMap<Utils::Id, QVariantMap> settingsData;
|
QMap<Utils::Id, QVariantMap> settingsData;
|
||||||
Utils::Id runConfigId;
|
Utils::Id runConfigId;
|
||||||
BuildTargetInfo buildTargetInfo;
|
BuildTargetInfo buildTargetInfo;
|
||||||
BuildConfiguration::BuildType buildType = BuildConfiguration::Unknown;
|
|
||||||
FilePath buildDirectory;
|
FilePath buildDirectory;
|
||||||
Environment buildEnvironment;
|
Environment buildEnvironment;
|
||||||
Kit *kit = nullptr; // Not owned.
|
Kit *kit = nullptr; // Not owned.
|
||||||
@@ -430,7 +430,6 @@ void RunControl::setTarget(Target *target)
|
|||||||
d->buildTargetInfo = target->buildTarget(d->buildKey);
|
d->buildTargetInfo = target->buildTarget(d->buildKey);
|
||||||
|
|
||||||
if (auto bc = target->activeBuildConfiguration()) {
|
if (auto bc = target->activeBuildConfiguration()) {
|
||||||
d->buildType = bc->buildType();
|
|
||||||
d->buildDirectory = bc->buildDirectory();
|
d->buildDirectory = bc->buildDirectory();
|
||||||
d->buildEnvironment = bc->environment();
|
d->buildEnvironment = bc->environment();
|
||||||
}
|
}
|
||||||
@@ -447,8 +446,8 @@ void RunControl::setKit(Kit *kit)
|
|||||||
d->kit = kit;
|
d->kit = kit;
|
||||||
d->macroExpander = kit->macroExpander();
|
d->macroExpander = kit->macroExpander();
|
||||||
|
|
||||||
if (d->runnable.device)
|
if (!d->runnable.command.isEmpty())
|
||||||
setDevice(d->runnable.device);
|
setDevice(DeviceManager::deviceForPath(d->runnable.command.executable()));
|
||||||
else
|
else
|
||||||
setDevice(DeviceKitAspect::device(kit));
|
setDevice(DeviceKitAspect::device(kit));
|
||||||
}
|
}
|
||||||
@@ -905,13 +904,50 @@ const Runnable &RunControl::runnable() const
|
|||||||
return d->runnable;
|
return d->runnable;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RunControl::setRunnable(const Runnable &runnable)
|
const CommandLine &RunControl::commandLine() const
|
||||||
{
|
{
|
||||||
d->runnable = runnable;
|
return d->runnable.command;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RunControl::setCommandLine(const CommandLine &command)
|
||||||
|
{
|
||||||
|
d->runnable.command = command;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FilePath &RunControl::workingDirectory() const
|
||||||
|
{
|
||||||
|
return d->runnable.workingDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RunControl::setWorkingDirectory(const FilePath &workingDirectory)
|
||||||
|
{
|
||||||
|
d->runnable.workingDirectory = workingDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Environment &RunControl::environment() const
|
||||||
|
{
|
||||||
|
return d->runnable.environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RunControl::setEnvironment(const Environment &environment)
|
||||||
|
{
|
||||||
|
d->runnable.environment = environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QVariantHash &RunControl::extraData() const
|
||||||
|
{
|
||||||
|
return d->runnable.extraData;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RunControl::setExtraData(const QVariantHash &extraData)
|
||||||
|
{
|
||||||
|
d->runnable.extraData = extraData;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString RunControl::displayName() const
|
QString RunControl::displayName() const
|
||||||
{
|
{
|
||||||
|
if (d->displayName.isEmpty())
|
||||||
|
return d->runnable.command.executable().toUserOutput();
|
||||||
return d->displayName;
|
return d->displayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -975,11 +1011,6 @@ QString RunControl::buildKey() const
|
|||||||
return d->buildKey;
|
return d->buildKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
BuildConfiguration::BuildType RunControl::buildType() const
|
|
||||||
{
|
|
||||||
return d->buildType;
|
|
||||||
}
|
|
||||||
|
|
||||||
FilePath RunControl::buildDirectory() const
|
FilePath RunControl::buildDirectory() const
|
||||||
{
|
{
|
||||||
return d->buildDirectory;
|
return d->buildDirectory;
|
||||||
@@ -1242,7 +1273,10 @@ public:
|
|||||||
State m_state = Inactive;
|
State m_state = Inactive;
|
||||||
bool m_stopRequested = false;
|
bool m_stopRequested = false;
|
||||||
|
|
||||||
Runnable m_runnable;
|
Utils::CommandLine m_command;
|
||||||
|
Utils::FilePath m_workingDirectory;
|
||||||
|
Utils::Environment m_environment;
|
||||||
|
QVariantHash m_extraData;
|
||||||
|
|
||||||
ProcessResultData m_resultData;
|
ProcessResultData m_resultData;
|
||||||
|
|
||||||
@@ -1398,12 +1432,12 @@ void SimpleTargetRunnerPrivate::handleStandardError()
|
|||||||
|
|
||||||
void SimpleTargetRunnerPrivate::start()
|
void SimpleTargetRunnerPrivate::start()
|
||||||
{
|
{
|
||||||
m_isLocal = m_runnable.device.isNull() || m_runnable.device.dynamicCast<const DesktopDevice>();
|
m_isLocal = !m_command.executable().needsDevice();
|
||||||
|
|
||||||
m_resultData = {};
|
m_resultData = {};
|
||||||
|
|
||||||
if (m_isLocal) {
|
if (m_isLocal) {
|
||||||
Environment env = m_runnable.environment;
|
Environment env = m_environment;
|
||||||
if (m_runAsRoot)
|
if (m_runAsRoot)
|
||||||
RunControl::provideAskPassEntry(env);
|
RunControl::provideAskPassEntry(env);
|
||||||
|
|
||||||
@@ -1413,7 +1447,7 @@ void SimpleTargetRunnerPrivate::start()
|
|||||||
|
|
||||||
WinDebugInterface::startIfNeeded();
|
WinDebugInterface::startIfNeeded();
|
||||||
|
|
||||||
CommandLine cmdLine = m_runnable.command;
|
CommandLine cmdLine = m_command;
|
||||||
|
|
||||||
if (HostOsInfo::isMacHost()) {
|
if (HostOsInfo::isMacHost()) {
|
||||||
CommandLine disclaim(Core::ICore::libexecPath("disclaim"));
|
CommandLine disclaim(Core::ICore::libexecPath("disclaim"));
|
||||||
@@ -1426,7 +1460,8 @@ void SimpleTargetRunnerPrivate::start()
|
|||||||
} else {
|
} else {
|
||||||
QTC_ASSERT(m_state == Inactive, return);
|
QTC_ASSERT(m_state == Inactive, return);
|
||||||
|
|
||||||
if (!m_runnable.device) {
|
const IDevice::ConstPtr device = DeviceManager::deviceForPath(m_command.executable());
|
||||||
|
if (!device) {
|
||||||
m_resultData.m_errorString = tr("Cannot run: No device.");
|
m_resultData.m_errorString = tr("Cannot run: No device.");
|
||||||
m_resultData.m_error = QProcess::FailedToStart;
|
m_resultData.m_error = QProcess::FailedToStart;
|
||||||
m_resultData.m_exitStatus = QProcess::CrashExit;
|
m_resultData.m_exitStatus = QProcess::CrashExit;
|
||||||
@@ -1434,7 +1469,7 @@ void SimpleTargetRunnerPrivate::start()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_runnable.device->isEmptyCommandAllowed() && m_runnable.command.isEmpty()) {
|
if (!device->isEmptyCommandAllowed() && m_command.isEmpty()) {
|
||||||
m_resultData.m_errorString = tr("Cannot run: No command given.");
|
m_resultData.m_errorString = tr("Cannot run: No command given.");
|
||||||
m_resultData.m_error = QProcess::FailedToStart;
|
m_resultData.m_error = QProcess::FailedToStart;
|
||||||
m_resultData.m_exitStatus = QProcess::CrashExit;
|
m_resultData.m_exitStatus = QProcess::CrashExit;
|
||||||
@@ -1445,12 +1480,12 @@ void SimpleTargetRunnerPrivate::start()
|
|||||||
m_state = Run;
|
m_state = Run;
|
||||||
m_stopRequested = false;
|
m_stopRequested = false;
|
||||||
|
|
||||||
m_process.setCommand(m_runnable.command);
|
m_process.setCommand(m_command);
|
||||||
m_process.setEnvironment(m_runnable.environment);
|
m_process.setEnvironment(m_environment);
|
||||||
m_process.setExtraData(m_runnable.extraData);
|
m_process.setExtraData(m_extraData);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_process.setWorkingDirectory(m_runnable.workingDirectory);
|
m_process.setWorkingDirectory(m_workingDirectory);
|
||||||
|
|
||||||
if (m_isLocal)
|
if (m_isLocal)
|
||||||
m_outputCodec = QTextCodec::codecForLocale();
|
m_outputCodec = QTextCodec::codecForLocale();
|
||||||
@@ -1485,14 +1520,14 @@ void SimpleTargetRunnerPrivate::forwardDone()
|
|||||||
{
|
{
|
||||||
if (m_stopReported)
|
if (m_stopReported)
|
||||||
return;
|
return;
|
||||||
const QString executable = m_runnable.command.executable().toUserOutput();
|
const QString executable = m_command.executable().toUserOutput();
|
||||||
QString msg = tr("%1 exited with code %2").arg(executable).arg(m_resultData.m_exitCode);
|
QString msg = tr("%1 exited with code %2").arg(executable).arg(m_resultData.m_exitCode);
|
||||||
if (m_resultData.m_exitStatus == QProcess::CrashExit)
|
if (m_resultData.m_exitStatus == QProcess::CrashExit)
|
||||||
msg = tr("%1 crashed.").arg(executable);
|
msg = tr("%1 crashed.").arg(executable);
|
||||||
else if (m_stopForced)
|
else if (m_stopForced)
|
||||||
msg = tr("The process was ended forcefully.");
|
msg = tr("The process was ended forcefully.");
|
||||||
else if (m_resultData.m_error != QProcess::UnknownError)
|
else if (m_resultData.m_error != QProcess::UnknownError)
|
||||||
msg = RunWorker::userMessageForProcessError(m_resultData.m_error, m_runnable.command.executable());
|
msg = RunWorker::userMessageForProcessError(m_resultData.m_error, m_command.executable());
|
||||||
q->appendMessage(msg, NormalMessageFormat);
|
q->appendMessage(msg, NormalMessageFormat);
|
||||||
m_stopReported = true;
|
m_stopReported = true;
|
||||||
q->reportStopped();
|
q->reportStopped();
|
||||||
@@ -1500,8 +1535,7 @@ void SimpleTargetRunnerPrivate::forwardDone()
|
|||||||
|
|
||||||
void SimpleTargetRunnerPrivate::forwardStarted()
|
void SimpleTargetRunnerPrivate::forwardStarted()
|
||||||
{
|
{
|
||||||
const bool isDesktop = m_runnable.device.isNull()
|
const bool isDesktop = !m_command.executable().needsDevice();
|
||||||
|| m_runnable.device.dynamicCast<const DesktopDevice>();
|
|
||||||
if (isDesktop) {
|
if (isDesktop) {
|
||||||
// Console processes only know their pid after being started
|
// Console processes only know their pid after being started
|
||||||
ProcessHandle pid{privateApplicationPID()};
|
ProcessHandle pid{privateApplicationPID()};
|
||||||
@@ -1514,8 +1548,10 @@ void SimpleTargetRunnerPrivate::forwardStarted()
|
|||||||
|
|
||||||
void SimpleTargetRunner::start()
|
void SimpleTargetRunner::start()
|
||||||
{
|
{
|
||||||
d->m_runnable = runControl()->runnable();
|
d->m_command = runControl()->commandLine();
|
||||||
d->m_runnable.device = runControl()->device();
|
d->m_workingDirectory = runControl()->workingDirectory();
|
||||||
|
d->m_environment = runControl()->environment();
|
||||||
|
d->m_extraData = runControl()->extraData();
|
||||||
|
|
||||||
if (d->m_startModifier)
|
if (d->m_startModifier)
|
||||||
d->m_startModifier();
|
d->m_startModifier();
|
||||||
@@ -1534,12 +1570,11 @@ void SimpleTargetRunner::start()
|
|||||||
d->m_process.setTerminalMode(useTerminal ? Utils::TerminalMode::On : Utils::TerminalMode::Off);
|
d->m_process.setTerminalMode(useTerminal ? Utils::TerminalMode::On : Utils::TerminalMode::Off);
|
||||||
d->m_runAsRoot = runAsRoot;
|
d->m_runAsRoot = runAsRoot;
|
||||||
|
|
||||||
const QString msg = RunControl::tr("Starting %1...").arg(d->m_runnable.command.toUserOutput());
|
const QString msg = RunControl::tr("Starting %1...").arg(d->m_command.toUserOutput());
|
||||||
appendMessage(msg, Utils::NormalMessageFormat);
|
appendMessage(msg, NormalMessageFormat);
|
||||||
|
|
||||||
const bool isDesktop = d->m_runnable.device.isNull()
|
const bool isDesktop = !d->m_command.executable().needsDevice();
|
||||||
|| d->m_runnable.device.dynamicCast<const DesktopDevice>();
|
if (isDesktop && d->m_command.isEmpty()) {
|
||||||
if (isDesktop && d->m_runnable.command.isEmpty()) {
|
|
||||||
reportFailure(RunControl::tr("No executable specified."));
|
reportFailure(RunControl::tr("No executable specified."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1559,31 +1594,30 @@ void SimpleTargetRunner::setStartModifier(const std::function<void ()> &startMod
|
|||||||
|
|
||||||
CommandLine SimpleTargetRunner::commandLine() const
|
CommandLine SimpleTargetRunner::commandLine() const
|
||||||
{
|
{
|
||||||
return d->m_runnable.command;
|
return d->m_command;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimpleTargetRunner::setCommandLine(const Utils::CommandLine &commandLine)
|
void SimpleTargetRunner::setCommandLine(const Utils::CommandLine &commandLine)
|
||||||
{
|
{
|
||||||
d->m_runnable.command = commandLine;
|
d->m_command = commandLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimpleTargetRunner::setEnvironment(const Environment &environment)
|
void SimpleTargetRunner::setEnvironment(const Environment &environment)
|
||||||
{
|
{
|
||||||
d->m_runnable.environment = environment;
|
d->m_environment = environment;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimpleTargetRunner::setWorkingDirectory(const FilePath &workingDirectory)
|
void SimpleTargetRunner::setWorkingDirectory(const FilePath &workingDirectory)
|
||||||
{
|
{
|
||||||
d->m_runnable.workingDirectory = workingDirectory;
|
d->m_workingDirectory = workingDirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimpleTargetRunner::forceRunOnHost()
|
void SimpleTargetRunner::forceRunOnHost()
|
||||||
{
|
{
|
||||||
d->m_runnable.device = {};
|
const FilePath executable = d->m_command.executable();
|
||||||
const FilePath executable = d->m_runnable.command.executable();
|
|
||||||
if (executable.needsDevice()) {
|
if (executable.needsDevice()) {
|
||||||
QTC_CHECK(false);
|
QTC_CHECK(false);
|
||||||
d->m_runnable.command.setExecutable(FilePath::fromString(executable.path()));
|
d->m_command.setExecutable(FilePath::fromString(executable.path()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1796,11 +1830,6 @@ IDevice::ConstPtr RunWorker::device() const
|
|||||||
return d->runControl->device();
|
return d->runControl->device();
|
||||||
}
|
}
|
||||||
|
|
||||||
const Runnable &RunWorker::runnable() const
|
|
||||||
{
|
|
||||||
return d->runControl->runnable();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RunWorker::addStartDependency(RunWorker *dependency)
|
void RunWorker::addStartDependency(RunWorker *dependency)
|
||||||
{
|
{
|
||||||
d->startDependencies.append(dependency);
|
d->startDependencies.append(dependency);
|
||||||
@@ -1906,11 +1935,6 @@ void RunWorker::stop()
|
|||||||
reportStopped();
|
reportStopped();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Runnable::displayName() const
|
|
||||||
{
|
|
||||||
return command.executable().toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// OutputFormatterFactory
|
// OutputFormatterFactory
|
||||||
|
|
||||||
static QList<OutputFormatterFactory *> g_outputFormatterFactories;
|
static QList<OutputFormatterFactory *> g_outputFormatterFactories;
|
||||||
|
@@ -25,9 +25,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "buildconfiguration.h"
|
|
||||||
#include "devicesupport/idevicefwd.h"
|
#include "devicesupport/idevicefwd.h"
|
||||||
#include "projectexplorerconstants.h"
|
|
||||||
#include "runconfiguration.h"
|
#include "runconfiguration.h"
|
||||||
|
|
||||||
#include <utils/commandline.h>
|
#include <utils/commandline.h>
|
||||||
@@ -50,8 +48,6 @@ class OutputLineParser;
|
|||||||
} // Utils
|
} // Utils
|
||||||
|
|
||||||
namespace ProjectExplorer {
|
namespace ProjectExplorer {
|
||||||
class GlobalOrProjectAspect;
|
|
||||||
class Node;
|
|
||||||
class RunConfiguration;
|
class RunConfiguration;
|
||||||
class RunControl;
|
class RunControl;
|
||||||
class Target;
|
class Target;
|
||||||
@@ -71,11 +67,7 @@ public:
|
|||||||
Utils::CommandLine command;
|
Utils::CommandLine command;
|
||||||
Utils::FilePath workingDirectory;
|
Utils::FilePath workingDirectory;
|
||||||
Utils::Environment environment;
|
Utils::Environment environment;
|
||||||
IDeviceConstPtr device; // Override the kit's device. Keep unset by default.
|
|
||||||
QVariantHash extraData;
|
QVariantHash extraData;
|
||||||
|
|
||||||
// FIXME: Not necessarily a display name
|
|
||||||
QString displayName() const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class PROJECTEXPLORER_EXPORT RunWorker : public QObject
|
class PROJECTEXPLORER_EXPORT RunWorker : public QObject
|
||||||
@@ -102,7 +94,6 @@ public:
|
|||||||
// Part of read-only interface of RunControl for convenience.
|
// Part of read-only interface of RunControl for convenience.
|
||||||
void appendMessage(const QString &msg, Utils::OutputFormat format, bool appendNewLine = true);
|
void appendMessage(const QString &msg, Utils::OutputFormat format, bool appendNewLine = true);
|
||||||
IDeviceConstPtr device() const;
|
IDeviceConstPtr device() const;
|
||||||
const Runnable &runnable() const;
|
|
||||||
|
|
||||||
// States
|
// States
|
||||||
void initiateStart();
|
void initiateStart();
|
||||||
@@ -211,7 +202,7 @@ public:
|
|||||||
|
|
||||||
bool supportsReRunning() const;
|
bool supportsReRunning() const;
|
||||||
|
|
||||||
virtual QString displayName() const;
|
QString displayName() const;
|
||||||
void setDisplayName(const QString &displayName);
|
void setDisplayName(const QString &displayName);
|
||||||
|
|
||||||
bool isRunning() const;
|
bool isRunning() const;
|
||||||
@@ -239,7 +230,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
QString buildKey() const;
|
QString buildKey() const;
|
||||||
BuildConfiguration::BuildType buildType() const;
|
|
||||||
Utils::FilePath buildDirectory() const;
|
Utils::FilePath buildDirectory() const;
|
||||||
Utils::Environment buildEnvironment() const;
|
Utils::Environment buildEnvironment() const;
|
||||||
|
|
||||||
@@ -252,7 +242,18 @@ public:
|
|||||||
Utils::Id runMode() const;
|
Utils::Id runMode() const;
|
||||||
|
|
||||||
const Runnable &runnable() const;
|
const Runnable &runnable() const;
|
||||||
void setRunnable(const Runnable &runnable);
|
|
||||||
|
const Utils::CommandLine &commandLine() const;
|
||||||
|
void setCommandLine(const Utils::CommandLine &command);
|
||||||
|
|
||||||
|
const Utils::FilePath &workingDirectory() const;
|
||||||
|
void setWorkingDirectory(const Utils::FilePath &workingDirectory);
|
||||||
|
|
||||||
|
const Utils::Environment &environment() const;
|
||||||
|
void setEnvironment(const Utils::Environment &environment);
|
||||||
|
|
||||||
|
const QVariantHash &extraData() const;
|
||||||
|
void setExtraData(const QVariantHash &extraData);
|
||||||
|
|
||||||
static bool showPromptToStopDialog(const QString &title, const QString &text,
|
static bool showPromptToStopDialog(const QString &title, const QString &text,
|
||||||
const QString &stopButtonText = QString(),
|
const QString &stopButtonText = QString(),
|
||||||
|
@@ -89,23 +89,8 @@ PySideBuildStep::PySideBuildStep(BuildStepList *bsl, Id id)
|
|||||||
setWorkingDirectoryProvider([this] { return target()->project()->projectDirectory(); });
|
setWorkingDirectoryProvider([this] { return target()->project()->projectDirectory(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void PySideBuildStep::updateInterpreter(const Utils::FilePath &python)
|
void PySideBuildStep::updatePySideProjectPath(const Utils::FilePath &pySideProjectPath)
|
||||||
{
|
{
|
||||||
Utils::FilePath pySideProjectPath;
|
|
||||||
const PipPackage pySide6Package("PySide6");
|
|
||||||
const PipPackageInfo info = pySide6Package.info(python);
|
|
||||||
for (const FilePath &file : qAsConst(info.files)) {
|
|
||||||
if (file.fileName() == HostOsInfo::withExecutableSuffix("pyside6-project")) {
|
|
||||||
pySideProjectPath = info.location.resolvePath(file);
|
|
||||||
pySideProjectPath = pySideProjectPath.cleanPath();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pySideProjectPath.isExecutableFile())
|
|
||||||
pySideProjectPath = Environment::systemEnvironment().searchInPath("pyside6-project");
|
|
||||||
|
|
||||||
if (pySideProjectPath.isExecutableFile())
|
|
||||||
m_pysideProject->setFilePath(pySideProjectPath);
|
m_pysideProject->setFilePath(pySideProjectPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -50,7 +50,7 @@ class PySideBuildStep : public ProjectExplorer::AbstractProcessStep
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
PySideBuildStep(ProjectExplorer::BuildStepList *bsl, Utils::Id id);
|
PySideBuildStep(ProjectExplorer::BuildStepList *bsl, Utils::Id id);
|
||||||
void updateInterpreter(const Utils::FilePath &python);
|
void updatePySideProjectPath(const Utils::FilePath &pySideProjectPath);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Utils::StringAspect *m_pysideProject;
|
Utils::StringAspect *m_pysideProject;
|
||||||
|
@@ -140,9 +140,13 @@ private:
|
|||||||
class PythonRunConfiguration : public RunConfiguration
|
class PythonRunConfiguration : public RunConfiguration
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PythonRunConfiguration(Target *target, Id id)
|
PythonRunConfiguration(Target *target, Id id);
|
||||||
|
void currentInterpreterChanged();
|
||||||
|
};
|
||||||
|
|
||||||
|
PythonRunConfiguration::PythonRunConfiguration(Target *target, Id id)
|
||||||
: RunConfiguration(target, id)
|
: RunConfiguration(target, id)
|
||||||
{
|
{
|
||||||
auto interpreterAspect = addAspect<InterpreterAspect>();
|
auto interpreterAspect = addAspect<InterpreterAspect>();
|
||||||
interpreterAspect->setSettingsKey("PythonEditor.RunConfiguation.Interpreter");
|
interpreterAspect->setSettingsKey("PythonEditor.RunConfiguation.Interpreter");
|
||||||
interpreterAspect->setSettingsDialogId(Constants::C_PYTHONOPTIONS_PAGE_ID);
|
interpreterAspect->setSettingsDialogId(Constants::C_PYTHONOPTIONS_PAGE_ID);
|
||||||
@@ -156,8 +160,7 @@ public:
|
|||||||
QList<Interpreter> interpreters = PythonSettings::detectPythonVenvs(
|
QList<Interpreter> interpreters = PythonSettings::detectPythonVenvs(
|
||||||
project()->projectDirectory());
|
project()->projectDirectory());
|
||||||
interpreterAspect->updateInterpreters(PythonSettings::interpreters());
|
interpreterAspect->updateInterpreters(PythonSettings::interpreters());
|
||||||
Interpreter defaultInterpreter = interpreters.isEmpty()
|
Interpreter defaultInterpreter = interpreters.isEmpty() ? PythonSettings::defaultInterpreter()
|
||||||
? PythonSettings::defaultInterpreter()
|
|
||||||
: interpreters.first();
|
: interpreters.first();
|
||||||
if (!defaultInterpreter.command.isExecutableFile())
|
if (!defaultInterpreter.command.isExecutableFile())
|
||||||
defaultInterpreter = PythonSettings::interpreters().value(0);
|
defaultInterpreter = PythonSettings::interpreters().value(0);
|
||||||
@@ -178,7 +181,7 @@ public:
|
|||||||
|
|
||||||
auto argumentsAspect = addAspect<ArgumentsAspect>(macroExpander());
|
auto argumentsAspect = addAspect<ArgumentsAspect>(macroExpander());
|
||||||
|
|
||||||
addAspect<WorkingDirectoryAspect>(nullptr);
|
addAspect<WorkingDirectoryAspect>(macroExpander(), nullptr);
|
||||||
addAspect<TerminalAspect>();
|
addAspect<TerminalAspect>();
|
||||||
|
|
||||||
setCommandLineGetter([bufferedAspect, interpreterAspect, argumentsAspect, scriptAspect] {
|
setCommandLineGetter([bufferedAspect, interpreterAspect, argumentsAspect, scriptAspect] {
|
||||||
@@ -199,15 +202,27 @@ public:
|
|||||||
});
|
});
|
||||||
|
|
||||||
connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update);
|
connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PythonRunConfiguration::currentInterpreterChanged()
|
||||||
|
{
|
||||||
|
const FilePath python = aspect<InterpreterAspect>()->currentInterpreter().command;
|
||||||
|
BuildStepList *buildSteps = target()->activeBuildConfiguration()->buildSteps();
|
||||||
|
|
||||||
|
Utils::FilePath pySideProjectPath;
|
||||||
|
const PipPackage pySide6Package("PySide6");
|
||||||
|
const PipPackageInfo info = pySide6Package.info(python);
|
||||||
|
|
||||||
|
for (const FilePath &file : qAsConst(info.files)) {
|
||||||
|
if (file.fileName() == HostOsInfo::withExecutableSuffix("pyside6-project")) {
|
||||||
|
pySideProjectPath = info.location.resolvePath(file);
|
||||||
|
pySideProjectPath = pySideProjectPath.cleanPath();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void currentInterpreterChanged()
|
|
||||||
{
|
|
||||||
const FilePath python = aspect<InterpreterAspect>()->currentInterpreter().command;
|
|
||||||
|
|
||||||
BuildStepList *buildSteps = target()->activeBuildConfiguration()->buildSteps();
|
|
||||||
if (auto pySideBuildStep = buildSteps->firstOfType<PySideBuildStep>())
|
if (auto pySideBuildStep = buildSteps->firstOfType<PySideBuildStep>())
|
||||||
pySideBuildStep->updateInterpreter(python);
|
pySideBuildStep->updatePySideProjectPath(pySideProjectPath);
|
||||||
|
|
||||||
for (FilePath &file : project()->files(Project::AllFiles)) {
|
for (FilePath &file : project()->files(Project::AllFiles)) {
|
||||||
if (auto document = TextEditor::TextDocument::textDocumentForFilePath(file)) {
|
if (auto document = TextEditor::TextDocument::textDocumentForFilePath(file)) {
|
||||||
@@ -217,8 +232,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
PythonRunConfigurationFactory::PythonRunConfigurationFactory()
|
PythonRunConfigurationFactory::PythonRunConfigurationFactory()
|
||||||
{
|
{
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user