diff --git a/dist/changes-5.0.2.md b/dist/changes-5.0.2.md new file mode 100644 index 00000000000..f01fc5e2d8d --- /dev/null +++ b/dist/changes-5.0.2.md @@ -0,0 +1,70 @@ +Qt Creator 5.0.2 +================ + +Qt Creator version 5.0.2 contains bug fixes. + +The most important changes are listed in this document. For a complete list of +changes, see the Git log for the Qt Creator sources that you can check out from +the public Git repository. For example: + + git clone git://code.qt.io/qt-creator/qt-creator.git + git log --cherry-pick --pretty=oneline origin/v5.0.1..v5.0.2 + +Help +---- + +* Fixed that Qt 5 context help was shown even if Qt 6 documentation is available + (QTCREATORBUG-26292) + +Projects +-------- + +* Fixed canceling of builds (QTCREATORBUG-26271) + +### CMake + +* Changed the `File System` special node to be shown only on parsing failure + (QTCREATORBUG-25994, QTCREATORBUG-25974) +* Fixed loading of projects without targets (QTCREATORBUG-25509) +* Fixed that no targets where shown in added build step (QTCREATORBUG-25759) +* Fixed that `ninja` could not be found after changing Qt installation location + (QTCREATORBUG-26289) + +Debugging +--------- + +### GDB + +* Fixed debugging of terminal applications with GDB < 10 (QTCREATORBUG-26299) + +Platforms +--------- + +### macOS + +* Fixed issue with absolute RPATH in `clazy-standalone` (QTCREATORBUG-26196) + +### Android + +* Fixed that wrong deployment file could be used (QTCREATORBUG-25793) + +Credits for these changes go to: +-------------------------------- +Alessandro Portale +Assam Boudjelthia +Christian Kandeler +Christian Stenger +Cristian Adam +Eike Ziller +Henning Gruendl +Ivan Komissarov +Jaroslaw Kobus +Johanna Vanhatapio +Kai Köhne +Knud Dollereder +Leena Miettinen +Mahmoud Badri +Marco Bubke +Orgad Shaneh +Robert Löhning +Thomas Hartmann diff --git a/share/qtcreator/templates/wizards/autotest/autotest.png b/share/qtcreator/templates/wizards/autotest/autotest.png index 3045e83a29d..0139c89983d 100644 Binary files a/share/qtcreator/templates/wizards/autotest/autotest.png and b/share/qtcreator/templates/wizards/autotest/autotest.png differ diff --git a/share/qtcreator/templates/wizards/autotest/autotest@2x.png b/share/qtcreator/templates/wizards/autotest/autotest@2x.png index 8a29aa6316c..4c699b7b812 100644 Binary files a/share/qtcreator/templates/wizards/autotest/autotest@2x.png and b/share/qtcreator/templates/wizards/autotest/autotest@2x.png differ diff --git a/share/qtcreator/templates/wizards/autotest/wizard.json b/share/qtcreator/templates/wizards/autotest/wizard.json index 84f3f3d3104..9850079be8c 100644 --- a/share/qtcreator/templates/wizards/autotest/wizard.json +++ b/share/qtcreator/templates/wizards/autotest/wizard.json @@ -7,6 +7,7 @@ "trDisplayName": "Auto Test Project", "trDisplayCategory": "Other Project", "icon": "autotest.png", + "iconKind": "Themed", "featuresRequired": [ "QtSupport.Wizards.FeatureDesktop" ], "enabled": "%{JS: value('Plugins').indexOf('CppEditor') >= 0}", diff --git a/share/qtcreator/templates/wizards/classes/python/wizard.json b/share/qtcreator/templates/wizards/classes/python/wizard.json index 960a99b9fe5..7a86aa341e5 100644 --- a/share/qtcreator/templates/wizards/classes/python/wizard.json +++ b/share/qtcreator/templates/wizards/classes/python/wizard.json @@ -7,6 +7,7 @@ "trDisplayName": "Python Class", "trDisplayCategory": "Python", "icon": "../../files/python/icon.png", + "iconKind": "Themed", "enabled": "%{JS: value('Plugins').indexOf('Python') >= 0}", "options": diff --git a/share/qtcreator/templates/wizards/files/nim/wizard.json b/share/qtcreator/templates/wizards/files/nim/wizard.json index 1baf376aa41..e1957ed3f64 100644 --- a/share/qtcreator/templates/wizards/files/nim/wizard.json +++ b/share/qtcreator/templates/wizards/files/nim/wizard.json @@ -7,6 +7,7 @@ "trDisplayName": "Nim File", "trDisplayCategory": "Nim", "icon": "../../projects/nim/icon.png", + "iconKind": "Themed", "enabled": "%{JS: value('Plugins').indexOf('Nim') >= 0}", "options": { "key": "DefaultSuffix", "value": "nim" }, diff --git a/share/qtcreator/templates/wizards/files/nimscript/wizard.json b/share/qtcreator/templates/wizards/files/nimscript/wizard.json index 94012a7167f..09afa96325d 100644 --- a/share/qtcreator/templates/wizards/files/nimscript/wizard.json +++ b/share/qtcreator/templates/wizards/files/nimscript/wizard.json @@ -7,6 +7,7 @@ "trDisplayName": "Nim Script File", "trDisplayCategory": "Nim", "icon": "../../projects/nim/icon.png", + "iconKind": "Themed", "enabled": "%{JS: value('Plugins').indexOf('Nim') >= 0}", "options": { "key": "DefaultSuffix", "value": "nims" }, diff --git a/share/qtcreator/templates/wizards/files/python/icon.png b/share/qtcreator/templates/wizards/files/python/icon.png index 8ab694fe7af..8eb51c44550 100644 Binary files a/share/qtcreator/templates/wizards/files/python/icon.png and b/share/qtcreator/templates/wizards/files/python/icon.png differ diff --git a/share/qtcreator/templates/wizards/files/python/icon@2x.png b/share/qtcreator/templates/wizards/files/python/icon@2x.png index 8d1f2751860..70464b179f4 100644 Binary files a/share/qtcreator/templates/wizards/files/python/icon@2x.png and b/share/qtcreator/templates/wizards/files/python/icon@2x.png differ diff --git a/share/qtcreator/templates/wizards/files/python/wizard.json b/share/qtcreator/templates/wizards/files/python/wizard.json index a01e32bf4a6..a4398f802bd 100644 --- a/share/qtcreator/templates/wizards/files/python/wizard.json +++ b/share/qtcreator/templates/wizards/files/python/wizard.json @@ -7,6 +7,7 @@ "trDisplayName": "Python File", "trDisplayCategory": "Python", "icon": "icon.png", + "iconKind": "Themed", "enabled": "%{JS: value('Plugins').indexOf('Python') >= 0}", "options": { "key": "DefaultSuffix", "value": "%{JS: Util.preferredSuffix('text/x-python')}" }, diff --git a/share/qtcreator/templates/wizards/global/consoleapplication.png b/share/qtcreator/templates/wizards/global/consoleapplication.png index 36c822d9dc1..3f1f148bd1e 100644 Binary files a/share/qtcreator/templates/wizards/global/consoleapplication.png and b/share/qtcreator/templates/wizards/global/consoleapplication.png differ diff --git a/share/qtcreator/templates/wizards/global/consoleapplication@2x.png b/share/qtcreator/templates/wizards/global/consoleapplication@2x.png index d94451c152b..d3995d28f79 100644 Binary files a/share/qtcreator/templates/wizards/global/consoleapplication@2x.png and b/share/qtcreator/templates/wizards/global/consoleapplication@2x.png differ diff --git a/share/qtcreator/templates/wizards/global/guiapplication.png b/share/qtcreator/templates/wizards/global/guiapplication.png index fba6e608a09..b60008a3c26 100644 Binary files a/share/qtcreator/templates/wizards/global/guiapplication.png and b/share/qtcreator/templates/wizards/global/guiapplication.png differ diff --git a/share/qtcreator/templates/wizards/global/guiapplication@2x.png b/share/qtcreator/templates/wizards/global/guiapplication@2x.png index 1c20ad695b3..1ccd0931eca 100644 Binary files a/share/qtcreator/templates/wizards/global/guiapplication@2x.png and b/share/qtcreator/templates/wizards/global/guiapplication@2x.png differ diff --git a/share/qtcreator/templates/wizards/global/lib.png b/share/qtcreator/templates/wizards/global/lib.png index 52cb73e382e..e163a749cb0 100644 Binary files a/share/qtcreator/templates/wizards/global/lib.png and b/share/qtcreator/templates/wizards/global/lib.png differ diff --git a/share/qtcreator/templates/wizards/global/lib@2x.png b/share/qtcreator/templates/wizards/global/lib@2x.png index c959779b029..946e55a7df3 100644 Binary files a/share/qtcreator/templates/wizards/global/lib@2x.png and b/share/qtcreator/templates/wizards/global/lib@2x.png differ diff --git a/share/qtcreator/templates/wizards/projects/consoleapp/wizard.json b/share/qtcreator/templates/wizards/projects/consoleapp/wizard.json index 1bc8554c8b7..0fac3208cba 100644 --- a/share/qtcreator/templates/wizards/projects/consoleapp/wizard.json +++ b/share/qtcreator/templates/wizards/projects/consoleapp/wizard.json @@ -7,6 +7,7 @@ "trDisplayName": "Qt Console Application", "trDisplayCategory": "Application (Qt)", "icon": "../../global/consoleapplication.png", + "iconKind": "Themed", "featuresRequired": [ "QtSupport.Wizards.FeatureQt" ], "enabled": "%{JS: value('Plugins').indexOf('QmakeProjectManager') >= 0 || value('Plugins').indexOf('QbsProjectManager') >= 0 || value('Plugins').indexOf('CMakeProjectManager') >= 0 || value('Plugins').indexOf('MesonProjectManager') >= 0}", diff --git a/share/qtcreator/templates/wizards/projects/cpplibrary/wizard.json b/share/qtcreator/templates/wizards/projects/cpplibrary/wizard.json index 217a5c1a735..c66dce44b0e 100644 --- a/share/qtcreator/templates/wizards/projects/cpplibrary/wizard.json +++ b/share/qtcreator/templates/wizards/projects/cpplibrary/wizard.json @@ -7,6 +7,7 @@ "trDisplayName": "C++ Library", "trDisplayCategory": "Library", "icon": "../../global/lib.png", + "iconKind": "Themed", "enabled": "%{JS: value('Plugins').indexOf('CppEditor') >= 0 && (value('Plugins').indexOf('QmakeProjectManager') >= 0 || value('Plugins').indexOf('CMakeProjectManager') >= 0 || value('Plugins').indexOf('MesonProjectManager') >= 0)}", "options": diff --git a/share/qtcreator/templates/wizards/projects/nim/icon.png b/share/qtcreator/templates/wizards/projects/nim/icon.png index d3f8575b26b..8fe8c036395 100644 Binary files a/share/qtcreator/templates/wizards/projects/nim/icon.png and b/share/qtcreator/templates/wizards/projects/nim/icon.png differ diff --git a/share/qtcreator/templates/wizards/projects/nim/icon@2x.png b/share/qtcreator/templates/wizards/projects/nim/icon@2x.png index bdc8c57055f..b02a7ed104f 100644 Binary files a/share/qtcreator/templates/wizards/projects/nim/icon@2x.png and b/share/qtcreator/templates/wizards/projects/nim/icon@2x.png differ diff --git a/share/qtcreator/templates/wizards/projects/nim/wizard.json b/share/qtcreator/templates/wizards/projects/nim/wizard.json index 1239e444e91..68970d4d9ac 100644 --- a/share/qtcreator/templates/wizards/projects/nim/wizard.json +++ b/share/qtcreator/templates/wizards/projects/nim/wizard.json @@ -8,6 +8,7 @@ "trDisplayCategory": "Non-Qt Project", "featuresRequired": [ "ToolChain.Nim.NimToolChain" ], "icon": "icon.png", + "iconKind": "Themed", "enabled": "%{JS: value('Plugins').indexOf('Nim') >= 0 }", "options": diff --git a/share/qtcreator/templates/wizards/projects/nimble/wizard.json b/share/qtcreator/templates/wizards/projects/nimble/wizard.json index 4f3ac9d8ea2..8a0a14b8304 100644 --- a/share/qtcreator/templates/wizards/projects/nimble/wizard.json +++ b/share/qtcreator/templates/wizards/projects/nimble/wizard.json @@ -10,6 +10,7 @@ "ToolChain.Nim.NimToolChain" ], "icon":"../nim/icon.png", + "iconKind": "Themed", "enabled":"%{JS: value('Plugins').indexOf('Nim') >= 0 }", "options":[ { diff --git a/share/qtcreator/templates/wizards/projects/plainc/wizard.json b/share/qtcreator/templates/wizards/projects/plainc/wizard.json index 2f6d8bf5ca4..e7885bde6ae 100644 --- a/share/qtcreator/templates/wizards/projects/plainc/wizard.json +++ b/share/qtcreator/templates/wizards/projects/plainc/wizard.json @@ -7,6 +7,7 @@ "trDisplayName": "Plain C Application", "trDisplayCategory": "Non-Qt Project", "icon": "../../global/consoleapplication.png", + "iconKind": "Themed", "enabled": "%{JS: value('Plugins').indexOf('CppEditor') >= 0 && (value('Plugins').indexOf('QmakeProjectManager') >= 0 || value('Plugins').indexOf('QbsProjectManager') >= 0 || value('Plugins').indexOf('CMakeProjectManager') >= 0 || value('Plugins').indexOf('MesonProjectManager') >= 0)}", "options": diff --git a/share/qtcreator/templates/wizards/projects/plaincpp/wizard.json b/share/qtcreator/templates/wizards/projects/plaincpp/wizard.json index 3475d272072..52cb071bee5 100644 --- a/share/qtcreator/templates/wizards/projects/plaincpp/wizard.json +++ b/share/qtcreator/templates/wizards/projects/plaincpp/wizard.json @@ -7,6 +7,7 @@ "trDisplayName": "Plain C++ Application", "trDisplayCategory": "Non-Qt Project", "icon": "../../global/consoleapplication.png", + "iconKind": "Themed", "enabled": "%{JS: value('Plugins').indexOf('CppEditor') >= 0 && (value('Plugins').indexOf('QmakeProjectManager') >= 0 || value('Plugins').indexOf('QbsProjectManager') >= 0 || value('Plugins').indexOf('CMakeProjectManager') >= 0 || value('Plugins').indexOf('MesonProjectManager') >= 0)}", "options": diff --git a/share/qtcreator/templates/wizards/projects/qmake/empty/wizard.json b/share/qtcreator/templates/wizards/projects/qmake/empty/wizard.json index 84f019c222f..1c449e0c0d3 100644 --- a/share/qtcreator/templates/wizards/projects/qmake/empty/wizard.json +++ b/share/qtcreator/templates/wizards/projects/qmake/empty/wizard.json @@ -7,6 +7,7 @@ "trDisplayName": "Empty qmake Project", "trDisplayCategory": "Other Project", "icon": "../../../global/guiapplication.png", + "iconKind": "Themed", "featuresRequired": [ "QtSupport.Wizards.FeatureQt" ], "enabled": "%{JS: value('Plugins').indexOf('QmakeProjectManager') >= 0}", "options": diff --git a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/empty/icon.png b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/empty/icon.png index 483f71196d3..96a00a82ebf 100644 Binary files a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/empty/icon.png and b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/empty/icon.png differ diff --git a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/empty/icon@2x.png b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/empty/icon@2x.png index 81beed5e239..77675b61961 100644 Binary files a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/empty/icon@2x.png and b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/empty/icon@2x.png differ diff --git a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/empty/wizard.json b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/empty/wizard.json index 817ad3609e5..268fc6bd9e6 100644 --- a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/empty/wizard.json +++ b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/empty/wizard.json @@ -7,6 +7,7 @@ "trDisplayName": "Qt for Python - Empty", "trDisplayCategory": "Application (Qt for Python)", "icon": "icon.png", + "iconKind": "Themed", "enabled": "%{JS: value('Plugins').indexOf('Python') >= 0}", "options": diff --git a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/icons/icon.png b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/icons/icon.png index ba97ffd6620..e2a92fb5667 100644 Binary files a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/icons/icon.png and b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/icons/icon.png differ diff --git a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/icons/icon@2x.png b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/icons/icon@2x.png index 4c26be65ba4..8cfe07e1b9f 100644 Binary files a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/icons/icon@2x.png and b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/icons/icon@2x.png differ diff --git a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/mainwindow/wizard.json b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/mainwindow/wizard.json index 47a26438bd3..a16b7fd900a 100644 --- a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/mainwindow/wizard.json +++ b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/mainwindow/wizard.json @@ -7,6 +7,7 @@ "trDisplayName": "Qt for Python - Window", "trDisplayCategory": "Application (Qt for Python)", "icon": "../icons/icon.png", + "iconKind": "Themed", "enabled": "%{JS: value('Plugins').indexOf('Python') >= 0}", "options": diff --git a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/qtquickapplication/wizard.json b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/qtquickapplication/wizard.json index 1967d1666a3..268367d1f6f 100644 --- a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/qtquickapplication/wizard.json +++ b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/qtquickapplication/wizard.json @@ -7,6 +7,7 @@ "trDisplayName": "Qt for Python - Qt Quick Application - Empty", "trDisplayCategory": "Application (Qt for Python)", "icon": "../icons/icon.png", + "iconKind": "Themed", "enabled": "%{JS: value('Plugins').indexOf('Python') >= 0}", "options": diff --git a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/widget/wizard.json b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/widget/wizard.json index de9f36df931..6a068d5fd3c 100644 --- a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/widget/wizard.json +++ b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/widget/wizard.json @@ -7,6 +7,7 @@ "trDisplayName": "Qt for Python - Window (UI file)", "trDisplayCategory": "Application (Qt for Python)", "icon": "../icons/icon.png", + "iconKind": "Themed", "enabled": "%{JS: value('Plugins').indexOf('Python') >= 0}", "options": diff --git a/share/qtcreator/templates/wizards/projects/qtquick2-extension/lib.png b/share/qtcreator/templates/wizards/projects/qtquick2-extension/lib.png index 0566f1d5f12..7e53940e461 100644 Binary files a/share/qtcreator/templates/wizards/projects/qtquick2-extension/lib.png and b/share/qtcreator/templates/wizards/projects/qtquick2-extension/lib.png differ diff --git a/share/qtcreator/templates/wizards/projects/qtquick2-extension/lib@2x.png b/share/qtcreator/templates/wizards/projects/qtquick2-extension/lib@2x.png index 90a842d6e83..262342b2cf1 100644 Binary files a/share/qtcreator/templates/wizards/projects/qtquick2-extension/lib@2x.png and b/share/qtcreator/templates/wizards/projects/qtquick2-extension/lib@2x.png differ diff --git a/share/qtcreator/templates/wizards/projects/qtquick2-extension/wizard.json b/share/qtcreator/templates/wizards/projects/qtquick2-extension/wizard.json index 404fd0951c2..45989fb7de2 100644 --- a/share/qtcreator/templates/wizards/projects/qtquick2-extension/wizard.json +++ b/share/qtcreator/templates/wizards/projects/qtquick2-extension/wizard.json @@ -7,6 +7,7 @@ "trDisplayName": "Qt Quick 2 Extension Plugin", "trDisplayCategory": "Library", "icon": "lib.png", + "iconKind": "Themed", "featuresRequired": [ "QtSupport.Wizards.FeatureQtQuick", "QtSupport.Wizards.FeatureQtQuick.2" ], "enabled": "%{JS: value('Plugins').indexOf('QmakeProjectManager') >= 0}", diff --git a/share/qtcreator/templates/wizards/projects/qtquickapplication/empty/icon.png b/share/qtcreator/templates/wizards/projects/qtquickapplication/empty/icon.png index d9747274a91..8be6c371bed 100644 Binary files a/share/qtcreator/templates/wizards/projects/qtquickapplication/empty/icon.png and b/share/qtcreator/templates/wizards/projects/qtquickapplication/empty/icon.png differ diff --git a/share/qtcreator/templates/wizards/projects/qtquickapplication/empty/icon@2x.png b/share/qtcreator/templates/wizards/projects/qtquickapplication/empty/icon@2x.png index 7d79b412e32..094df34dba0 100644 Binary files a/share/qtcreator/templates/wizards/projects/qtquickapplication/empty/icon@2x.png and b/share/qtcreator/templates/wizards/projects/qtquickapplication/empty/icon@2x.png differ diff --git a/share/qtcreator/templates/wizards/projects/qtquickapplication/empty/wizard.json b/share/qtcreator/templates/wizards/projects/qtquickapplication/empty/wizard.json index 4e4936a77af..401fb21e16c 100644 --- a/share/qtcreator/templates/wizards/projects/qtquickapplication/empty/wizard.json +++ b/share/qtcreator/templates/wizards/projects/qtquickapplication/empty/wizard.json @@ -7,6 +7,7 @@ "trDisplayName": "Qt Quick Application", "trDisplayCategory": "Application (Qt)", "icon": "icon.png", + "iconKind": "Themed", "featuresRequired": [ "QtSupport.Wizards.FeatureQt.5.6" ], "enabled": "%{JS: value('Plugins').indexOf('QmakeProjectManager') >= 0 || value('Plugins').indexOf('QbsProjectManager') >= 0 || value('Plugins').indexOf('CMakeProjectManager') >= 0}", diff --git a/share/qtcreator/templates/wizards/projects/qtquickuiprototype/qtquickuiprototype.png b/share/qtcreator/templates/wizards/projects/qtquickuiprototype/qtquickuiprototype.png index 949b09c8516..3d120b6e30c 100644 Binary files a/share/qtcreator/templates/wizards/projects/qtquickuiprototype/qtquickuiprototype.png and b/share/qtcreator/templates/wizards/projects/qtquickuiprototype/qtquickuiprototype.png differ diff --git a/share/qtcreator/templates/wizards/projects/qtquickuiprototype/qtquickuiprototype@2x.png b/share/qtcreator/templates/wizards/projects/qtquickuiprototype/qtquickuiprototype@2x.png index ab779dca3ea..8667c4d1077 100644 Binary files a/share/qtcreator/templates/wizards/projects/qtquickuiprototype/qtquickuiprototype@2x.png and b/share/qtcreator/templates/wizards/projects/qtquickuiprototype/qtquickuiprototype@2x.png differ diff --git a/share/qtcreator/templates/wizards/projects/qtquickuiprototype/wizard.json b/share/qtcreator/templates/wizards/projects/qtquickuiprototype/wizard.json index 764998e6f80..366ec1e4cd1 100644 --- a/share/qtcreator/templates/wizards/projects/qtquickuiprototype/wizard.json +++ b/share/qtcreator/templates/wizards/projects/qtquickuiprototype/wizard.json @@ -7,6 +7,7 @@ "trDisplayName": "Qt Quick UI Prototype", "trDisplayCategory": "Other Project", "icon": "qtquickuiprototype.png", + "iconKind": "Themed", "enabled": "%{JS: value('Plugins').indexOf('QmlProjectManager') >= 0}", "featuresRequired": [ "QtSupport.Wizards.FeatureQtQuickProject", "QtSupport.Wizards.FeatureQt" ], diff --git a/share/qtcreator/templates/wizards/projects/qtwidgetsapplication/wizard.json b/share/qtcreator/templates/wizards/projects/qtwidgetsapplication/wizard.json index baa5d860e18..2a72449b2c9 100644 --- a/share/qtcreator/templates/wizards/projects/qtwidgetsapplication/wizard.json +++ b/share/qtcreator/templates/wizards/projects/qtwidgetsapplication/wizard.json @@ -7,6 +7,7 @@ "trDisplayName": "Qt Widgets Application", "trDisplayCategory": "Application (Qt)", "icon": "../../global/guiapplication.png", + "iconKind": "Themed", "featuresRequired": [ "QtSupport.Wizards.FeatureQt" ], "enabled": "%{JS: value('Plugins').indexOf('QmakeProjectManager') >= 0 || value('Plugins').indexOf('CMakeProjectManager') >= 0 || value('Plugins').indexOf('QbsProjectManager') >= 0 || value('Plugins').indexOf('MesonProjectManager') >= 0}", diff --git a/share/qtcreator/templates/wizards/projects/vcs/bazaar/icon.png b/share/qtcreator/templates/wizards/projects/vcs/bazaar/icon.png index 4c04cdbad95..256a2008c83 100644 Binary files a/share/qtcreator/templates/wizards/projects/vcs/bazaar/icon.png and b/share/qtcreator/templates/wizards/projects/vcs/bazaar/icon.png differ diff --git a/share/qtcreator/templates/wizards/projects/vcs/bazaar/icon@2x.png b/share/qtcreator/templates/wizards/projects/vcs/bazaar/icon@2x.png index d0ee3bb43b0..9550f6c2209 100644 Binary files a/share/qtcreator/templates/wizards/projects/vcs/bazaar/icon@2x.png and b/share/qtcreator/templates/wizards/projects/vcs/bazaar/icon@2x.png differ diff --git a/share/qtcreator/templates/wizards/projects/vcs/bazaar/wizard.json b/share/qtcreator/templates/wizards/projects/vcs/bazaar/wizard.json index 060b55944a5..8a20a1ef2e8 100644 --- a/share/qtcreator/templates/wizards/projects/vcs/bazaar/wizard.json +++ b/share/qtcreator/templates/wizards/projects/vcs/bazaar/wizard.json @@ -7,6 +7,7 @@ "trDisplayName": "Bazaar Clone (Or Branch)", "trDisplayCategory": "Import Project", "icon": "icon.png", + "iconKind": "Themed", "enabled": "%{JS: value('Plugins').indexOf('Bazaar') >= 0}", "options": diff --git a/share/qtcreator/templates/wizards/projects/vcs/cvs/icon.png b/share/qtcreator/templates/wizards/projects/vcs/cvs/icon.png index bd925b2e984..1f837052dae 100644 Binary files a/share/qtcreator/templates/wizards/projects/vcs/cvs/icon.png and b/share/qtcreator/templates/wizards/projects/vcs/cvs/icon.png differ diff --git a/share/qtcreator/templates/wizards/projects/vcs/cvs/icon@2x.png b/share/qtcreator/templates/wizards/projects/vcs/cvs/icon@2x.png index ec330c9f440..8d4e8921175 100644 Binary files a/share/qtcreator/templates/wizards/projects/vcs/cvs/icon@2x.png and b/share/qtcreator/templates/wizards/projects/vcs/cvs/icon@2x.png differ diff --git a/share/qtcreator/templates/wizards/projects/vcs/cvs/wizard.json b/share/qtcreator/templates/wizards/projects/vcs/cvs/wizard.json index 075a69fdb16..25287a56b07 100644 --- a/share/qtcreator/templates/wizards/projects/vcs/cvs/wizard.json +++ b/share/qtcreator/templates/wizards/projects/vcs/cvs/wizard.json @@ -7,6 +7,7 @@ "trDisplayName": "CVS Checkout", "trDisplayCategory": "Import Project", "icon": "icon.png", + "iconKind": "Themed", "enabled": "%{JS: value('Plugins').indexOf('CVS') >= 0}", "options": diff --git a/share/qtcreator/templates/wizards/projects/vcs/git/icon.png b/share/qtcreator/templates/wizards/projects/vcs/git/icon.png index cfbd0c497ff..df5bb7df518 100644 Binary files a/share/qtcreator/templates/wizards/projects/vcs/git/icon.png and b/share/qtcreator/templates/wizards/projects/vcs/git/icon.png differ diff --git a/share/qtcreator/templates/wizards/projects/vcs/git/icon@2x.png b/share/qtcreator/templates/wizards/projects/vcs/git/icon@2x.png index 2edb04b5a06..7c2d0639329 100644 Binary files a/share/qtcreator/templates/wizards/projects/vcs/git/icon@2x.png and b/share/qtcreator/templates/wizards/projects/vcs/git/icon@2x.png differ diff --git a/share/qtcreator/templates/wizards/projects/vcs/git/wizard.json b/share/qtcreator/templates/wizards/projects/vcs/git/wizard.json index 62bad7ee123..2967a51f31c 100644 --- a/share/qtcreator/templates/wizards/projects/vcs/git/wizard.json +++ b/share/qtcreator/templates/wizards/projects/vcs/git/wizard.json @@ -7,6 +7,7 @@ "trDisplayName": "Git Clone", "trDisplayCategory": "Import Project", "icon": "icon.png", + "iconKind": "Themed", "enabled": "%{JS: value('Plugins').indexOf('Git') >= 0}", "options": diff --git a/share/qtcreator/templates/wizards/projects/vcs/mercurial/icon.png b/share/qtcreator/templates/wizards/projects/vcs/mercurial/icon.png index b60e2df21a9..41c03f4f2dd 100644 Binary files a/share/qtcreator/templates/wizards/projects/vcs/mercurial/icon.png and b/share/qtcreator/templates/wizards/projects/vcs/mercurial/icon.png differ diff --git a/share/qtcreator/templates/wizards/projects/vcs/mercurial/icon@2x.png b/share/qtcreator/templates/wizards/projects/vcs/mercurial/icon@2x.png index 28adac54bcd..6e470773f28 100644 Binary files a/share/qtcreator/templates/wizards/projects/vcs/mercurial/icon@2x.png and b/share/qtcreator/templates/wizards/projects/vcs/mercurial/icon@2x.png differ diff --git a/share/qtcreator/templates/wizards/projects/vcs/mercurial/wizard.json b/share/qtcreator/templates/wizards/projects/vcs/mercurial/wizard.json index 81fa26da041..cb7e5ab4f10 100644 --- a/share/qtcreator/templates/wizards/projects/vcs/mercurial/wizard.json +++ b/share/qtcreator/templates/wizards/projects/vcs/mercurial/wizard.json @@ -7,6 +7,7 @@ "trDisplayName": "Mercurial Clone", "trDisplayCategory": "Import Project", "icon": "icon.png", + "iconKind": "Themed", "enabled": "%{JS: value('Plugins').indexOf('Mercurial') >= 0}", "options": diff --git a/share/qtcreator/templates/wizards/projects/vcs/subversion/icon.png b/share/qtcreator/templates/wizards/projects/vcs/subversion/icon.png index 815b68ed1cf..cd5c5aca206 100644 Binary files a/share/qtcreator/templates/wizards/projects/vcs/subversion/icon.png and b/share/qtcreator/templates/wizards/projects/vcs/subversion/icon.png differ diff --git a/share/qtcreator/templates/wizards/projects/vcs/subversion/icon@2x.png b/share/qtcreator/templates/wizards/projects/vcs/subversion/icon@2x.png index fa986f28937..636b7f8e1f2 100644 Binary files a/share/qtcreator/templates/wizards/projects/vcs/subversion/icon@2x.png and b/share/qtcreator/templates/wizards/projects/vcs/subversion/icon@2x.png differ diff --git a/share/qtcreator/templates/wizards/projects/vcs/subversion/wizard.json b/share/qtcreator/templates/wizards/projects/vcs/subversion/wizard.json index 2e63b12eb4f..1ced38032e0 100644 --- a/share/qtcreator/templates/wizards/projects/vcs/subversion/wizard.json +++ b/share/qtcreator/templates/wizards/projects/vcs/subversion/wizard.json @@ -7,6 +7,7 @@ "trDisplayName": "Subversion Checkout", "trDisplayCategory": "Import Project", "icon": "icon.png", + "iconKind": "Themed", "enabled": "%{JS: value('Plugins').indexOf('Subversion') >= 0}", "options": diff --git a/share/qtcreator/templates/wizards/qtcreatorplugin/qtcreatorplugin.png b/share/qtcreator/templates/wizards/qtcreatorplugin/qtcreatorplugin.png index f4c54981afd..9fa454a498f 100644 Binary files a/share/qtcreator/templates/wizards/qtcreatorplugin/qtcreatorplugin.png and b/share/qtcreator/templates/wizards/qtcreatorplugin/qtcreatorplugin.png differ diff --git a/share/qtcreator/templates/wizards/qtcreatorplugin/qtcreatorplugin@2x.png b/share/qtcreator/templates/wizards/qtcreatorplugin/qtcreatorplugin@2x.png index 3d3bea267f5..df1e5889893 100644 Binary files a/share/qtcreator/templates/wizards/qtcreatorplugin/qtcreatorplugin@2x.png and b/share/qtcreator/templates/wizards/qtcreatorplugin/qtcreatorplugin@2x.png differ diff --git a/share/qtcreator/templates/wizards/qtcreatorplugin/wizard.json b/share/qtcreator/templates/wizards/qtcreatorplugin/wizard.json index 6a629f0d05e..95549b2cd99 100644 --- a/share/qtcreator/templates/wizards/qtcreatorplugin/wizard.json +++ b/share/qtcreator/templates/wizards/qtcreatorplugin/wizard.json @@ -7,6 +7,7 @@ "trDisplayName": "Qt Creator Plugin", "trDisplayCategory": "Library", "icon": "qtcreatorplugin.png", + "iconKind": "Themed", "featuresRequired": [ "QtSupport.Wizards.FeatureQt", "QtSupport.Wizards.FeatureDesktop" ], "enabled": "%{JS: value('Plugins').indexOf('CMakeProjectManager') >= 0}", diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 236421558ca..ed41f3dce0d 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -12,7 +12,7 @@ install( add_qtc_executable(qtcreator DEFINES IDE_LIBRARY_BASENAME=\"${IDE_LIBRARY_BASE_PATH}\" - DEPENDS Aggregation ExtensionSystem Qt5::Core Qt5::Widgets Utils QtcSsh shared_qtsingleapplication app_version + DEPENDS Aggregation ExtensionSystem Qt5::Core Qt5::Widgets Utils shared_qtsingleapplication app_version SOURCES main.cpp ../tools/qtcreatorcrashhandler/crashhandlersetup.cpp ../tools/qtcreatorcrashhandler/crashhandlersetup.h diff --git a/src/app/app.pro b/src/app/app.pro index 1d15d390c3d..730427ba897 100644 --- a/src/app/app.pro +++ b/src/app/app.pro @@ -17,7 +17,7 @@ include(../rpath.pri) include(../libs/qt-breakpad/qtbreakpad.pri) -LIBS *= -l$$qtLibraryName(ExtensionSystem) -l$$qtLibraryName(Aggregation) -l$$qtLibraryName(Utils) -l$$qtLibraryName(QtcSsh) +LIBS *= -l$$qtLibraryName(ExtensionSystem) -l$$qtLibraryName(Aggregation) -l$$qtLibraryName(Utils) win32 { # We need the version in two separate formats for the .rc file diff --git a/src/app/app.qbs b/src/app/app.qbs index c97a6457274..42172aa09f6 100644 --- a/src/app/app.qbs +++ b/src/app/app.qbs @@ -46,7 +46,6 @@ QtcProduct { Depends { name: "app_version_header" } Depends { name: "Qt"; submodules: ["widgets", "network"] } Depends { name: "Utils" } - Depends { name: "QtcSsh" } Depends { name: "ExtensionSystem" } files: [ diff --git a/src/app/main.cpp b/src/app/main.cpp index bc0d72aeacb..8ab587a7131 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -32,16 +32,13 @@ #include #include -#include - #include #include #include #include -#include #include -#include #include +#include #include #include @@ -537,10 +534,7 @@ int main(int argc, char **argv) QCoreApplication::setOrganizationName(QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR)); QGuiApplication::setApplicationDisplayName(Core::Constants::IDE_DISPLAY_NAME); - Utils::ProcessReaper processReaper; - Utils::LauncherInterface::startLauncher(); - auto cleanup = qScopeGuard([] { Utils::LauncherInterface::stopLauncher(); }); - QSsh::SshConnectionManager sshConnectionManager; + auto cleanup = qScopeGuard([] { Utils::Singleton::deleteAll(); }); const QStringList pluginArguments = app.arguments(); diff --git a/src/libs/ssh/sshconnectionmanager.cpp b/src/libs/ssh/sshconnectionmanager.cpp index 59ddb78f2ac..3ac82a7dabc 100644 --- a/src/libs/ssh/sshconnectionmanager.cpp +++ b/src/libs/ssh/sshconnectionmanager.cpp @@ -53,16 +53,11 @@ bool operator!=(const UnacquiredConnection &c1, const UnacquiredConnection &c2) return !(c1 == c2); } -static class SshConnectionManagerPrivate *s_instance = nullptr; - class SshConnectionManagerPrivate : public QObject { public: SshConnectionManagerPrivate() { - QTC_ASSERT(s_instance == nullptr, return); - s_instance = this; - connect(&m_removalTimer, &QTimer::timeout, this, &SshConnectionManagerPrivate::removeInactiveConnections); m_removalTimer.start(SshSettings::connectionSharingTimeout() * 1000 * 60 / 2); @@ -70,8 +65,6 @@ public: ~SshConnectionManagerPrivate() override { - QTC_ASSERT(s_instance == this, return); - for (const UnacquiredConnection &connection : qAsConst(m_unacquiredConnections)) { disconnect(connection.connection, nullptr, this, nullptr); delete connection.connection; @@ -79,8 +72,6 @@ public: QTC_CHECK(m_acquiredConnections.isEmpty()); QTC_CHECK(m_deprecatedConnections.isEmpty()); - - s_instance = nullptr; } SshConnection *acquireConnection(const SshConnectionParameters &sshParams) @@ -213,6 +204,7 @@ private: SshConnectionManager::SshConnectionManager() : d(new Internal::SshConnectionManagerPrivate()) { + QTC_CHECK(QThread::currentThread() == qApp->thread()); } SshConnectionManager::~SshConnectionManager() @@ -222,20 +214,17 @@ SshConnectionManager::~SshConnectionManager() SshConnection *SshConnectionManager::acquireConnection(const SshConnectionParameters &sshParams) { - QTC_ASSERT(Internal::s_instance, return nullptr); - return Internal::s_instance->acquireConnection(sshParams); + return instance()->d->acquireConnection(sshParams); } void SshConnectionManager::releaseConnection(SshConnection *connection) { - QTC_ASSERT(Internal::s_instance, return); - Internal::s_instance->releaseConnection(connection); + instance()->d->releaseConnection(connection); } void SshConnectionManager::forceNewConnection(const SshConnectionParameters &sshParams) { - QTC_ASSERT(Internal::s_instance, return); - Internal::s_instance->forceNewConnection(sshParams); + instance()->d->forceNewConnection(sshParams); } } // namespace QSsh diff --git a/src/libs/ssh/sshconnectionmanager.h b/src/libs/ssh/sshconnectionmanager.h index 8d8a03f43a5..d46a8d8b89f 100644 --- a/src/libs/ssh/sshconnectionmanager.h +++ b/src/libs/ssh/sshconnectionmanager.h @@ -27,6 +27,9 @@ #include "ssh_global.h" +#include +#include + namespace QSsh { class SshConnection; @@ -35,18 +38,21 @@ class SshConnectionParameters; namespace Internal { class SshConnectionManagerPrivate; } class QSSH_EXPORT SshConnectionManager final + : public Utils::SingletonWithOptionalDependencies { public: - SshConnectionManager(); - ~SshConnectionManager(); - static SshConnection *acquireConnection(const SshConnectionParameters &sshParams); static void releaseConnection(SshConnection *connection); // Make sure the next acquireConnection with the given parameters will return a new connection. static void forceNewConnection(const SshConnectionParameters &sshParams); private: + SshConnectionManager(); + ~SshConnectionManager(); + Internal::SshConnectionManagerPrivate *d; + friend class Utils::SingletonWithOptionalDependencies; }; } // namespace QSsh diff --git a/src/libs/utils/CMakeLists.txt b/src/libs/utils/CMakeLists.txt index 3ef203bc21b..9db6ea5f1e7 100644 --- a/src/libs/utils/CMakeLists.txt +++ b/src/libs/utils/CMakeLists.txt @@ -144,6 +144,7 @@ add_qtc_library(Utils settingsutils.h shellcommand.cpp shellcommand.h shellcommandpage.cpp shellcommandpage.h + singleton.cpp singleton.h sizedarray.h smallstring.h smallstringfwd.h diff --git a/src/libs/utils/images/wizardicon-file.png b/src/libs/utils/images/wizardicon-file.png index 01df9663193..f476d80db08 100644 Binary files a/src/libs/utils/images/wizardicon-file.png and b/src/libs/utils/images/wizardicon-file.png differ diff --git a/src/libs/utils/images/wizardicon-file@2x.png b/src/libs/utils/images/wizardicon-file@2x.png index ed7c0cbb696..46b58fe757a 100644 Binary files a/src/libs/utils/images/wizardicon-file@2x.png and b/src/libs/utils/images/wizardicon-file@2x.png differ diff --git a/src/libs/utils/launcherinterface.cpp b/src/libs/utils/launcherinterface.cpp index 269e4393d73..a5609fd6e7c 100644 --- a/src/libs/utils/launcherinterface.cpp +++ b/src/libs/utils/launcherinterface.cpp @@ -28,7 +28,6 @@ #include "filepath.h" #include "launcherpackets.h" #include "launchersocket.h" -#include "processreaper.h" #include "qtcassert.h" #include @@ -42,7 +41,6 @@ #endif namespace Utils { - namespace Internal { class LauncherProcess : public QProcess @@ -185,92 +183,73 @@ void LauncherInterfacePrivate::handleProcessStderr() using namespace Utils::Internal; static QMutex s_instanceMutex; -static LauncherInterface *s_instance = nullptr; +static QString s_pathToLauncher; +static std::atomic_bool s_started = false; LauncherInterface::LauncherInterface() : m_private(new LauncherInterfacePrivate()) { m_private->moveToThread(&m_thread); - connect(m_private, &LauncherInterfacePrivate::errorOccurred, - this, &LauncherInterface::errorOccurred); - connect(&m_thread, &QThread::finished, m_private, &QObject::deleteLater); + QObject::connect(&m_thread, &QThread::finished, m_private, &QObject::deleteLater); m_thread.start(); -} + m_thread.moveToThread(qApp->thread()); -LauncherInterface::~LauncherInterface() -{ - m_thread.quit(); - m_thread.wait(); -} - -// Called from main thread -void LauncherInterface::startLauncher(const QString &pathToLauncher) -{ - QMutexLocker locker(&s_instanceMutex); - QTC_ASSERT(s_instance == nullptr, return); - s_instance = new LauncherInterface(); - LauncherInterfacePrivate *p = s_instance->m_private; - p->setPathToLauncher(pathToLauncher); - const FilePath launcherFilePath = FilePath::fromString(p->launcherFilePath()) + m_private->setPathToLauncher(s_pathToLauncher); + const FilePath launcherFilePath = FilePath::fromString(m_private->launcherFilePath()) .cleanPath().withExecutableSuffix(); auto launcherIsNotExecutable = [&launcherFilePath]() { qWarning() << "The Creator's process launcher" << launcherFilePath << "is not executable."; }; QTC_ASSERT(launcherFilePath.isExecutableFile(), launcherIsNotExecutable(); return); + s_started = true; // Call in launcher's thread. - QMetaObject::invokeMethod(p, &LauncherInterfacePrivate::doStart); + QMetaObject::invokeMethod(m_private, &LauncherInterfacePrivate::doStart); } -// Called from main thread -void LauncherInterface::stopLauncher() +LauncherInterface::~LauncherInterface() { QMutexLocker locker(&s_instanceMutex); - QTC_ASSERT(s_instance != nullptr, return); - LauncherInterfacePrivate *p = s_instance->m_private; + LauncherInterfacePrivate *p = instance()->m_private; // Call in launcher's thread. QMetaObject::invokeMethod(p, &LauncherInterfacePrivate::doStop, Qt::BlockingQueuedConnection); - delete s_instance; - s_instance = nullptr; + m_thread.quit(); + m_thread.wait(); +} + +void LauncherInterface::setPathToLauncher(const QString &pathToLauncher) +{ + s_pathToLauncher = pathToLauncher; } bool LauncherInterface::isStarted() { - QMutexLocker locker(&s_instanceMutex); - return s_instance != nullptr; + return s_started; } bool LauncherInterface::isReady() { QMutexLocker locker(&s_instanceMutex); - QTC_ASSERT(s_instance != nullptr, return false); - - return s_instance->m_private->socket()->isReady(); + return instance()->m_private->socket()->isReady(); } void LauncherInterface::sendData(const QByteArray &data) { QMutexLocker locker(&s_instanceMutex); - QTC_ASSERT(s_instance != nullptr, return); - - s_instance->m_private->socket()->sendData(data); + instance()->m_private->socket()->sendData(data); } Utils::Internal::CallerHandle *LauncherInterface::registerHandle(QObject *parent, quintptr token, ProcessMode mode) { QMutexLocker locker(&s_instanceMutex); - QTC_ASSERT(s_instance != nullptr, return nullptr); - - return s_instance->m_private->socket()->registerHandle(parent, token, mode); + return instance()->m_private->socket()->registerHandle(parent, token, mode); } void LauncherInterface::unregisterHandle(quintptr token) { QMutexLocker locker(&s_instanceMutex); - QTC_ASSERT(s_instance != nullptr, return); - - s_instance->m_private->socket()->unregisterHandle(token); + instance()->m_private->socket()->unregisterHandle(token); } } // namespace Utils diff --git a/src/libs/utils/launcherinterface.h b/src/libs/utils/launcherinterface.h index 0df595af855..541501efeec 100644 --- a/src/libs/utils/launcherinterface.h +++ b/src/libs/utils/launcherinterface.h @@ -27,9 +27,10 @@ #include "utils_global.h" +#include "processreaper.h" #include "processutils.h" +#include "singleton.h" -#include #include namespace Utils { @@ -40,15 +41,11 @@ class LauncherInterfacePrivate; class ProcessLauncherImpl; } -class QTCREATOR_UTILS_EXPORT LauncherInterface : public QObject +class QTCREATOR_UTILS_EXPORT LauncherInterface final + : public SingletonWithOptionalDependencies { - Q_OBJECT public: - static void startLauncher(const QString &pathToLauncher = {}); - static void stopLauncher(); - -signals: - void errorOccurred(const QString &error); + static void setPathToLauncher(const QString &pathToLauncher); private: friend class Utils::Internal::CallerHandle; @@ -63,10 +60,11 @@ private: static void unregisterHandle(quintptr token); LauncherInterface(); - ~LauncherInterface() override; + ~LauncherInterface(); QThread m_thread; Internal::LauncherInterfacePrivate *m_private; + friend class SingletonWithOptionalDependencies; }; } // namespace Utils diff --git a/src/libs/utils/processreaper.cpp b/src/libs/utils/processreaper.cpp index b3679c7d957..c6d2a071f50 100644 --- a/src/libs/utils/processreaper.cpp +++ b/src/libs/utils/processreaper.cpp @@ -36,8 +36,6 @@ using namespace Utils; namespace Utils { namespace Internal { -static ProcessReaper *d = nullptr; - class Reaper final : public QObject { public: @@ -57,7 +55,7 @@ private: Reaper::Reaper(QProcess *p, int timeoutMs) : m_process(p) { - d->m_reapers.append(this); + ProcessReaper::instance()->m_reapers.append(this); m_iterationTimer.setInterval(timeoutMs); m_iterationTimer.setSingleShot(true); @@ -68,7 +66,7 @@ Reaper::Reaper(QProcess *p, int timeoutMs) : m_process(p) Reaper::~Reaper() { - d->m_reapers.removeOne(this); + ProcessReaper::instance()->m_reapers.removeOne(this); } int Reaper::timeoutMs() const @@ -112,15 +110,8 @@ void Reaper::nextIteration() } // namespace Internal -ProcessReaper::ProcessReaper() -{ - QTC_ASSERT(Internal::d == nullptr, return); - Internal::d = this; -} - ProcessReaper::~ProcessReaper() { - QTC_ASSERT(Internal::d == this, return); while (!m_reapers.isEmpty()) { int alreadyWaited = 0; QList toDelete; @@ -145,8 +136,6 @@ ProcessReaper::~ProcessReaper() qDeleteAll(toDelete); toDelete.clear(); } - - Internal::d = nullptr; } void ProcessReaper::reap(QProcess *process, int timeoutMs) @@ -175,8 +164,6 @@ void ProcessReaper::reap(QProcess *process, int timeoutMs) return; } - QTC_ASSERT(Internal::d, return); - new Internal::Reaper(process, timeoutMs); } diff --git a/src/libs/utils/processreaper.h b/src/libs/utils/processreaper.h index d26325b6f02..f8485c12e39 100644 --- a/src/libs/utils/processreaper.h +++ b/src/libs/utils/processreaper.h @@ -27,6 +27,8 @@ #include "utils_global.h" +#include "singleton.h" + #include QT_BEGIN_NAMESPACE @@ -38,15 +40,16 @@ namespace Utils { namespace Internal { class Reaper; } class QTCREATOR_UTILS_EXPORT ProcessReaper final + : public SingletonWithOptionalDependencies { public: - ProcessReaper(); - ~ProcessReaper(); - static void reap(QProcess *process, int timeoutMs = 500); private: + ProcessReaper() = default; + ~ProcessReaper(); QList m_reapers; friend class Internal::Reaper; + friend class SingletonWithOptionalDependencies; }; } // namespace Utils diff --git a/src/libs/utils/singleton.cpp b/src/libs/utils/singleton.cpp new file mode 100644 index 00000000000..6bd79ca36f6 --- /dev/null +++ b/src/libs/utils/singleton.cpp @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2021 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 "qtcassert.h" +#include "singleton.h" + +#include +#include +#include + +#include + +using namespace Utils; + +namespace Utils { + +// The order of elements reflects dependencies, i.e. +// if B requires A then B will follow A on this list +static QList s_singletonList; +static QMutex s_mutex; +static std::unordered_map s_staticDataList; + +Singleton::~Singleton() +{ + QMutexLocker locker(&s_mutex); + s_singletonList.removeAll(this); +} + +void Singleton::addSingleton(Singleton *singleton) +{ + QMutexLocker locker(&s_mutex); + s_singletonList.append(singleton); +} + +SingletonStaticData &Singleton::staticData(std::type_index index) +{ + QMutexLocker locker(&s_mutex); + return s_staticDataList[index]; +} + +// Note: it's caller responsibility to ensure that this function is being called when all other +// threads don't use any singleton. As a good practice: finish all other threads that were using +// singletons before this function is called. +// Some singletons (currently e.g. SshConnectionManager) can work only in main thread, +// so this method should be called from main thread only. +void Singleton::deleteAll() +{ + QTC_ASSERT(QThread::currentThread() == qApp->thread(), return); + QList oldList; + { + QMutexLocker locker(&s_mutex); + oldList = s_singletonList; + s_singletonList = {}; + } + // Keep the reverse order when deleting + while (!oldList.isEmpty()) + delete oldList.takeLast(); +} + +} // namespace Utils diff --git a/src/libs/utils/singleton.h b/src/libs/utils/singleton.h new file mode 100644 index 00000000000..4ba4dfecd03 --- /dev/null +++ b/src/libs/utils/singleton.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2021 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 "utils_global.h" + +#include +#include + +#include +#include + +namespace Utils { + +class Singleton; + +struct SingletonStaticData +{ + Singleton *m_instance = nullptr; + QMutex m_mutex; +}; + +class QTCREATOR_UTILS_EXPORT Singleton +{ + Q_DISABLE_COPY_MOVE(Singleton) +public: + static void deleteAll(); + +private: + template + friend class SingletonWithOptionalDependencies; + + Singleton() = default; + virtual ~Singleton(); + static void addSingleton(Singleton *singleton); + static SingletonStaticData &staticData(std::type_index index); +}; + +template +class SingletonWithOptionalDependencies : public Singleton +{ +public: + Q_DISABLE_COPY_MOVE(SingletonWithOptionalDependencies) + static SingletonSubClass *instance() + { + SingletonStaticData &data = staticData(); + QMutexLocker locker(&data.m_mutex); + if (data.m_instance == nullptr) { + // instantiate all dependencies first + if constexpr (sizeof...(Dependencies)) + instantiateDependencies(); + data.m_instance = new SingletonSubClass; + // put instance into static list of registered instances + addSingleton(data.m_instance); + } + return static_cast(data.m_instance); + } + +protected: + SingletonWithOptionalDependencies() = default; + ~SingletonWithOptionalDependencies() override + { + SingletonStaticData &data = staticData(); + QMutexLocker locker(&data.m_mutex); + if (data.m_instance == this) + data.m_instance = nullptr; + } + +private: + template static void instantiateDependencies() + { + static_assert ((... && std::is_base_of_v), + "All Dependencies must derive from SingletonWithOptionalDependencies class."); + (..., Dependency::instance()); + } + static SingletonStaticData &staticData() + { + static SingletonStaticData &data = Singleton::staticData(std::type_index(typeid(SingletonSubClass))); + return data; + } +}; + +} // namespace Utils diff --git a/src/libs/utils/utils-lib.pri b/src/libs/utils/utils-lib.pri index 7ceac359e8d..ddaae4bb697 100644 --- a/src/libs/utils/utils-lib.pri +++ b/src/libs/utils/utils-lib.pri @@ -143,6 +143,7 @@ SOURCES += \ $$PWD/link.cpp \ $$PWD/linecolumn.cpp \ $$PWD/multitextcursor.cpp \ + $$PWD/singleton.cpp HEADERS += \ $$PWD/environmentfwd.h \ @@ -307,8 +308,9 @@ HEADERS += \ $$PWD/launcherinterface.h \ $$PWD/launcherpackets.h \ $$PWD/launchersocket.h \ - $$PWD/qtcsettings.h + $$PWD/qtcsettings.h \ $$PWD/multitextcursor.h \ + $$PWD/singleton.h FORMS += $$PWD/filewizardpage.ui \ $$PWD/projectintropage.ui \ diff --git a/src/libs/utils/utils.qbs b/src/libs/utils/utils.qbs index e6b1d0621dd..77f80dd81b8 100644 --- a/src/libs/utils/utils.qbs +++ b/src/libs/utils/utils.qbs @@ -264,6 +264,8 @@ Project { "shellcommand.h", "shellcommandpage.cpp", "shellcommandpage.h", + "singleton.cpp", + "singleton.h", "sizedarray.h", "smallstring.h", "smallstringiterator.h", diff --git a/src/plugins/android/androidavdmanager.cpp b/src/plugins/android/androidavdmanager.cpp index 58204b1cc71..27af7b76db7 100644 --- a/src/plugins/android/androidavdmanager.cpp +++ b/src/plugins/android/androidavdmanager.cpp @@ -119,6 +119,7 @@ static CreateAvdInfo createAvdCommand(const AndroidConfig &config, const CreateA qCDebug(avdManagerLog) << "Running AVD Manager command:" << CommandLine(avdManagerTool, arguments).toUserOutput(); QProcess proc; + proc.setEnvironment(AndroidConfigurations::toolsEnvironment(config).toStringList()); proc.start(avdManagerTool.toString(), arguments); if (!proc.waitForStarted()) { result.error = QApplication::translate("AndroidAvdManager", diff --git a/src/plugins/android/androiddevice.cpp b/src/plugins/android/androiddevice.cpp index 26f9a446e2b..38d6e09ad8b 100644 --- a/src/plugins/android/androiddevice.cpp +++ b/src/plugins/android/androiddevice.cpp @@ -604,50 +604,20 @@ AndroidDeviceFactory::AndroidDeviceFactory() IDevice::Ptr AndroidDeviceFactory::create() const { - AndroidSdkManager sdkManager = AndroidSdkManager(m_androidConfig); - const CreateAvdInfo info = AvdDialog::gatherCreateAVDInfo(Core::ICore::dialogParent(), - &sdkManager, m_androidConfig); - if (!info.isValid()) { - if (!info.cancelled) { - AndroidDeviceWidget::criticalDialog( - QObject::tr("The returned device info is invalid.")); - } - return nullptr; + AvdDialog dialog = AvdDialog(m_androidConfig, Core::ICore::dialogParent()); + if (dialog.exec() != QDialog::Accepted) + return ProjectExplorer::IDevice::Ptr(); + + const ProjectExplorer::IDevice::Ptr dev = dialog.device(); + const AndroidDevice *androidDev = static_cast(dev.data()); + if (androidDev) { + qCDebug(androidDeviceLog, "Created new Android AVD id \"%s\".", + qPrintable(androidDev->avdName())); + } else { + AndroidDeviceWidget::criticalDialog( + QObject::tr("The device info returned from AvdDialog is invalid.")); } - const AndroidAvdManager avdManager = AndroidAvdManager(m_androidConfig); - QFutureWatcher createAvdFutureWatcher; - createAvdFutureWatcher.setFuture(avdManager.createAvd(info)); - - QEventLoop loop; - QObject::connect(&createAvdFutureWatcher, &QFutureWatcher::finished, - &loop, &QEventLoop::quit); - QObject::connect(&createAvdFutureWatcher, &QFutureWatcher::canceled, - &loop, &QEventLoop::quit); - loop.exec(QEventLoop::ExcludeUserInputEvents); - - QFuture future = createAvdFutureWatcher.future(); - if (!(future.isResultReadyAt(0) && future.result().isValid())) { - AndroidDeviceWidget::criticalDialog(QObject::tr("The device info returned by " - "avdmanager tool is invalid for the device name \"%1\".").arg(info.name)); - return nullptr; - } - - const CreateAvdInfo newAvdInfo = createAvdFutureWatcher.result(); - - AndroidDevice *dev = new AndroidDevice(); - const Utils::Id deviceId = AndroidDevice::idFromAvdInfo(newAvdInfo); - dev->setupId(IDevice::AutoDetected, deviceId); - dev->setMachineType(IDevice::Emulator); - dev->setDisplayName(newAvdInfo.name); - dev->setDeviceState(IDevice::DeviceConnected); - dev->setExtraData(Constants::AndroidAvdName, newAvdInfo.name); - dev->setExtraData(Constants::AndroidCpuAbi, {newAvdInfo.abi}); - dev->setExtraData(Constants::AndroidSdk, newAvdInfo.systemImage->apiLevel()); - dev->setExtraData(Constants::AndroidAvdSdcard, QString("%1 MB").arg(newAvdInfo.sdcardSize)); - dev->setExtraData(Constants::AndroidAvdDevice, newAvdInfo.deviceDefinition); - - qCDebug(androidDeviceLog, "Created new Android AVD id \"%s\".", qPrintable(newAvdInfo.name)); return IDevice::Ptr(dev); } diff --git a/src/plugins/android/androidsdkmanager.cpp b/src/plugins/android/androidsdkmanager.cpp index 246b528f1e6..943e371178a 100644 --- a/src/plugins/android/androidsdkmanager.cpp +++ b/src/plugins/android/androidsdkmanager.cpp @@ -694,7 +694,7 @@ bool SdkManagerOutputParser::parseAbstractData(SdkManagerOutputParser::GenericPa for (const auto &key: qAsConst(keys)) { if (valueForKey(key, line, &value)) { if (key == installLocationKey) - output.installedLocation = Utils::FilePath::fromString(value); + output.installedLocation = Utils::FilePath::fromUserInput(value); else if (key == revisionKey) output.revision = QVersionNumber::fromString(value); else if (key == descriptionKey) diff --git a/src/plugins/android/androidsettingswidget.cpp b/src/plugins/android/androidsettingswidget.cpp index d67c53a9637..44fc69e92b9 100644 --- a/src/plugins/android/androidsettingswidget.cpp +++ b/src/plugins/android/androidsettingswidget.cpp @@ -241,7 +241,7 @@ void AndroidSettingsWidget::updateNdkList() const auto installedPkgs = m_sdkManager.installedNdkPackages(); for (const Ndk *ndk : installedPkgs) { m_ui.ndkListWidget->addItem(new QListWidgetItem(Icons::LOCKED.icon(), - ndk->installedLocation().toString())); + ndk->installedLocation().toUserOutput())); } const auto customNdks = m_androidConfig.getCustomNdkList(); diff --git a/src/plugins/android/avddialog.cpp b/src/plugins/android/avddialog.cpp index 5f5a5cf6821..d3418f39ca8 100644 --- a/src/plugins/android/avddialog.cpp +++ b/src/plugins/android/avddialog.cpp @@ -26,6 +26,8 @@ #include "avddialog.h" #include "androidsdkmanager.h" #include "androidavdmanager.h" +#include "androiddevice.h" +#include "androidconstants.h" #include #include @@ -33,6 +35,7 @@ #include #include +#include #include #include #include @@ -46,29 +49,35 @@ namespace { static Q_LOGGING_CATEGORY(avdDialogLog, "qtc.android.avdDialog", QtWarningMsg) } -AvdDialog::AvdDialog(int minApiLevel, AndroidSdkManager *sdkManager, const QStringList &abis, - const AndroidConfig &config, QWidget *parent) : - QDialog(parent), - m_sdkManager(sdkManager), - m_minApiLevel(minApiLevel), - m_allowedNameChars(QLatin1String("[a-z|A-Z|0-9|._-]*")), - m_androidConfig(config) +AvdDialog::AvdDialog(const AndroidConfig &config, QWidget *parent) + : QDialog(parent), + m_sdkManager(m_androidConfig), + m_allowedNameChars(QLatin1String("[a-z|A-Z|0-9|._-]*")), + m_androidConfig(config) { - QTC_CHECK(m_sdkManager); m_avdDialog.setupUi(this); m_hideTipTimer.setInterval(2000); m_hideTipTimer.setSingleShot(true); - if (abis.isEmpty()) { - m_avdDialog.abiComboBox->addItems(QStringList({ - ProjectExplorer::Constants::ANDROID_ABI_X86, - ProjectExplorer::Constants::ANDROID_ABI_X86_64, - ProjectExplorer::Constants::ANDROID_ABI_ARMEABI_V7A, - ProjectExplorer::Constants::ANDROID_ABI_ARMEABI, - ProjectExplorer::Constants::ANDROID_ABI_ARM64_V8A})); - } else { - m_avdDialog.abiComboBox->addItems(abis); - } + connect(&m_hideTipTimer, &QTimer::timeout, this, &Utils::ToolTip::hide); + connect(m_avdDialog.deviceDefinitionTypeComboBox, + QOverload::of(&QComboBox::currentIndexChanged), + this, &AvdDialog::updateDeviceDefinitionComboBox); + connect(m_avdDialog.abiComboBox, + QOverload::of(&QComboBox::currentIndexChanged), + this, &AvdDialog::updateApiLevelComboBox); + + deviceTypeToStringMap.insert(AvdDialog::Phone, "Phone"); + deviceTypeToStringMap.insert(AvdDialog::Tablet, "Tablet"); + deviceTypeToStringMap.insert(AvdDialog::Automotive, "Automotive"); + deviceTypeToStringMap.insert(AvdDialog::TV, "TV"); + deviceTypeToStringMap.insert(AvdDialog::Wear, "Wear"); + + m_avdDialog.abiComboBox->addItems(QStringList({ + ProjectExplorer::Constants::ANDROID_ABI_X86, + ProjectExplorer::Constants::ANDROID_ABI_X86_64, + ProjectExplorer::Constants::ANDROID_ABI_ARMEABI_V7A, + ProjectExplorer::Constants::ANDROID_ABI_ARM64_V8A})); auto v = new QRegularExpressionValidator(m_allowedNameChars, this); m_avdDialog.nameLineEdit->setValidator(v); @@ -77,46 +86,70 @@ AvdDialog::AvdDialog(int minApiLevel, AndroidSdkManager *sdkManager, const QStri m_avdDialog.warningText->setType(Utils::InfoLabel::Warning); m_avdDialog.warningText->setElideMode(Qt::ElideNone); - connect(&m_hideTipTimer, &QTimer::timeout, this, []() { Utils::ToolTip::hide(); }); - parseDeviceDefinitionsList(); - for (const QString &type : DeviceTypeToStringMap) + for (const QString &type : deviceTypeToStringMap) m_avdDialog.deviceDefinitionTypeComboBox->addItem(type); - connect(m_avdDialog.deviceDefinitionTypeComboBox, - QOverload::of(&QComboBox::currentIndexChanged), - this, - &AvdDialog::updateDeviceDefinitionComboBox); - connect(m_avdDialog.abiComboBox, - QOverload::of(&QComboBox::currentIndexChanged), - this, &AvdDialog::updateApiLevelComboBox); - - m_avdDialog.deviceDefinitionTypeComboBox->setCurrentIndex(1); // Set Phone type as default index updateApiLevelComboBox(); } +int AvdDialog::exec() +{ + const int execResult = QDialog::exec(); + if (execResult == QDialog::Accepted) { + CreateAvdInfo result; + result.systemImage = systemImage(); + result.name = name(); + result.abi = abi(); + result.deviceDefinition = deviceDefinition(); + result.sdcardSize = sdcardSize(); + result.overwrite = m_avdDialog.overwriteCheckBox->isChecked(); + + const AndroidAvdManager avdManager = AndroidAvdManager(m_androidConfig); + QFutureWatcher createAvdFutureWatcher; + createAvdFutureWatcher.setFuture(avdManager.createAvd(result)); + + QEventLoop loop; + QObject::connect(&createAvdFutureWatcher, &QFutureWatcher::finished, + &loop, &QEventLoop::quit); + QObject::connect(&createAvdFutureWatcher, &QFutureWatcher::canceled, + &loop, &QEventLoop::quit); + loop.exec(QEventLoop::ExcludeUserInputEvents); + + const QFuture future = createAvdFutureWatcher.future(); + if (future.isResultReadyAt(0)) + m_createdAvdInfo = future.result(); + } + + return execResult; +} + bool AvdDialog::isValid() const { return !name().isEmpty() && systemImage() && systemImage()->isValid() && !abi().isEmpty(); } -CreateAvdInfo AvdDialog::gatherCreateAVDInfo(QWidget *parent, AndroidSdkManager *sdkManager, - const AndroidConfig &config, int minApiLevel, const QStringList &abis) +ProjectExplorer::IDevice::Ptr AvdDialog::device() const { - CreateAvdInfo result; - AvdDialog d(minApiLevel, sdkManager, abis, config, parent); - result.cancelled = (d.exec() != QDialog::Accepted); - if (result.cancelled || !d.isValid()) - return result; + AndroidDevice *dev = new AndroidDevice(); + const Utils::Id deviceId = AndroidDevice::idFromAvdInfo(m_createdAvdInfo); + using namespace ProjectExplorer; + dev->setupId(IDevice::AutoDetected, deviceId); + dev->setMachineType(IDevice::Emulator); + dev->setDisplayName(m_createdAvdInfo.name); + dev->setDeviceState(IDevice::DeviceConnected); + dev->setExtraData(Constants::AndroidAvdName, m_createdAvdInfo.name); + dev->setExtraData(Constants::AndroidCpuAbi, {m_createdAvdInfo.abi}); + if (!m_createdAvdInfo.systemImage) { + qCWarning(avdDialogLog) << "System image of the created AVD is nullptr"; + return IDevice::Ptr(); + } + dev->setExtraData(Constants::AndroidSdk, m_createdAvdInfo.systemImage->apiLevel()); + dev->setExtraData(Constants::AndroidAvdSdcard, QString("%1 MB") + .arg(m_createdAvdInfo.sdcardSize)); + dev->setExtraData(Constants::AndroidAvdDevice, m_createdAvdInfo.deviceDefinition); - result.systemImage = d.systemImage(); - result.name = d.name(); - result.abi = d.abi(); - result.deviceDefinition = d.deviceDefinition(); - result.sdcardSize = d.sdcardSize(); - result.overwrite = d.m_avdDialog.overwriteCheckBox->isChecked(); - - return result; + return IDevice::Ptr(dev); } AvdDialog::DeviceType AvdDialog::tagToDeviceType(const QString &type_tag) @@ -175,7 +208,7 @@ void AvdDialog::parseDeviceDefinitionsList() void AvdDialog::updateDeviceDefinitionComboBox() { - DeviceType curDeviceType = DeviceTypeToStringMap.key( + DeviceType curDeviceType = deviceTypeToStringMap.key( m_avdDialog.deviceDefinitionTypeComboBox->currentText()); m_avdDialog.deviceDefinitionComboBox->clear(); @@ -215,8 +248,8 @@ int AvdDialog::sdcardSize() const void AvdDialog::updateApiLevelComboBox() { - SystemImageList installedSystemImages = m_sdkManager->installedSystemImages(); - DeviceType curDeviceType = DeviceTypeToStringMap.key( + SystemImageList installedSystemImages = m_sdkManager.installedSystemImages(); + DeviceType curDeviceType = deviceTypeToStringMap.key( m_avdDialog.deviceDefinitionTypeComboBox->currentText()); QString selectedAbi = abi(); @@ -250,8 +283,7 @@ void AvdDialog::updateApiLevelComboBox() m_avdDialog.warningText->setVisible(true); m_avdDialog.warningText->setText( tr("Cannot create a new AVD. No suitable Android system image is installed.
" - "Install a system image of at least API version %1 from the SDK Manager tab.") - .arg(m_minApiLevel)); + "Install a system image for the intended Android version from the SDK Manager.")); m_avdDialog.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); } else if (filteredList.isEmpty()) { m_avdDialog.targetApiComboBox->setEnabled(false); diff --git a/src/plugins/android/avddialog.h b/src/plugins/android/avddialog.h index 9813cc813ba..3f95bed2130 100644 --- a/src/plugins/android/avddialog.h +++ b/src/plugins/android/avddialog.h @@ -41,21 +41,27 @@ class AvdDialog : public QDialog { Q_OBJECT public: - explicit AvdDialog(int minApiLevel, - AndroidSdkManager *sdkManager, - const QStringList &abis, - const AndroidConfig &config, - QWidget *parent = nullptr); + explicit AvdDialog(const AndroidConfig &config, QWidget *parent = nullptr); + int exec() override; - enum DeviceType { TV, Phone, Wear, Tablet, Automotive, PhoneOrTablet }; + enum DeviceType { Phone, Tablet, Automotive, TV, Wear, PhoneOrTablet }; - const QMap DeviceTypeToStringMap{ - {TV, "TV"}, - {Phone, "Phone"}, - {Wear, "Wear"}, - {Tablet, "Tablet"}, - {Automotive, "Automotive"} - }; + ProjectExplorer::IDevice::Ptr device() const; + + const SystemImage *systemImage() const; + QString name() const; + QString abi() const; + QString deviceDefinition() const; + int sdcardSize() const; + bool isValid() const; + +private: + void parseDeviceDefinitionsList(); + void updateDeviceDefinitionComboBox(); + void updateApiLevelComboBox(); + bool eventFilter(QObject *obj, QEvent *event) override; + + static AvdDialog::DeviceType tagToDeviceType(const QString &type_tag); struct DeviceDefinitionStruct { @@ -64,30 +70,14 @@ public: DeviceType deviceType; }; - const SystemImage *systemImage() const; - QString name() const; - QString abi() const; - QString deviceDefinition() const; - int sdcardSize() const; - bool isValid() const; - static AvdDialog::DeviceType tagToDeviceType(const QString &type_tag); - static CreateAvdInfo gatherCreateAVDInfo(QWidget *parent, AndroidSdkManager *sdkManager, - const AndroidConfig &config, - int minApiLevel = 0, const QStringList &abis = {}); - -private: - void parseDeviceDefinitionsList(); - void updateDeviceDefinitionComboBox(); - void updateApiLevelComboBox(); - bool eventFilter(QObject *obj, QEvent *event) override; - Ui::AddNewAVDDialog m_avdDialog; - AndroidSdkManager *m_sdkManager; - int m_minApiLevel; + AndroidSdkManager m_sdkManager; + CreateAvdInfo m_createdAvdInfo; QTimer m_hideTipTimer; QRegularExpression m_allowedNameChars; QList m_deviceDefinitionsList; AndroidConfig m_androidConfig; + QMap deviceTypeToStringMap; }; } } diff --git a/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp b/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp index 363aac03c38..af964f5be95 100644 --- a/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp +++ b/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp @@ -568,7 +568,7 @@ void ClangCompletionAssistProcessor::completeIncludePath(const QString &realPath bool ClangCompletionAssistProcessor::completePreprocessorDirectives() { - foreach (const QString &preprocessorCompletion, m_preprocessorCompletions) + foreach (const QString &preprocessorCompletion, preprocessorCompletions()) addCompletionItem(preprocessorCompletion, Utils::CodeModelIcon::iconForType(Utils::CodeModelIcon::Macro)); diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp index e9212b3d765..6103fada825 100644 --- a/src/plugins/clangcodemodel/clangdclient.cpp +++ b/src/plugins/clangcodemodel/clangdclient.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -79,6 +80,7 @@ using namespace Core; using namespace LanguageClient; using namespace LanguageServerProtocol; using namespace ProjectExplorer; +using namespace TextEditor; namespace ClangCodeModel { namespace Internal { @@ -89,32 +91,6 @@ static Q_LOGGING_CATEGORY(clangdLogAst, "qtc.clangcodemodel.clangd.ast", QtWarni static Q_LOGGING_CATEGORY(clangdLogHighlight, "qtc.clangcodemodel.clangd.highlight", QtWarningMsg); static QString indexingToken() { return "backgroundIndexProgress"; } -class AstParams : public JsonObject -{ -public: - AstParams() {} - AstParams(const TextDocumentIdentifier &document, const Range &range = {}) - { - setTextDocument(document); - if (range.isValid()) - setRange(range); - } - - using JsonObject::JsonObject; - - // The open file to inspect. - TextDocumentIdentifier textDocument() const - { return typedValue(textDocumentKey); } - void setTextDocument(const TextDocumentIdentifier &id) { insert(textDocumentKey, id); } - - // The region of the source code whose AST is fetched. The highest-level node that entirely - // contains the range is returned. - Utils::optional range() const { return optionalValue(rangeKey); } - void setRange(const Range &range) { insert(rangeKey, range); } - - bool isValid() const override { return contains(textDocumentKey); } -}; - class AstNode : public JsonObject { public: @@ -329,6 +305,14 @@ static QList getAstPath(const AstNode &root, const Range &range) return path; } +static AstNode getAstNode(const AstNode &root, const Range &range) +{ + const QList path = getAstPath(root, range); + if (!path.isEmpty()) + return path.last(); + return {}; +} + static Usage::Type getUsageType(const QList &path) { bool potentialWrite = false; @@ -401,13 +385,6 @@ static Usage::Type getUsageType(const QList &path) return Usage::Type::Other; } -class AstRequest : public Request -{ -public: - using Request::Request; - explicit AstRequest(const AstParams ¶ms) : Request("textDocument/ast", params) {} -}; - class SymbolDetails : public JsonObject { public: @@ -487,7 +464,7 @@ public: using SymbolData = QPair; using SymbolDataList = QList; -class ClangdClient::VirtualFunctionAssistProcessor : public TextEditor::IAssistProcessor +class ClangdClient::VirtualFunctionAssistProcessor : public IAssistProcessor { public: VirtualFunctionAssistProcessor(ClangdClient::Private *data) : m_data(data) {} @@ -499,34 +476,34 @@ public: void finalize(); private: - TextEditor::IAssistProposal *perform(const TextEditor::AssistInterface *) override + IAssistProposal *perform(const AssistInterface *) override { return nullptr; } - TextEditor::IAssistProposal *immediateProposal(const TextEditor::AssistInterface *) override + IAssistProposal *immediateProposal(const AssistInterface *) override { return createProposal(false); } void resetData(); - TextEditor::IAssistProposal *immediateProposalImpl() const; - TextEditor::IAssistProposal *createProposal(bool final) const; + IAssistProposal *immediateProposalImpl() const; + IAssistProposal *createProposal(bool final) const; CppEditor::VirtualFunctionProposalItem *createEntry(const QString &name, const Utils::Link &link) const; ClangdClient::Private *m_data = nullptr; }; -class ClangdClient::VirtualFunctionAssistProvider : public TextEditor::IAssistProvider +class ClangdClient::VirtualFunctionAssistProvider : public IAssistProvider { public: VirtualFunctionAssistProvider(ClangdClient::Private *data) : m_data(data) {} private: RunType runType() const override { return Asynchronous; } - TextEditor::IAssistProcessor *createProcessor(const TextEditor::AssistInterface *) const override; + IAssistProcessor *createProcessor(const AssistInterface *) const override; ClangdClient::Private * const m_data; }; @@ -588,7 +565,7 @@ public: class SwitchDeclDefData { public: - SwitchDeclDefData(quint64 id, TextEditor::TextDocument *doc, const QTextCursor &cursor, + SwitchDeclDefData(quint64 id, TextDocument *doc, const QTextCursor &cursor, CppEditor::CppEditorWidget *editorWidget, Utils::ProcessLinkCallback &&callback) : id(id), document(doc), uri(DocumentUri::fromFilePath(doc->filePath())), @@ -629,7 +606,7 @@ public: } const quint64 id; - const QPointer document; + const QPointer document; const DocumentUri uri; const QTextCursor cursor; const QPointer editorWidget; @@ -640,8 +617,8 @@ public: class LocalRefsData { public: - LocalRefsData(quint64 id, TextEditor::TextDocument *doc, const QTextCursor &cursor, - CppEditor::RefactoringEngineInterface::RenameCallback &&callback) + LocalRefsData(quint64 id, TextDocument *doc, const QTextCursor &cursor, + CppEditor::RefactoringEngineInterface::RenameCallback &&callback) : id(id), document(doc), cursor(cursor), callback(std::move(callback)), uri(DocumentUri::fromFilePath(doc->filePath())), revision(doc->document()->revision()) {} @@ -653,7 +630,7 @@ public: } const quint64 id; - const QPointer document; + const QPointer document; const QTextCursor cursor; CppEditor::RefactoringEngineInterface::RenameCallback callback; const DocumentUri uri; @@ -678,37 +655,122 @@ public: { insert("publishDiagnostics", caps); } }; -class DoxygenAssistProcessor : public TextEditor::IAssistProcessor + +enum class CustomAssistMode { Doxygen, Preprocessor }; +class CustomAssistProcessor : public IAssistProcessor { public: - DoxygenAssistProcessor(int position, unsigned completionOperator, - const ProposalHandler &handler) - : m_position(position), m_completionOperator(completionOperator), m_handler(handler) {} + CustomAssistProcessor(ClangdClient *client, int position, unsigned completionOperator, + CustomAssistMode mode) + : m_client(client) + , m_position(position) + , m_completionOperator(completionOperator) + , m_mode(mode) + {} private: - TextEditor::IAssistProposal *perform(const TextEditor::AssistInterface *) override + IAssistProposal *perform(const AssistInterface *interface) override { - QList completions; - for (int i = 1; i < CppEditor::T_DOXY_LAST_TAG; ++i) { - const auto item = new ClangPreprocessorAssistProposalItem; - item->setText(QLatin1String(CppEditor::doxygenTagSpell(i))); - item->setIcon(CPlusPlus::Icons::keywordIcon()); - item->setCompletionOperator(m_completionOperator); - completions.append(item); + QList completions; + switch (m_mode) { + case CustomAssistMode::Doxygen: + for (int i = 1; i < CppEditor::T_DOXY_LAST_TAG; ++i) { + completions << createItem(QLatin1String(CppEditor::doxygenTagSpell(i)), + CPlusPlus::Icons::keywordIcon()); + } + break; + case CustomAssistMode::Preprocessor: + static QIcon macroIcon = Utils::CodeModelIcon::iconForType(Utils::CodeModelIcon::Macro); + for (const QString &completion + : CppEditor::CppCompletionAssistProcessor::preprocessorCompletions()) + completions << createItem(completion, macroIcon); + const CppEditor::ProjectFile::Kind fileType + = CppEditor::ProjectFile::classify(interface->filePath().toString()); + switch (fileType) { + case CppEditor::ProjectFile::ObjCHeader: + case CppEditor::ProjectFile::ObjCXXHeader: + case CppEditor::ProjectFile::ObjCSource: + case CppEditor::ProjectFile::ObjCXXSource: + completions << createItem("import", macroIcon); + break; + default: + break; + } } - TextEditor::GenericProposalModelPtr model(new TextEditor::GenericProposalModel); + GenericProposalModelPtr model(new GenericProposalModel); model->loadContent(completions); - const auto proposal = new TextEditor::GenericProposal(m_position, model); - if (m_handler) { - m_handler(proposal); + const auto proposal = new GenericProposal(m_position, model); + if (m_client->testingEnabled()) { + emit m_client->proposalReady(proposal); return nullptr; } return proposal; } + AssistProposalItemInterface *createItem(const QString &text, const QIcon &icon) const + { + const auto item = new ClangPreprocessorAssistProposalItem; + item->setText(text); + item->setIcon(icon); + item->setCompletionOperator(m_completionOperator); + return item; + } + + ClangdClient * const m_client; const int m_position; const unsigned m_completionOperator; - const ProposalHandler m_handler; + const CustomAssistMode m_mode; +}; + +static qint64 getRevision(const TextDocument *doc) +{ + return doc->document()->revision(); +} +static qint64 getRevision(const Utils::FilePath &fp) +{ + return fp.lastModified().toMSecsSinceEpoch(); +} + +template class VersionedDocData +{ +public: + VersionedDocData(const DocType &doc, const DataType &data) : + revision(getRevision(doc)), data(data) {} + + const qint64 revision; + const DataType data; +}; + +template class VersionedDataCache +{ +public: + void insert(const DocType &doc, const DataType &data) + { + m_data.emplace(std::make_pair(doc, VersionedDocData(doc, data))); + } + void remove(const DocType &doc) { m_data.erase(doc); } + Utils::optional> take(const DocType &doc) + { + const auto it = m_data.find(doc); + if (it == m_data.end()) + return {}; + const auto data = it->second; + m_data.erase(it); + return data; + } + Utils::optional get(const DocType &doc) + { + const auto it = m_data.find(doc); + if (it == m_data.end()) + return {}; + if (it->second.revision != getRevision(doc)) { + m_data.erase(it); + return {}; + } + return it->second.data; + } +private: + std::unordered_map> m_data; }; class ClangdClient::Private @@ -717,7 +779,7 @@ public: Private(ClangdClient *q, Project *project) : q(q), settings(CppEditor::ClangdProjectSettings(project).settings()) {} - void findUsages(TextEditor::TextDocument *document, const QTextCursor &cursor, + void findUsages(TextDocument *document, const QTextCursor &cursor, const QString &searchTerm, const Utils::optional &replacement, bool categorize); void handleFindUsagesResult(quint64 key, const QList &locations); @@ -744,8 +806,13 @@ public: HelpItem::Category category = HelpItem::Unknown, const QString &type = {}); - void handleSemanticTokens(TextEditor::TextDocument *doc, - const QList &tokens); + void handleSemanticTokens(TextDocument *doc, const QList &tokens); + + enum class AstCallbackMode { SyncIfPossible, AlwaysAsync }; + using TextDocOrFile = const Utils::variant; + using AstHandler = const std::function; + MessageId getAndHandleAst(TextDocOrFile &doc, AstHandler &astHandler, + AstCallbackMode callbackMode); ClangdClient * const q; const CppEditor::ClangdSettings::Data settings; @@ -754,7 +821,9 @@ public: Utils::optional switchDeclDefData; Utils::optional localRefsData; Utils::optional versionNumber; - std::unordered_map highlighters; + std::unordered_map highlighters; + VersionedDataCache astCache; + VersionedDataCache externalAstCache; quint64 nextJobId = 0; bool isFullyIndexed = false; bool isTesting = false; @@ -770,22 +839,94 @@ public: } }; +class ClangdCompletionItem : public LanguageClientCompletionItem +{ +public: + using LanguageClientCompletionItem::LanguageClientCompletionItem; + void apply(TextDocumentManipulatorInterface &manipulator, + int basePosition) const override; +}; + +class ClangdClient::ClangdCompletionAssistProcessor : public LanguageClientCompletionAssistProcessor +{ +public: + ClangdCompletionAssistProcessor(ClangdClient *client, const QString &snippetsGroup) + : LanguageClientCompletionAssistProcessor(client, snippetsGroup) + , m_client(client) + { + } + +private: + IAssistProposal *perform(const AssistInterface *interface) override + { + if (m_client->d->isTesting) { + setAsyncCompletionAvailableHandler([this](IAssistProposal *proposal) { + emit m_client->proposalReady(proposal); + }); + } + return LanguageClientCompletionAssistProcessor::perform(interface); + } + + QList generateCompletionItems( + const QList &items) const override; + + ClangdClient * const m_client; +}; + +QList +ClangdClient::ClangdCompletionAssistProcessor::generateCompletionItems( + const QList &items) const +{ + qCDebug(clangdLog) << "received" << items.count() << "completions"; + + auto itemGenerator = [](const QList &items) { + return Utils::transform>( + items, [](const LanguageServerProtocol::CompletionItem &item) { + return new ClangdCompletionItem(item); + }); + }; + + // If there are signals among the candidates, we employ the built-in code model to find out + // whether the cursor was on the second argument of a (dis)connect() call. + // If so, we offer only signals, as nothing else makes sense in that context. + static const auto criterion = [](const CompletionItem &ci) { + const Utils::optional doc = ci.documentation(); + if (!doc) + return false; + QString docText; + if (Utils::holds_alternative(*doc)) + docText = Utils::get(*doc); + else if (Utils::holds_alternative(*doc)) + docText = Utils::get(*doc).content(); + return docText.contains("Annotation: qt_signal"); + }; + const QTextDocument *doc = document(); + const int pos = basePos(); + if (!doc || pos < 0 || !Utils::anyOf(items, criterion)) + return itemGenerator(items); + const QString content = doc->toPlainText(); + const bool requiresSignal = CppEditor::CppModelManager::instance() + ->positionRequiresSignal(filePath().toString(), + content.toUtf8(), + pos); + if (requiresSignal) + return itemGenerator(Utils::filtered(items, criterion)); + return itemGenerator(items); +} + class ClangdClient::ClangdCompletionAssistProvider : public LanguageClientCompletionAssistProvider { public: ClangdCompletionAssistProvider(ClangdClient *client); private: - TextEditor::IAssistProcessor *createProcessor( - const TextEditor::AssistInterface *assistInterface) const override; + IAssistProcessor *createProcessor(const AssistInterface *assistInterface) const override; int activationCharSequenceLength() const override { return 3; } bool isActivationCharSequence(const QString &sequence) const override; bool isContinuationChar(const QChar &c) const override; - void applyCompletionItem(const CompletionItem &item, - TextEditor::TextDocumentManipulatorInterface &manipulator, - QChar typedChar); + ClangdClient * const m_client; }; ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir) @@ -862,12 +1003,9 @@ ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir) } }; setSymbolStringifier(symbolStringifier); - - setSemanticTokensHandler([this](TextEditor::TextDocument *doc, - const QList &tokens) { + setSemanticTokensHandler([this](TextDocument *doc, const QList &tokens) { d->handleSemanticTokens(doc, tokens); }); - hoverHandler()->setHelpItemProvider([this](const HoverRequest::Response &response, const DocumentUri &uri) { gatherHelpItemForTooltip(response, uri); @@ -942,7 +1080,7 @@ void ClangdClient::closeExtraFile(const Utils::FilePath &filePath) SendDocUpdates::Ignore); } -void ClangdClient::findUsages(TextEditor::TextDocument *document, const QTextCursor &cursor, +void ClangdClient::findUsages(TextDocument *document, const QTextCursor &cursor, const Utils::optional &replacement) { // Quick check: Are we even on anything searchable? @@ -987,9 +1125,19 @@ void ClangdClient::handleDiagnostics(const PublishDiagnosticsParams ¶ms) } } -void ClangdClient::handleDocumentClosed(TextEditor::TextDocument *doc) +void ClangdClient::handleDocumentOpened(TextDocument *doc) +{ + const auto data = d->externalAstCache.take(doc->filePath()); + if (!data) + return; + if (data->revision == getRevision(doc->filePath())) + d->astCache.insert(doc, data->data); +} + +void ClangdClient::handleDocumentClosed(TextDocument *doc) { d->highlighters.erase(doc); + d->astCache.remove(doc); } QVersionNumber ClangdClient::versionNumber() const @@ -1012,7 +1160,7 @@ QVersionNumber ClangdClient::versionNumber() const CppEditor::ClangdSettings::Data ClangdClient::settingsData() const { return d->settings; } -void ClangdClient::Private::findUsages(TextEditor::TextDocument *document, +void ClangdClient::Private::findUsages(TextDocument *document, const QTextCursor &cursor, const QString &searchTerm, const Utils::optional &replacement, bool categorize) { @@ -1081,13 +1229,11 @@ void ClangdClient::Private::findUsages(TextEditor::TextDocument *document, void ClangdClient::enableTesting() { d->isTesting = true; - setCompletionProposalHandler([this](TextEditor::IAssistProposal *proposal) { - QMetaObject::invokeMethod(this, [this, proposal] { emit proposalReady(proposal); }, - Qt::QueuedConnection); - }); - setFunctionHintProposalHandler([this](TextEditor::IAssistProposal *proposal) { - emit proposalReady(proposal); - }); +} + +bool ClangdClient::testingEnabled() const +{ + return d->isTesting; } void ClangdClient::Private::handleFindUsagesResult(quint64 key, const QList &locations) @@ -1140,27 +1286,22 @@ void ClangdClient::Private::handleFindUsagesResult(quint64 key, const QListfileData.begin(); it != refData->fileData.end(); ++it) { - const bool extraOpen = !q->documentForFilePath(it.key().toFilePath()); - if (extraOpen) + const TextDocument * const doc = q->documentForFilePath(it.key().toFilePath()); + if (!doc) q->openExtraFile(it.key().toFilePath(), it->fileContent); it->fileContent.clear(); - - AstParams params; - params.setTextDocument(TextDocumentIdentifier(it.key())); - AstRequest request(params); - request.setResponseCallback([this, key, loc = it.key(), request] - (AstRequest::Response response) { - qCDebug(clangdLog) << "AST response for" << loc.toFilePath(); + const auto docVariant = doc ? TextDocOrFile(doc) : TextDocOrFile(it.key().toFilePath()); + const auto astHandler = [this, key, loc = it.key()](const AstNode &ast, + const MessageId &reqId) { + qCDebug(clangdLog) << "AST for" << loc.toFilePath(); const auto refData = runningFindUsages.find(key); if (refData == runningFindUsages.end()) return; if (!refData->search || refData->canceled) return; ReferencesFileData &data = refData->fileData[loc]; - const auto result = response.result(); - if (result) - data.ast = *result; - refData->pendingAstRequests.removeOne(request.id()); + data.ast = ast; + refData->pendingAstRequests.removeOne(reqId); qCDebug(clangdLog) << refData->pendingAstRequests.size() << "AST requests still pending"; addSearchResultsForFile(*refData, loc.toFilePath(), data); @@ -1169,12 +1310,11 @@ void ClangdClient::Private::handleFindUsagesResult(quint64 key, const QListpendingAstRequests << request.id(); - q->sendContent(request, SendDocUpdates::Ignore); - - if (extraOpen) + }; + const MessageId reqId = getAndHandleAst(docVariant, astHandler, + AstCallbackMode::AlwaysAsync); + refData->pendingAstRequests << reqId; + if (!doc) q->closeExtraFile(it.key().toFilePath()); } } @@ -1185,9 +1325,8 @@ void ClangdClient::Private::handleRenameRequest(const SearchResult *search, const QList &checkedItems, bool preserveCase) { - const Utils::FilePaths filePaths = TextEditor::BaseFileFind::replaceAll(newSymbolName, - checkedItems, - preserveCase); + const Utils::FilePaths filePaths = BaseFileFind::replaceAll(newSymbolName, checkedItems, + preserveCase); if (!filePaths.isEmpty()) SearchResultWindow::instance()->hide(); @@ -1271,7 +1410,7 @@ void ClangdClient::Private::finishSearch(const ReferencesData &refData, bool can runningFindUsages.remove(refData.key); } -void ClangdClient::followSymbol(TextEditor::TextDocument *document, +void ClangdClient::followSymbol(TextDocument *document, const QTextCursor &cursor, CppEditor::CppEditorWidget *editorWidget, Utils::ProcessLinkCallback &&callback, @@ -1314,25 +1453,19 @@ void ClangdClient::followSymbol(TextEditor::TextDocument *document, return; } - AstRequest astRequest(AstParams(TextDocumentIdentifier(d->followSymbolData->uri), - Range(cursor))); - astRequest.setResponseCallback([this, id = d->followSymbolData->id]( - const AstRequest::Response &response) { + const auto astHandler = [this, id = d->followSymbolData->id, range = Range(cursor)] + (const AstNode &ast, const MessageId &) { qCDebug(clangdLog) << "received ast response for cursor"; if (!d->followSymbolData || d->followSymbolData->id != id) return; - const auto result = response.result(); - if (result) - d->followSymbolData->cursorNode = *result; - else - d->followSymbolData->cursorNode.emplace(AstNode()); + d->followSymbolData->cursorNode = getAstNode(ast, range); if (d->followSymbolData->defLink.hasValidTarget()) d->handleGotoDefinitionResult(); - }); - sendContent(astRequest, SendDocUpdates::Ignore); + }; + d->getAndHandleAst(document, astHandler, Private::AstCallbackMode::SyncIfPossible); } -void ClangdClient::switchDeclDef(TextEditor::TextDocument *document, const QTextCursor &cursor, +void ClangdClient::switchDeclDef(TextDocument *document, const QTextCursor &cursor, CppEditor::CppEditorWidget *editorWidget, Utils::ProcessLinkCallback &&callback) { @@ -1344,31 +1477,26 @@ void ClangdClient::switchDeclDef(TextEditor::TextDocument *document, const QText std::move(callback)); // Retrieve AST and document symbols. - AstParams astParams; - astParams.setTextDocument(TextDocumentIdentifier(d->switchDeclDefData->uri)); - AstRequest astRequest(astParams); - astRequest.setResponseCallback([this, id = d->switchDeclDefData->id] - (const AstRequest::Response &response) { + const auto astHandler = [this, id = d->switchDeclDefData->id](const AstNode &ast, + const MessageId &) { qCDebug(clangdLog) << "received ast for decl/def switch"; if (!d->switchDeclDefData || d->switchDeclDefData->id != id || !d->switchDeclDefData->document) return; - const auto result = response.result(); - if (!result) { + if (!ast.isValid()) { d->switchDeclDefData.reset(); return; } - d->switchDeclDefData->ast = *result; + d->switchDeclDefData->ast = ast; if (d->switchDeclDefData->docSymbols) d->handleDeclDefSwitchReplies(); - }); - sendContent(astRequest, SendDocUpdates::Ignore); + }; + d->getAndHandleAst(document, astHandler, Private::AstCallbackMode::SyncIfPossible); documentSymbolCache()->requestSymbols(d->switchDeclDefData->uri, Schedule::Now); - } -void ClangdClient::findLocalUsages(TextEditor::TextDocument *document, const QTextCursor &cursor, +void ClangdClient::findLocalUsages(TextDocument *document, const QTextCursor &cursor, CppEditor::RefactoringEngineInterface::RenameCallback &&callback) { QTC_ASSERT(documentOpen(document), openDocument(document)); @@ -1395,19 +1523,17 @@ void ClangdClient::findLocalUsages(TextEditor::TextDocument *document, const QTe } // Step 2: Get AST and check whether it's a local variable. - AstRequest astRequest(AstParams(TextDocumentIdentifier(d->localRefsData->uri))); - astRequest.setResponseCallback([this, link, id](const AstRequest::Response &response) { + const auto astHandler = [this, link, id](const AstNode &ast, const MessageId &) { qCDebug(clangdLog) << "received ast response"; if (!d->localRefsData || id != d->localRefsData->id) return; - const auto result = response.result(); - if (!result || !d->localRefsData->document) { + if (!ast.isValid() || !d->localRefsData->document) { d->localRefsData.reset(); return; } const Position linkPos(link.targetLine - 1, link.targetColumn); - const QList astPath = getAstPath(*result, Range(linkPos, linkPos)); + const QList astPath = getAstPath(ast, Range(linkPos, linkPos)); bool isVar = false; for (auto it = astPath.rbegin(); it != astPath.rend(); ++it) { if (it->role() == "declaration" && it->kind() == "Function") { @@ -1448,9 +1574,10 @@ void ClangdClient::findLocalUsages(TextEditor::TextDocument *document, const QTe } } d->localRefsData.reset(); - }); + }; qCDebug(clangdLog) << "sending ast request for link"; - sendContent(astRequest, SendDocUpdates::Ignore); + d->getAndHandleAst(d->localRefsData->document, astHandler, + Private::AstCallbackMode::SyncIfPossible); }; symbolSupport().findLinkAt(document, cursor, std::move(gotoDefCallback), true); } @@ -1492,10 +1619,10 @@ void ClangdClient::gatherHelpItemForTooltip(const HoverRequest::Response &hoverR } } - AstRequest req((AstParams(TextDocumentIdentifier(uri)))); - req.setResponseCallback([this, uri, hoverResponse](const AstRequest::Response &response) { + const TextDocument * const doc = documentForFilePath(uri.toFilePath()); + QTC_ASSERT(doc, return); + const auto astHandler = [this, uri, hoverResponse](const AstNode &ast, const MessageId &) { const MessageId id = hoverResponse.id(); - const AstNode ast = response.result().value_or(AstNode()); const Range range = hoverResponse.result()->range().value_or(Range()); const QList path = getAstPath(ast, range); if (path.isEmpty()) { @@ -1613,8 +1740,8 @@ void ClangdClient::gatherHelpItemForTooltip(const HoverRequest::Response &hoverR return; } d->setHelpItemForTooltip(id); - }); - sendContent(req, SendDocUpdates::Ignore); + }; + d->getAndHandleAst(doc, astHandler, Private::AstCallbackMode::SyncIfPossible); } void ClangdClient::Private::handleGotoDefinitionResult() @@ -1692,8 +1819,8 @@ void ClangdClient::Private::handleGotoImplementationResult( // procedure, to let the user know that things are happening. if (followSymbolData->allLinks.size() > 1 && !followSymbolData->virtualFuncAssistProcessor && followSymbolData->editorWidget) { - followSymbolData->editorWidget->invokeTextEditorWidgetAssist( - TextEditor::FollowSymbol, &followSymbolData->virtualFuncAssistProvider); + followSymbolData->editorWidget->invokeTextEditorWidgetAssist(FollowSymbol, + &followSymbolData->virtualFuncAssistProvider); } if (!followSymbolData->pendingGotoImplRequests.isEmpty()) @@ -1779,27 +1906,24 @@ void ClangdClient::Private::handleGotoImplementationResult( q->sendContent(defReq, SendDocUpdates::Ignore); } - const DocumentUri defLinkUri - = DocumentUri::fromFilePath(followSymbolData->defLink.targetFilePath); + const Utils::FilePath defLinkFilePath = followSymbolData->defLink.targetFilePath; + const TextDocument * const defLinkDoc = q->documentForFilePath(defLinkFilePath); + const auto defLinkDocVariant = defLinkDoc ? TextDocOrFile(defLinkDoc) + : TextDocOrFile(defLinkFilePath); const Position defLinkPos(followSymbolData->defLink.targetLine - 1, followSymbolData->defLink.targetColumn); - AstRequest astRequest(AstParams(TextDocumentIdentifier(defLinkUri), - Range(defLinkPos, defLinkPos))); - astRequest.setResponseCallback([this, id = followSymbolData->id]( - const AstRequest::Response &response) { + const auto astHandler = [this, range = Range(defLinkPos, defLinkPos), id = followSymbolData->id] + (const AstNode &ast, const MessageId &) { qCDebug(clangdLog) << "received ast response for def link"; if (!followSymbolData || followSymbolData->id != id) return; - const auto result = response.result(); - if (result) - followSymbolData->defLinkNode = *result; + followSymbolData->defLinkNode = getAstNode(ast, range); if (followSymbolData->pendingSymbolInfoRequests.isEmpty() && followSymbolData->pendingGotoDefRequests.isEmpty()) { handleDocumentInfoResults(); } - }); - qCDebug(clangdLog) << "sending ast request for def link"; - q->sendContent(astRequest, SendDocUpdates::Ignore); + }; + getAndHandleAst(defLinkDocVariant, astHandler, AstCallbackMode::SyncIfPossible); } void ClangdClient::Private::handleDocumentInfoResults() @@ -1889,19 +2013,19 @@ void ClangdClient::Private::setHelpItemForTooltip(const MessageId &token, const q->hoverHandler()->setHelpItem(token, helpItem); } -static void collectExtraResults(QFutureInterface &future, - TextEditor::HighlightingResults &results, const AstNode &ast, +static void collectExtraResults(QFutureInterface &future, + HighlightingResults &results, const AstNode &ast, QTextDocument *doc, const QString &docContent) { if (!ast.isValid()) return; - static const auto lessThan = [](const TextEditor::HighlightingResult &r1, - const TextEditor::HighlightingResult &r2) { + static const auto 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); }; - const auto insert = [&](const TextEditor::HighlightingResult &result) { + const auto insert = [&](const HighlightingResult &result) { if (!result.isValid()) // Some nodes don't have a range. return; const auto it = std::lower_bound(results.begin(), results.end(), result, lessThan); @@ -1913,14 +2037,14 @@ static void collectExtraResults(QFutureInterface } // This is for conversion operators, whose type part is only reported as a type by clangd. - if ((it->textStyles.mainStyle == TextEditor::C_TYPE - || it->textStyles.mainStyle == TextEditor::C_PRIMITIVE_TYPE) + if ((it->textStyles.mainStyle == C_TYPE + || it->textStyles.mainStyle == C_PRIMITIVE_TYPE) && !result.textStyles.mixinStyles.empty() - && result.textStyles.mixinStyles.at(0) == TextEditor::C_OPERATOR) { + && result.textStyles.mixinStyles.at(0) == C_OPERATOR) { it->textStyles.mixinStyles = result.textStyles.mixinStyles; } }; - const auto setFromRange = [doc](TextEditor::HighlightingResult &result, const Range &range) { + const auto setFromRange = [doc](HighlightingResult &result, const Range &range) { if (!range.isValid()) return; const Position startPos = range.start(); @@ -1950,28 +2074,27 @@ static void collectExtraResults(QFutureInterface nodes << children; if (node.kind().endsWith("Literal")) { - TextEditor::HighlightingResult result; + HighlightingResult result; result.useTextSyles = true; const bool isStringLike = node.kind().startsWith("String") || node.kind().startsWith("Character"); - result.textStyles.mainStyle = isStringLike - ? TextEditor::C_STRING : TextEditor::C_NUMBER; + result.textStyles.mainStyle = isStringLike ? C_STRING : C_NUMBER; setFromRange(result, node.range()); insert(result); continue; } if (node.role() == "type" && node.kind() == "Builtin") { - TextEditor::HighlightingResult result; + HighlightingResult result; result.useTextSyles = true; - result.textStyles.mainStyle = TextEditor::C_PRIMITIVE_TYPE; + result.textStyles.mainStyle = C_PRIMITIVE_TYPE; setFromRange(result, node.range()); insert(result); continue; } if (node.role() == "attribute" && (node.kind() == "Override" || node.kind() == "Final")) { - TextEditor::HighlightingResult result; + HighlightingResult result; result.useTextSyles = true; - result.textStyles.mainStyle = TextEditor::C_KEYWORD; + result.textStyles.mainStyle = C_KEYWORD; setFromRange(result, node.range()); insert(result); continue; @@ -2021,10 +2144,10 @@ static void collectExtraResults(QFutureInterface if (absQuestionMarkPos > absColonPos) continue; - TextEditor::HighlightingResult result; + HighlightingResult result; result.useTextSyles = true; - result.textStyles.mainStyle = TextEditor::C_PUNCTUATION; - result.textStyles.mixinStyles.push_back(TextEditor::C_OPERATOR); + result.textStyles.mainStyle = C_PUNCTUATION; + result.textStyles.mixinStyles.push_back(C_OPERATOR); Utils::Text::convertPosition(doc, absQuestionMarkPos, &result.line, &result.column); result.length = 1; result.kind = CppEditor::SemanticHighlighter::TernaryIf; @@ -2059,9 +2182,9 @@ static void collectExtraResults(QFutureInterface if (absOpeningAngleBracketPos > absClosingAngleBracketPos) return; - TextEditor::HighlightingResult result; + HighlightingResult result; result.useTextSyles = true; - result.textStyles.mainStyle = TextEditor::C_PUNCTUATION; + result.textStyles.mainStyle = C_PUNCTUATION; Utils::Text::convertPosition(doc, absOpeningAngleBracketPos, &result.line, &result.column); result.length = 1; @@ -2187,7 +2310,7 @@ static void collectExtraResults(QFutureInterface // 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. - // TODO: Can we fix this in clang? + // This issue is tracked at https://github.com/clangd/clangd/issues/871. if (searchStart2 == searchEnd2) --searchStart2; insertAngleBracketInfo(nodeStartPos, posForNodeStart(children.at(1)), @@ -2212,21 +2335,21 @@ static void collectExtraResults(QFutureInterface if (!isCallToNew && !isCallToDelete) detail.remove(0, operatorPrefix.length()); - TextEditor::HighlightingResult result; + 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 - ? TextEditor::C_PRIMITIVE_TYPE + ? C_PRIMITIVE_TYPE : isCallToNew || isCallToDelete || detail.at(0).isSpace() - ? TextEditor::C_KEYWORD : TextEditor::C_PUNCTUATION; - result.textStyles.mixinStyles.push_back(TextEditor::C_OPERATOR); + ? C_KEYWORD : C_PUNCTUATION; + result.textStyles.mixinStyles.push_back(C_OPERATOR); if (isOverloaded) - result.textStyles.mixinStyles.push_back(TextEditor::C_OVERLOADED_OPERATOR); + result.textStyles.mixinStyles.push_back(C_OVERLOADED_OPERATOR); if (isDeclaration) - result.textStyles.mixinStyles.push_back(TextEditor::C_DECLARATION); + result.textStyles.mixinStyles.push_back(C_DECLARATION); const QStringView nodeText = QStringView(docContent) .mid(nodeStartPos, nodeEndPos - nodeStartPos); @@ -2244,7 +2367,7 @@ static void collectExtraResults(QFutureInterface if (closingBracketOffset == -1 || closingBracketOffset < openingBracketOffset) continue; - result.textStyles.mainStyle = TextEditor::C_PUNCTUATION; + result.textStyles.mainStyle = C_PUNCTUATION; result.length = 1; Utils::Text::convertPosition(doc, nodeStartPos + openingBracketOffset, @@ -2307,7 +2430,7 @@ static void collectExtraResults(QFutureInterface if (!isArray && !isCall && !isArrayNew && !isArrayDelete) continue; - result.textStyles.mainStyle = TextEditor::C_PUNCTUATION; + result.textStyles.mainStyle = C_PUNCTUATION; result.length = 1; const int openingParenOffset = nodeText.indexOf( isCall ? '(' : '[', prefixOffset + operatorPrefix.length()); @@ -2327,20 +2450,27 @@ static void collectExtraResults(QFutureInterface // 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. -// TODO: Fix in clangd? -static void cleanupDisabledCode(TextEditor::HighlightingResults &results, QTextDocument *doc, - const QString &docContent) +// But note that we require this behavior, as otherwise we would not be able to grey out +// e.g. empty lines after an #fdef, due to the lack of symbols. +static QList cleanupDisabledCode(HighlightingResults &results, QTextDocument *doc, + const QString &docContent) { - bool inDisabled = false; + QList ifdefedOutRanges; + int rangeStartPos = -1; for (auto it = results.begin(); it != results.end();) { - const bool wasInDisabled = inDisabled; - if (it->textStyles.mainStyle != TextEditor::C_DISABLED_CODE) { - inDisabled = false; + const bool wasIfdefedOut = rangeStartPos != -1; + if (it->textStyles.mainStyle != C_DISABLED_CODE) { + if (wasIfdefedOut) { + const QTextBlock block = doc->findBlockByNumber(it->line - 1); + ifdefedOutRanges << BlockRange(rangeStartPos, block.position()); + rangeStartPos = -1; + } ++it; continue; } - inDisabled = true; + if (!wasIfdefedOut) + rangeStartPos = doc->findBlockByNumber(it->line - 1).position(); const int pos = Utils::Text::positionInText(doc, it->line, it->column); const QStringView content(QStringView(docContent).mid(pos, it->length).trimmed()); if (!content.startsWith(QLatin1String("#if")) @@ -2351,25 +2481,37 @@ static void cleanupDisabledCode(TextEditor::HighlightingResults &results, QTextD continue; } - if (!wasInDisabled) { + 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 (wasInDisabled && (it + 1 == results.end() - || (it + 1)->textStyles.mainStyle != TextEditor::C_DISABLED_CODE)) { + if (wasIfdefedOut && (it + 1 == results.end() + || (it + 1)->textStyles.mainStyle != C_DISABLED_CODE)) { // 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()); + + return ifdefedOutRanges; } -static void semanticHighlighter(QFutureInterface &future, +static void semanticHighlighter(QFutureInterface &future, const QList &tokens, - const QString &docContents, const AstNode &ast) + const QString &docContents, const AstNode &ast, + const QPointer &widget, + int docRevision, const QVersionNumber &clangdVersion) { if (future.isCanceled()) { future.reportFinished(); @@ -2403,40 +2545,37 @@ static void semanticHighlighter(QFutureInterface return false; }; - const auto toResult = [&ast, &isOutputParameter](const ExpandedSemanticToken &token) { - TextEditor::TextStyles styles; + const auto toResult = [&ast, &isOutputParameter, &clangdVersion] + (const ExpandedSemanticToken &token) { + TextStyles styles; if (token.type == "variable") { if (token.modifiers.contains("functionScope")) { - styles.mainStyle = TextEditor::C_LOCAL; + styles.mainStyle = C_LOCAL; } else if (token.modifiers.contains("classScope")) { - styles.mainStyle = TextEditor::C_FIELD; + styles.mainStyle = C_FIELD; } else if (token.modifiers.contains("fileScope") || token.modifiers.contains("globalScope")) { - styles.mainStyle = TextEditor::C_GLOBAL; + styles.mainStyle = C_GLOBAL; } } else if (token.type == "function" || token.type == "method") { - if (token.modifiers.contains("virtual")) - styles.mainStyle = TextEditor::C_VIRTUAL_METHOD; - else - styles.mainStyle = TextEditor::C_FUNCTION; + styles.mainStyle = token.modifiers.contains("virtual") ? C_VIRTUAL_METHOD : C_FUNCTION; if (ast.isValid()) { const Position pos(token.line - 1, token.column - 1); const QList path = getAstPath(ast, Range(pos, pos)); if (path.length() > 1) { const AstNode declNode = path.at(path.length() - 2); if (declNode.kind() == "Function" || declNode.kind() == "CXXMethod") { - - // TODO: Remove this once we can assume clangd >= 14. - if (declNode.arcanaContains("' virtual")) - styles.mainStyle = TextEditor::C_VIRTUAL_METHOD; - + if (clangdVersion < QVersionNumber(14) + && declNode.arcanaContains("' virtual")) { + styles.mainStyle = C_VIRTUAL_METHOD; + } if (declNode.hasChildWithRole("statement")) - styles.mixinStyles.push_back(TextEditor::C_FUNCTION_DEFINITION); + styles.mixinStyles.push_back(C_FUNCTION_DEFINITION); } } } } else if (token.type == "class") { - styles.mainStyle = TextEditor::C_TYPE; + styles.mainStyle = C_TYPE; // clang hardly ever differentiates between constructors and the associated class, // whereas we highlight constructors as functions. @@ -2446,54 +2585,58 @@ static void semanticHighlighter(QFutureInterface if (!path.isEmpty()) { if (path.last().kind() == "CXXConstructor") { if (!path.last().arcanaContains("implicit")) - styles.mainStyle = TextEditor::C_FUNCTION; + styles.mainStyle = C_FUNCTION; } else if (path.last().kind() == "Record" && path.length() > 1) { const AstNode node = path.at(path.length() - 2); if (node.kind() == "CXXDestructor" && !node.arcanaContains("implicit")) { - styles.mainStyle = TextEditor::C_FUNCTION; - // TODO: "declaration" modifier is missing for destructors; fix in clangd - // (the scope is also wrong) + styles.mainStyle = C_FUNCTION; + + // https://github.com/clangd/clangd/issues/872 if (node.role() == "declaration") - styles.mixinStyles.push_back(TextEditor::C_DECLARATION); + styles.mixinStyles.push_back(C_DECLARATION); } } } } } else if (token.type == "comment") { // "comment" means code disabled via the preprocessor - styles.mainStyle = TextEditor::C_DISABLED_CODE; + styles.mainStyle = C_DISABLED_CODE; } else if (token.type == "namespace") { - styles.mainStyle = TextEditor::C_TYPE; + styles.mainStyle = C_TYPE; } else if (token.type == "property") { - styles.mainStyle = TextEditor::C_FIELD; + styles.mainStyle = C_FIELD; } else if (token.type == "enum") { - styles.mainStyle = TextEditor::C_TYPE; - styles.mixinStyles.push_back(TextEditor::C_ENUMERATION); + styles.mainStyle = C_TYPE; + styles.mixinStyles.push_back(C_ENUMERATION); } else if (token.type == "enumMember") { - styles.mainStyle = TextEditor::C_ENUMERATION; + styles.mainStyle = C_ENUMERATION; } else if (token.type == "parameter") { - styles.mainStyle = TextEditor::C_PARAMETER; + styles.mainStyle = C_PARAMETER; } else if (token.type == "macro") { - styles.mainStyle = TextEditor::C_PREPROCESSOR; + styles.mainStyle = C_PREPROCESSOR; } else if (token.type == "type") { - styles.mainStyle = TextEditor::C_TYPE; + styles.mainStyle = C_TYPE; } else if (token.type == "typeParameter") { - styles.mainStyle = TextEditor::C_TYPE; + styles.mainStyle = C_TYPE; } if (token.modifiers.contains("declaration")) - styles.mixinStyles.push_back(TextEditor::C_DECLARATION); + styles.mixinStyles.push_back(C_DECLARATION); if (isOutputParameter(token)) - styles.mixinStyles.push_back(TextEditor::C_OUTPUT_ARGUMENT); + styles.mixinStyles.push_back(C_OUTPUT_ARGUMENT); qCDebug(clangdLogHighlight) << "adding highlighting result" << token.line << token.column << token.length << int(styles.mainStyle); - return TextEditor::HighlightingResult(token.line, token.column, token.length, styles); + return HighlightingResult(token.line, token.column, token.length, styles); }; - TextEditor::HighlightingResults results = Utils::transform(tokens, toResult); - cleanupDisabledCode(results, &doc, docContents); + HighlightingResults results = Utils::transform(tokens, toResult); + const QList ifdefedOutBlocks = cleanupDisabledCode(results, &doc, docContents); + QMetaObject::invokeMethod(widget, [widget, ifdefedOutBlocks, docRevision] { + if (widget && widget->textDocument()->document()->revision() == docRevision) + widget->setIfdefedOutBlocks(ifdefedOutBlocks); + }, Qt::QueuedConnection); collectExtraResults(future, results, ast, &doc, docContents); if (!future.isCanceled()) { qCDebug(clangdLog) << "reporting" << results.size() << "highlighting results"; - future.reportResults(QVector(results.cbegin(), + future.reportResults(QVector(results.cbegin(), results.cend())); } future.reportFinished(); @@ -2511,7 +2654,7 @@ static void semanticHighlighter(QFutureInterface // - We consider most other tokens to be simple enough to be handled by the built-in code model. // Sometimes we have no choice, as for #include directives, which appear neither // in the semantic tokens nor in the AST. -void ClangdClient::Private::handleSemanticTokens(TextEditor::TextDocument *doc, +void ClangdClient::Private::handleSemanticTokens(TextDocument *doc, const QList &tokens) { qCDebug(clangdLog()) << "handling LSP tokens" << doc->filePath() << tokens.size(); @@ -2519,24 +2662,24 @@ void ClangdClient::Private::handleSemanticTokens(TextEditor::TextDocument *doc, qCDebug(clangdLogHighlight()) << '\t' << t.line << t.column << t.length << t.type << t.modifiers; - // TODO: Cache ASTs - AstParams params(TextDocumentIdentifier(DocumentUri::fromFilePath(doc->filePath()))); - AstRequest astReq(params); - astReq.setResponseCallback([this, tokens, doc](const AstRequest::Response &response) { + const auto astHandler = [this, tokens, doc](const AstNode &ast, const MessageId &) { if (!q->documentOpen(doc)) return; - const Utils::optional ast = response.result(); - if (ast && clangdLogAst().isDebugEnabled()) - ast->print(); + if (clangdLogAst().isDebugEnabled()) + ast.print(); - const auto runner = [tokens, text = doc->document()->toPlainText(), - theAst = ast ? *ast : AstNode()] { - return Utils::runAsync(semanticHighlighter, tokens, text, theAst); + IEditor * const editor = Utils::findOrDefault(EditorManager::visibleEditors(), + [doc](const IEditor *editor) { return editor->document() == doc; }); + const auto editorWidget = TextEditorWidget::fromEditor(editor); + const auto runner = [tokens, text = doc->document()->toPlainText(), ast, + w = QPointer(editorWidget), rev = doc->document()->revision(), + clangdVersion = q->versionNumber()] { + return Utils::runAsync(semanticHighlighter, tokens, text, ast, w, rev, clangdVersion); }; if (isTesting) { - const auto watcher = new QFutureWatcher(q); - connect(watcher, &QFutureWatcher::finished, + const auto watcher = new QFutureWatcher(q); + connect(watcher, &QFutureWatcher::finished, q, [this, watcher, fp = doc->filePath()] { emit q->highlightingResultsReady(watcher->future().results(), fp); watcher->deleteLater(); @@ -2553,8 +2696,8 @@ void ClangdClient::Private::handleSemanticTokens(TextEditor::TextDocument *doc, } it->second.setHighlightingRunner(runner); it->second.run(); - }); - q->sendContent(astReq, SendDocUpdates::Ignore); + }; + getAndHandleAst(doc, astHandler, AstCallbackMode::SyncIfPossible); } void ClangdClient::VirtualFunctionAssistProcessor::cancel() @@ -2593,11 +2736,11 @@ void ClangdClient::VirtualFunctionAssistProcessor::resetData() m_data = nullptr; } -TextEditor::IAssistProposal *ClangdClient::VirtualFunctionAssistProcessor::createProposal(bool final) const +IAssistProposal *ClangdClient::VirtualFunctionAssistProcessor::createProposal(bool final) const { QTC_ASSERT(m_data && m_data->followSymbolData, return nullptr); - QList items; + QList items; bool needsBaseDeclEntry = !m_data->followSymbolData->defLinkNode.range() .contains(Position(m_data->followSymbolData->cursor)); for (const SymbolData &symbol : qAsConst(m_data->followSymbolData->symbolsToDisplay)) { @@ -2647,8 +2790,8 @@ ClangdClient::VirtualFunctionAssistProcessor::createEntry(const QString &name, return item; } -TextEditor::IAssistProcessor *ClangdClient::VirtualFunctionAssistProvider::createProcessor( - const TextEditor::AssistInterface *) const +IAssistProcessor *ClangdClient::VirtualFunctionAssistProvider::createProcessor( + const AssistInterface *) const { return m_data->followSymbolData->virtualFuncAssistProcessor = new VirtualFunctionAssistProcessor(m_data); @@ -2664,60 +2807,60 @@ QString ClangdDiagnostic::category() const return typedValue("category"); } +class ClangdClient::ClangdFunctionHintProcessor : public FunctionHintProcessor +{ +public: + ClangdFunctionHintProcessor(ClangdClient *client) + : FunctionHintProcessor(client) + , m_client(client) + {} + +private: + IAssistProposal *perform(const AssistInterface *interface) override + { + if (m_client->d->isTesting) { + setAsyncCompletionAvailableHandler([this](IAssistProposal *proposal) { + emit m_client->proposalReady(proposal); + }); + } + return FunctionHintProcessor::perform(interface); + } + + ClangdClient * const m_client; +}; + ClangdClient::ClangdCompletionAssistProvider::ClangdCompletionAssistProvider(ClangdClient *client) : LanguageClientCompletionAssistProvider(client) -{ - setItemsTransformer([](const Utils::FilePath &filePath, const QString &content, - int pos, const QList &items) { - qCDebug(clangdLog) << "received" << items.count() << "completions"; + , m_client(client) +{} - // If there are signals among the candidates, we employ the built-in code model to find out - // whether the cursor was on the second argument of a (dis)connect() call. - // If so, we offer only signals, as nothing else makes sense in that context. - static const auto criterion = [](const CompletionItem &ci) { - const Utils::optional doc = ci.documentation(); - if (!doc) - return false; - QString docText; - if (Utils::holds_alternative(*doc)) - docText = Utils::get(*doc); - else if (Utils::holds_alternative(*doc)) - docText = Utils::get(*doc).content(); - return docText.contains("Annotation: qt_signal"); - }; - if (pos != -1 && Utils::anyOf(items, criterion) && CppEditor::CppModelManager::instance() - ->positionRequiresSignal(filePath.toString(), content.toUtf8(), pos)) { - return Utils::filtered(items, criterion); - } - return items; - }); - - setApplyHelper([this](const CompletionItem &item, - TextEditor::TextDocumentManipulatorInterface &manipulator, QChar typedChar) { - applyCompletionItem(item, manipulator, typedChar); - }); -} - -TextEditor::IAssistProcessor *ClangdClient::ClangdCompletionAssistProvider::createProcessor( - const TextEditor::AssistInterface *assistInterface) const +IAssistProcessor *ClangdClient::ClangdCompletionAssistProvider::createProcessor( + const AssistInterface *assistInterface) const { ClangCompletionContextAnalyzer contextAnalyzer(assistInterface->textDocument(), assistInterface->position(), false, {}); contextAnalyzer.analyze(); - client()->setSnippetsGroup( - contextAnalyzer.addSnippets() ? CppEditor::Constants::CPP_SNIPPETS_GROUP_ID : QString()); switch (contextAnalyzer.completionAction()) { case ClangCompletionContextAnalyzer::PassThroughToLibClangAfterLeftParen: qCDebug(clangdLog) << "completion changed to function hint"; - return new FunctionHintProcessor(client(), proposalHandler()); + return new ClangdFunctionHintProcessor(m_client); case ClangCompletionContextAnalyzer::CompleteDoxygenKeyword: - return new DoxygenAssistProcessor(contextAnalyzer.positionForProposal(), - contextAnalyzer.completionOperator(), - proposalHandler()); + return new CustomAssistProcessor(m_client, + contextAnalyzer.positionForProposal(), + contextAnalyzer.completionOperator(), + CustomAssistMode::Doxygen); + case ClangCompletionContextAnalyzer::CompletePreprocessorDirective: + return new CustomAssistProcessor(m_client, + contextAnalyzer.positionForProposal(), + contextAnalyzer.completionOperator(), + CustomAssistMode::Preprocessor); default: break; } - return LanguageClientCompletionAssistProvider::createProcessor(assistInterface); + const QString snippetsGroup = contextAnalyzer.addSnippets() + ? CppEditor::Constants::CPP_SNIPPETS_GROUP_ID + : QString(); + return new ClangdCompletionAssistProcessor(m_client, snippetsGroup); } bool ClangdClient::ClangdCompletionAssistProvider::isActivationCharSequence(const QString &sequence) const @@ -2747,10 +2890,11 @@ bool ClangdClient::ClangdCompletionAssistProvider::isContinuationChar(const QCha return CppEditor::isValidIdentifierChar(c); } -void ClangdClient::ClangdCompletionAssistProvider::applyCompletionItem( - const CompletionItem &item, TextEditor::TextDocumentManipulatorInterface &manipulator, - QChar typedChar) +void ClangdCompletionItem::apply(TextDocumentManipulatorInterface &manipulator, + int /*basePosition*/) const { + const LanguageServerProtocol::CompletionItem item = this->item(); + QChar typedChar = triggeredCommitCharacter(); const auto edit = item.textEdit(); if (!edit) return; @@ -2772,8 +2916,7 @@ void ClangdClient::ClangdCompletionAssistProvider::applyCompletionItem( } const QString detail = item.detail().value_or(QString()); - const TextEditor::CompletionSettings &completionSettings - = TextEditor::TextEditorSettings::completionSettings(); + const CompletionSettings &completionSettings = TextEditorSettings::completionSettings(); QString textToBeInserted = rawInsertText.left(firstParenOffset); QString extraCharacters; int cursorOffset = 0; @@ -2867,6 +3010,86 @@ void ClangdClient::ClangdCompletionAssistProvider::applyCompletionItem( } } +MessageId ClangdClient::Private::getAndHandleAst(const TextDocOrFile &doc, + const AstHandler &astHandler, + AstCallbackMode callbackMode) +{ + const auto textDocPtr = Utils::get_if(&doc); + const TextDocument * const textDoc = textDocPtr ? *textDocPtr : nullptr; + const Utils::FilePath filePath = textDoc ? textDoc->filePath() + : Utils::get(doc); + + // If the document's AST is in the cache and is up to date, call the handler. + if (const auto ast = textDoc ? astCache.get(textDoc) : externalAstCache.get(filePath)) { + qCDebug(clangdLog) << "using AST from cache"; + switch (callbackMode) { + case AstCallbackMode::SyncIfPossible: + astHandler(*ast, {}); + break; + case AstCallbackMode::AlwaysAsync: + QMetaObject::invokeMethod(q, [ast, astHandler] { astHandler(*ast, {}); }, + Qt::QueuedConnection); + break; + } + return {}; + } + + // Otherwise retrieve the AST from clangd. + + class AstParams : public JsonObject + { + public: + AstParams() {} + AstParams(const TextDocumentIdentifier &document, const Range &range = {}) + { + setTextDocument(document); + if (range.isValid()) + setRange(range); + } + + using JsonObject::JsonObject; + + // The open file to inspect. + TextDocumentIdentifier textDocument() const + { return typedValue(textDocumentKey); } + void setTextDocument(const TextDocumentIdentifier &id) { insert(textDocumentKey, id); } + + // The region of the source code whose AST is fetched. The highest-level node that entirely + // contains the range is returned. + Utils::optional range() const { return optionalValue(rangeKey); } + void setRange(const Range &range) { insert(rangeKey, range); } + + bool isValid() const override { return contains(textDocumentKey); } + }; + + class AstRequest : public Request + { + public: + using Request::Request; + explicit AstRequest(const AstParams ¶ms) : Request("textDocument/ast", params) {} + }; + + AstRequest request(AstParams(TextDocumentIdentifier(DocumentUri::fromFilePath(filePath)))); + request.setResponseCallback([this, filePath, guardedTextDoc = QPointer(textDoc), astHandler, + docRev = textDoc ? getRevision(textDoc) : -1, + fileRev = getRevision(filePath), reqId = request.id()] + (AstRequest::Response response) { + qCDebug(clangdLog) << "retrieved AST from clangd"; + const auto result = response.result(); + const AstNode ast = result ? *result : AstNode(); + if (guardedTextDoc) { + if (docRev == getRevision(guardedTextDoc)) + astCache.insert(guardedTextDoc, ast); + } else if (fileRev == getRevision(filePath) && !q->documentForFilePath(filePath)) { + externalAstCache.insert(filePath, ast); + } + astHandler(ast, reqId); + }); + qCDebug(clangdLog) << "requesting AST for" << filePath; + q->sendContent(request, SendDocUpdates::Ignore); + return request.id(); +} + } // namespace Internal } // namespace ClangCodeModel diff --git a/src/plugins/clangcodemodel/clangdclient.h b/src/plugins/clangcodemodel/clangdclient.h index 440c56e1d54..bec77b9261e 100644 --- a/src/plugins/clangcodemodel/clangdclient.h +++ b/src/plugins/clangcodemodel/clangdclient.h @@ -77,6 +77,7 @@ public: const LanguageServerProtocol::DocumentUri &uri); void enableTesting(); + bool testingEnabled() const; signals: void indexingFinished(); @@ -90,12 +91,15 @@ signals: private: void handleDiagnostics(const LanguageServerProtocol::PublishDiagnosticsParams ¶ms) override; + void handleDocumentOpened(TextEditor::TextDocument *doc) override; void handleDocumentClosed(TextEditor::TextDocument *doc) override; class Private; class FollowSymbolData; class VirtualFunctionAssistProcessor; class VirtualFunctionAssistProvider; + class ClangdFunctionHintProcessor; + class ClangdCompletionAssistProcessor; class ClangdCompletionAssistProvider; Private * const d; }; diff --git a/src/plugins/clangcodemodel/test/clangdtests.cpp b/src/plugins/clangcodemodel/test/clangdtests.cpp index f1da00e0770..4622db96a69 100644 --- a/src/plugins/clangcodemodel/test/clangdtests.cpp +++ b/src/plugins/clangcodemodel/test/clangdtests.cpp @@ -77,6 +77,7 @@ using Range = std::tuple; } // namespace ClangCodeModel Q_DECLARE_METATYPE(ClangCodeModel::Internal::Tests::Range) +Q_DECLARE_METATYPE(IAssistProposal *) namespace ClangCodeModel { namespace Internal { @@ -392,7 +393,7 @@ void ClangdTestFollowSymbol::test() timer.stop(); QCOMPARE(actualLink.targetFilePath, filePath(targetFile)); - QEXPECT_FAIL("union member ref", "FIXME: clangd points to union", Abort); + QEXPECT_FAIL("union member ref", "https://github.com/clangd/clangd/issues/877", Abort); QCOMPARE(actualLink.targetLine, targetLine); QCOMPARE(actualLink.targetColumn + 1, targetColumn); } @@ -1274,7 +1275,7 @@ void ClangdTestHighlighting::test() const TextEditor::HighlightingResults results = findResults(); QEXPECT_FAIL("typedef as underlying type in enum declaration", - "FIXME: clangd does not report this symbol", + "https://github.com/clangd/clangd/issues/878", Abort); QEXPECT_FAIL("Q_PROPERTY (property name)", "FIXME: How to do this?", Abort); QEXPECT_FAIL("Q_PROPERTY (getter)", "FIXME: How to do this?", Abort); @@ -1461,20 +1462,19 @@ void ClangdTestCompletion::testCompletePreprocessorKeywords() ProposalModelPtr proposal; getProposal("preprocessorKeywordsCompletion.cpp", proposal); QVERIFY(proposal); - QVERIFY(hasItem(proposal, " ifdef macro")); + QVERIFY(hasItem(proposal, "ifdef")); QVERIFY(!hasSnippet(proposal, "class ")); proposal.clear(); getProposal("preprocessorKeywordsCompletion2.cpp", proposal); QVERIFY(proposal); - QVERIFY(hasItem(proposal, " endif")); + QVERIFY(hasItem(proposal, "endif")); QVERIFY(!hasSnippet(proposal, "class ")); proposal.clear(); getProposal("preprocessorKeywordsCompletion3.cpp", proposal); QVERIFY(proposal); - QEXPECT_FAIL("", "TODO: Fix in clangd", Continue); - QVERIFY(hasItem(proposal, " endif")); + QVERIFY(hasItem(proposal, "endif")); QVERIFY(!hasSnippet(proposal, "class ")); } @@ -1885,7 +1885,7 @@ void ClangdTestCompletion::getProposal(const QString &fileName, connect(client(), &ClangdClient::proposalReady, &loop, [&proposal, &loop](IAssistProposal *p) { proposal = p; loop.quit(); - }); + }, Qt::QueuedConnection); editor->editorWidget()->invokeAssist(Completion, nullptr); timer.start(5000); loop.exec(); diff --git a/src/plugins/coreplugin/iwizardfactory.cpp b/src/plugins/coreplugin/iwizardfactory.cpp index b70cd1a2d03..988c4bdd958 100644 --- a/src/plugins/coreplugin/iwizardfactory.cpp +++ b/src/plugins/coreplugin/iwizardfactory.cpp @@ -34,6 +34,7 @@ #include #include +#include #include #include @@ -369,6 +370,11 @@ void IWizardFactory::requestNewItemDialog(const QString &title, s_reopenData.setData(title, factories, defaultLocation, extraVariables); } +QIcon IWizardFactory::themedIcon(const Utils::FilePath &iconMaskPath) +{ + return Utils::Icon({{iconMaskPath, Theme::PanelTextColorDark}}, Icon::Tint).icon(); +} + void IWizardFactory::destroyFeatureProvider() { qDeleteAll(s_providerList); @@ -427,7 +433,8 @@ void IWizardFactory::initialize() static QIcon iconWithText(const QIcon &icon, const QString &text) { if (icon.isNull()) { - static const QIcon fallBack(":/utils/images/wizardicon-file.png"); + static const QIcon fallBack = + IWizardFactory::themedIcon(":/utils/images/wizardicon-file.png"); return iconWithText(fallBack, text); } @@ -445,6 +452,7 @@ static QIcon iconWithText(const QIcon &icon, const QString &text) font.setPixelSize(fontSize); font.setStretch(85); QPainter p(&pixmap); + p.setPen(Utils::creatorTheme()->color(Theme::PanelTextColorDark)); p.setFont(font); QTextOption textOption(Qt::AlignHCenter | Qt::AlignBottom); textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); diff --git a/src/plugins/coreplugin/iwizardfactory.h b/src/plugins/coreplugin/iwizardfactory.h index b6ede111847..1e27ec6c900 100644 --- a/src/plugins/coreplugin/iwizardfactory.h +++ b/src/plugins/coreplugin/iwizardfactory.h @@ -115,6 +115,8 @@ public: const Utils::FilePath &defaultLocation, const QVariantMap &extraVariables); + static QIcon themedIcon(const Utils::FilePath &iconMaskPath); + protected: static QSet pluginFeatures(); static QSet availableFeatures(Utils::Id platformId); diff --git a/src/plugins/coreplugin/manhattanstyle.cpp b/src/plugins/coreplugin/manhattanstyle.cpp index f834cd2765b..9a8934b989f 100644 --- a/src/plugins/coreplugin/manhattanstyle.cpp +++ b/src/plugins/coreplugin/manhattanstyle.cpp @@ -37,6 +37,7 @@ #include #include +#include #include #include #include @@ -288,7 +289,8 @@ void ManhattanStyle::polish(QWidget *widget) if (qobject_cast(widget) || qobject_cast(widget)) { widget->setAttribute(Qt::WA_Hover); widget->setMaximumHeight(height - 2); - } else if (qobject_cast(widget)) { + } else if (qobject_cast(widget) || qobject_cast(widget) + || qobject_cast(widget)) { widget->setPalette(panelPalette(widget->palette(), lightColored(widget))); } else if (widget->property("panelwidget_singlerow").toBool()) { widget->setFixedHeight(height); @@ -303,10 +305,6 @@ void ManhattanStyle::polish(QWidget *widget) widget->setPalette(palette); widget->setMaximumHeight(height - 2); widget->setAttribute(Qt::WA_Hover); - } else if (qobject_cast(widget)) { - const bool isLightColored = lightColored(widget); - QPalette palette = panelPalette(widget->palette(), isLightColored); - widget->setPalette(palette); } } } diff --git a/src/plugins/cppeditor/cppcompletionassist.cpp b/src/plugins/cppeditor/cppcompletionassist.cpp index 595a6c85d23..cd2c70b78c3 100644 --- a/src/plugins/cppeditor/cppcompletionassist.cpp +++ b/src/plugins/cppeditor/cppcompletionassist.cpp @@ -1302,7 +1302,7 @@ void InternalCppCompletionAssistProcessor::completeInclude(const QString &realPa void InternalCppCompletionAssistProcessor::completePreprocessor() { - foreach (const QString &preprocessorCompletion, m_preprocessorCompletions) + foreach (const QString &preprocessorCompletion, preprocessorCompletions()) addCompletionItem(preprocessorCompletion); if (objcKeywordsWanted()) diff --git a/src/plugins/cppeditor/cppcompletionassistprocessor.cpp b/src/plugins/cppeditor/cppcompletionassistprocessor.cpp index d6ff7acb117..a222ce49a08 100644 --- a/src/plugins/cppeditor/cppcompletionassistprocessor.cpp +++ b/src/plugins/cppeditor/cppcompletionassistprocessor.cpp @@ -41,20 +41,24 @@ using namespace CPlusPlus; namespace CppEditor { CppCompletionAssistProcessor::CppCompletionAssistProcessor(int snippetItemOrder) - : m_preprocessorCompletions( - QStringList({"define", "error", "include", "line", "pragma", "pragma once", - "pragma omp atomic", "pragma omp parallel", "pragma omp for", - "pragma omp ordered", "pragma omp parallel for", "pragma omp section", - "pragma omp sections", "pragma omp parallel sections", "pragma omp single", - "pragma omp master", "pragma omp critical", "pragma omp barrier", - "pragma omp flush", "pragma omp threadprivate", "undef", "if", "ifdef", - "ifndef", "elif", "else", "endif"})) - , m_snippetCollector(QLatin1String(CppEditor::Constants::CPP_SNIPPETS_GROUP_ID), + : m_snippetCollector(QLatin1String(CppEditor::Constants::CPP_SNIPPETS_GROUP_ID), QIcon(QLatin1String(":/texteditor/images/snippet.png")), snippetItemOrder) { } +const QStringList CppCompletionAssistProcessor::preprocessorCompletions() +{ + static QStringList list{"define", "error", "include", "line", "pragma", "pragma once", + "pragma omp atomic", "pragma omp parallel", "pragma omp for", + "pragma omp ordered", "pragma omp parallel for", "pragma omp section", + "pragma omp sections", "pragma omp parallel sections", "pragma omp single", + "pragma omp master", "pragma omp critical", "pragma omp barrier", + "pragma omp flush", "pragma omp threadprivate", "undef", "if", "ifdef", + "ifndef", "elif", "else", "endif"}; + return list; +} + void CppCompletionAssistProcessor::addSnippets() { m_completions.append(m_snippetCollector.collect()); diff --git a/src/plugins/cppeditor/cppcompletionassistprocessor.h b/src/plugins/cppeditor/cppcompletionassistprocessor.h index 5a94c6cdd90..87d8527e111 100644 --- a/src/plugins/cppeditor/cppcompletionassistprocessor.h +++ b/src/plugins/cppeditor/cppcompletionassistprocessor.h @@ -45,6 +45,8 @@ class CPPEDITOR_EXPORT CppCompletionAssistProcessor : public TextEditor::IAssist public: explicit CppCompletionAssistProcessor(int snippetItemOrder = 0); + static const QStringList preprocessorCompletions(); + protected: void addSnippets(); @@ -60,7 +62,6 @@ protected: int m_positionForProposal = -1; QList m_completions; - QStringList m_preprocessorCompletions; TextEditor::IAssistProposal *m_hintProposal = nullptr; private: diff --git a/src/plugins/debugger/debuggersourcepathmappingwidget.cpp b/src/plugins/debugger/debuggersourcepathmappingwidget.cpp index 1f18596ee46..dacd3f57806 100644 --- a/src/plugins/debugger/debuggersourcepathmappingwidget.cpp +++ b/src/plugins/debugger/debuggersourcepathmappingwidget.cpp @@ -55,7 +55,7 @@ class SourcePathMappingModel; enum { SourceColumn, TargetColumn, ColumnCount }; -using Mapping = QPair; +using Mapping = QPair; class DebuggerSourcePathMappingWidget : public QGroupBox { @@ -79,7 +79,7 @@ private: void updateEnabled(); QString editSourceField() const; QString editTargetField() const; - void setEditFieldMapping(const QPair &m); + void setEditFieldMapping(const Mapping &m); int currentRow() const; void setCurrentRow(int r); @@ -162,9 +162,9 @@ SourcePathMap SourcePathMappingModel::sourcePathMap() const SourcePathMap rc; const int rows = rowCount(); for (int r = 0; r < rows; ++r) { - const QPair m = mappingAt(r); // Skip placeholders. + const Mapping m = mappingAt(r); // Skip placeholders. if (!m.first.isEmpty() && !m.second.isEmpty()) - rc.insert(m.first, m.second); + rc.insert(m.first.toString(), m.second.toString()); } return rc; } @@ -176,17 +176,17 @@ bool SourcePathMappingModel::isNewPlaceHolder(const Mapping &m) const const QChar greaterThan('>'); return m.first.isEmpty() || m.first.startsWith(lessThan) || m.first.endsWith(greaterThan) - || m.first == m_newSourcePlaceHolder + || m.first.toString() == m_newSourcePlaceHolder || m.second.isEmpty() || m.second.startsWith(lessThan) || m.second.endsWith(greaterThan) - || m.second == m_newTargetPlaceHolder; + || m.second.toString() == m_newTargetPlaceHolder; } // Return raw, unfixed mapping Mapping SourcePathMappingModel::rawMappingAt(int row) const { - return Mapping(QDir::fromNativeSeparators(item(row, SourceColumn)->text()), - QDir::fromNativeSeparators(item(row, TargetColumn)->text())); + return Mapping(FilePath::fromUserInput(item(row, SourceColumn)->text()), + FilePath::fromUserInput(item(row, TargetColumn)->text())); } // Return mapping, empty if it is the place holder. @@ -337,15 +337,14 @@ QString DebuggerSourcePathMappingWidget::editTargetField() const void DebuggerSourcePathMappingWidget::setEditFieldMapping(const Mapping &m) { - m_sourceLineEdit->setText(m.first); - m_targetChooser->setPath(m.second); + m_sourceLineEdit->setText(m.first.toUserOutput()); + m_targetChooser->setFilePath(m.second); } void DebuggerSourcePathMappingWidget::slotCurrentRowChanged (const QModelIndex ¤t, const QModelIndex &) { - setEditFieldMapping(current.isValid() - ? m_model->mappingAt(current.row()) : Mapping()); + setEditFieldMapping(current.isValid() ? m_model->mappingAt(current.row()) : Mapping()); updateEnabled(); } diff --git a/src/plugins/git/remotedialog.cpp b/src/plugins/git/remotedialog.cpp index 6463433f377..e99572ad11f 100644 --- a/src/plugins/git/remotedialog.cpp +++ b/src/plugins/git/remotedialog.cpp @@ -145,6 +145,8 @@ RemoteDialog::RemoteDialog(QWidget *parent) : connect(m_ui->remoteView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &RemoteDialog::updateButtonState); + connect(m_remoteModel, &RemoteModel::refreshed, + this, &RemoteDialog::updateButtonState); updateButtonState(); } diff --git a/src/plugins/git/remotemodel.cpp b/src/plugins/git/remotemodel.cpp index 71d49397ac3..5f8bcd3488b 100644 --- a/src/plugins/git/remotemodel.cpp +++ b/src/plugins/git/remotemodel.cpp @@ -199,6 +199,7 @@ bool RemoteModel::refresh(const FilePath &workingDirectory, QString *errorMessag m_remotes.push_back(newRemote); } endResetModel(); + emit refreshed(); return true; } diff --git a/src/plugins/git/remotemodel.h b/src/plugins/git/remotemodel.h index 1ef6b7ffc64..85ceabdafe8 100644 --- a/src/plugins/git/remotemodel.h +++ b/src/plugins/git/remotemodel.h @@ -65,6 +65,9 @@ public: Utils::FilePath workingDirectory() const; int findRemoteByName(const QString &name) const; +signals: + void refreshed(); + protected: class Remote { public: diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp index e65c7d81c5e..220ddb42c55 100644 --- a/src/plugins/languageclient/client.cpp +++ b/src/plugins/languageclient/client.cpp @@ -394,6 +394,7 @@ void Client::openDocument(TextEditor::TextDocument *document) m_documentVersions[filePath] = 0; item.setVersion(m_documentVersions[filePath]); sendContent(DidOpenTextDocumentNotification(DidOpenTextDocumentParams(item))); + handleDocumentOpened(document); const Client *currentClient = LanguageClientManager::clientForDocument(document); if (currentClient == this) { @@ -1047,22 +1048,6 @@ SymbolStringifier Client::symbolStringifier() const return m_symbolStringifier; } -void Client::setCompletionProposalHandler(const ProposalHandler &handler) -{ - if (const auto provider = qobject_cast( - m_clientProviders.completionAssistProvider)) { - provider->setProposalHandler(handler); - } -} - -void Client::setFunctionHintProposalHandler(const ProposalHandler &handler) -{ - if (const auto provider = qobject_cast( - m_clientProviders.functionHintProvider)) { - provider->setProposalHandler(handler); - } -} - void Client::setSnippetsGroup(const QString &group) { if (const auto provider = qobject_cast( diff --git a/src/plugins/languageclient/client.h b/src/plugins/languageclient/client.h index f6c16e72d23..bf06359ef41 100644 --- a/src/plugins/languageclient/client.h +++ b/src/plugins/languageclient/client.h @@ -185,8 +185,6 @@ public: void setSemanticTokensHandler(const SemanticTokensHandler &handler); void setSymbolStringifier(const LanguageServerProtocol::SymbolStringifier &stringifier); LanguageServerProtocol::SymbolStringifier symbolStringifier() const; - void setCompletionProposalHandler(const ProposalHandler &handler); - void setFunctionHintProposalHandler(const ProposalHandler &handler); void setSnippetsGroup(const QString &group); void setCompletionAssistProvider(LanguageClientCompletionAssistProvider *provider); @@ -240,6 +238,7 @@ private: void rehighlight(); virtual void handleDocumentClosed(TextEditor::TextDocument *) {} + virtual void handleDocumentOpened(TextEditor::TextDocument *) {} using ContentHandler = std::function #include -#include #include #include #include @@ -55,41 +54,8 @@ using namespace TextEditor; namespace LanguageClient { -class LanguageClientCompletionItem : public AssistProposalItemInterface -{ -public: - LanguageClientCompletionItem(CompletionItem item, const CompletionApplyHelper &applyHelper); - - // AssistProposalItemInterface interface - QString text() const override; - QString filterText() const override; - bool implicitlyApplies() const override; - bool prematurelyApplies(const QChar &typedCharacter) const override; - void apply(TextDocumentManipulatorInterface &manipulator, int basePosition) const override; - QIcon icon() const override; - QString detail() const override; - bool isSnippet() const override; - bool isValid() const override; - quint64 hash() const override; - - const QString &sortText() const; - bool hasSortText() const; - - bool operator <(const LanguageClientCompletionItem &other) const; - - bool isPerfectMatch(int pos, QTextDocument *doc) const; - -private: - CompletionItem m_item; - const CompletionApplyHelper m_applyHelper; - mutable QChar m_triggeredCommitCharacter; - mutable QString m_sortText; - mutable QString m_filterText; -}; - -LanguageClientCompletionItem::LanguageClientCompletionItem(CompletionItem item, - const CompletionApplyHelper &applyHelper) - : m_item(std::move(item)), m_applyHelper(applyHelper) +LanguageClientCompletionItem::LanguageClientCompletionItem(CompletionItem item) + : m_item(std::move(item)) { } QString LanguageClientCompletionItem::text() const @@ -110,11 +76,6 @@ bool LanguageClientCompletionItem::prematurelyApplies(const QChar &typedCharacte void LanguageClientCompletionItem::apply(TextDocumentManipulatorInterface &manipulator, int /*basePosition*/) const { - if (m_applyHelper) { - m_applyHelper(m_item, manipulator, m_triggeredCommitCharacter); - return; - } - if (auto edit = m_item.textEdit()) { applyTextEdit(manipulator, *edit, isSnippet()); } else { @@ -209,6 +170,16 @@ quint64 LanguageClientCompletionItem::hash() const return qHash(m_item.label()); // TODO: naaaa } +CompletionItem LanguageClientCompletionItem::item() const +{ + return m_item; +} + +QChar LanguageClientCompletionItem::triggeredCommitCharacter() const +{ + return m_triggeredCommitCharacter; +} + const QString &LanguageClientCompletionItem::sortText() const { if (m_sortText.isEmpty()) @@ -320,19 +291,30 @@ public: int m_pos = -1; }; - -LanguageClientCompletionAssistProcessor::LanguageClientCompletionAssistProcessor(Client *client, - const CompletionItemsTransformer &itemsTransformer, const CompletionApplyHelper &applyHelper, - const ProposalHandler &proposalHandler, const QString &snippetsGroup) - : m_client(client), m_itemsTransformer(itemsTransformer), m_applyHelper(applyHelper), - m_proposalHandler(proposalHandler), m_snippetsGroup(snippetsGroup) -{ } +LanguageClientCompletionAssistProcessor::LanguageClientCompletionAssistProcessor( + Client *client, + const QString &snippetsGroup) + : m_client(client) + , m_snippetsGroup(snippetsGroup) +{} LanguageClientCompletionAssistProcessor::~LanguageClientCompletionAssistProcessor() { QTC_ASSERT(!running(), cancel()); } +QTextDocument *LanguageClientCompletionAssistProcessor::document() const +{ + return m_document; +} + +QList LanguageClientCompletionAssistProcessor::generateCompletionItems( + const QList &items) const +{ + return Utils::transform>( + items, [](const CompletionItem &item) { return new LanguageClientCompletionItem(item); }); +} + static QString assistReasonString(AssistReason reason) { switch (reason) { @@ -442,17 +424,12 @@ void LanguageClientCompletionAssistProcessor::handleCompletionResponse( } else if (Utils::holds_alternative>(*result)) { items = Utils::get>(*result); } - if (m_itemsTransformer && m_document) - items = m_itemsTransformer(m_filePath, m_document->toPlainText(), m_basePos, items); - auto model = new LanguageClientCompletionModel(); - auto proposalItems = Utils::transform>(items, - [this](const CompletionItem &item) { - return new LanguageClientCompletionItem(item, m_applyHelper); - }); + auto proposalItems = generateCompletionItems(items); if (!m_snippetsGroup.isEmpty()) { proposalItems << TextEditor::SnippetAssistCollector( m_snippetsGroup, QIcon(":/texteditor/images/snippet.png")).collect(); } + auto model = new LanguageClientCompletionModel(); model->loadContent(proposalItems); LanguageClientCompletionProposal *proposal = new LanguageClientCompletionProposal(m_basePos, model); @@ -460,10 +437,7 @@ void LanguageClientCompletionAssistProcessor::handleCompletionResponse( proposal->m_pos = m_pos; proposal->setFragile(true); proposal->setSupportsPrefix(false); - if (m_proposalHandler) - m_proposalHandler(proposal); - else - setAsyncProposalAvailable(proposal); + setAsyncProposalAvailable(proposal); m_client->removeAssistProcessor(this); qCDebug(LOGLSPCOMPLETION) << QTime::currentTime() << " : " << items.count() << " completions handled"; @@ -477,8 +451,8 @@ LanguageClientCompletionAssistProvider::LanguageClientCompletionAssistProvider(C IAssistProcessor *LanguageClientCompletionAssistProvider::createProcessor( const AssistInterface *) const { - return new LanguageClientCompletionAssistProcessor(m_client, m_itemsTransformer, m_applyHelper, - m_proposalHandler, m_snippetsGroup); + return new LanguageClientCompletionAssistProcessor(m_client, + m_snippetsGroup); } IAssistProvider::RunType LanguageClientCompletionAssistProvider::runType() const @@ -509,16 +483,4 @@ void LanguageClientCompletionAssistProvider::setTriggerCharacters( } } -void LanguageClientCompletionAssistProvider::setItemsTransformer( - const CompletionItemsTransformer &transformer) -{ - m_itemsTransformer = transformer; -} - -void LanguageClientCompletionAssistProvider::setApplyHelper( - const CompletionApplyHelper &applyHelper) -{ - m_applyHelper = applyHelper; -} - } // namespace LanguageClient diff --git a/src/plugins/languageclient/languageclientcompletionassist.h b/src/plugins/languageclient/languageclientcompletionassist.h index 24f0243326a..2f5ba014fab 100644 --- a/src/plugins/languageclient/languageclientcompletionassist.h +++ b/src/plugins/languageclient/languageclientcompletionassist.h @@ -28,6 +28,7 @@ #include "languageclient_global.h" #include +#include #include #include @@ -46,14 +47,6 @@ namespace LanguageClient { class Client; -using CompletionItemsTransformer = std::function( - const Utils::FilePath &, const QString &, int, - const QList &)>; -using CompletionApplyHelper = std::function; -using ProposalHandler = std::function; - class LANGUAGECLIENT_EXPORT LanguageClientCompletionAssistProvider : public TextEditor::CompletionAssistProvider { @@ -71,20 +64,13 @@ public: void setTriggerCharacters(const Utils::optional> triggerChars); - void setProposalHandler(const ProposalHandler &handler) { m_proposalHandler = handler; } void setSnippetsGroup(const QString &group) { m_snippetsGroup = group; } protected: - void setItemsTransformer(const CompletionItemsTransformer &transformer); - void setApplyHelper(const CompletionApplyHelper &applyHelper); Client *client() const { return m_client; } - const ProposalHandler &proposalHandler() const { return m_proposalHandler; } private: QList m_triggerChars; - CompletionItemsTransformer m_itemsTransformer; - CompletionApplyHelper m_applyHelper; - ProposalHandler m_proposalHandler; QString m_snippetsGroup; int m_activationCharSequenceLength = 0; Client *m_client = nullptr; // not owned @@ -94,17 +80,20 @@ class LANGUAGECLIENT_EXPORT LanguageClientCompletionAssistProcessor : public TextEditor::IAssistProcessor { public: - LanguageClientCompletionAssistProcessor(Client *client, - const CompletionItemsTransformer &itemsTransformer, - const CompletionApplyHelper &applyHelper, - const ProposalHandler &proposalHandler, - const QString &snippetsGroup); + LanguageClientCompletionAssistProcessor(Client *client, const QString &snippetsGroup); ~LanguageClientCompletionAssistProcessor() override; TextEditor::IAssistProposal *perform(const TextEditor::AssistInterface *interface) override; bool running() override; bool needsRestart() const override { return true; } void cancel() override; +protected: + QTextDocument *document() const; + Utils::FilePath filePath() const { return m_filePath; } + int basePos() const { return m_basePos; } + virtual QList generateCompletionItems( + const QList &items) const; + private: void handleCompletionResponse(const LanguageServerProtocol::CompletionRequest::Response &response); @@ -113,12 +102,45 @@ private: QPointer m_client; Utils::optional m_currentRequest; QMetaObject::Connection m_postponedUpdateConnection; - const CompletionItemsTransformer m_itemsTransformer; - const CompletionApplyHelper m_applyHelper; - const ProposalHandler m_proposalHandler; const QString m_snippetsGroup; int m_pos = -1; int m_basePos = -1; }; +class LANGUAGECLIENT_EXPORT LanguageClientCompletionItem + : public TextEditor::AssistProposalItemInterface +{ +public: + LanguageClientCompletionItem(LanguageServerProtocol::CompletionItem item); + + // AssistProposalItemInterface interface + QString text() const override; + QString filterText() const override; + bool implicitlyApplies() const override; + bool prematurelyApplies(const QChar &typedCharacter) const override; + void apply(TextEditor::TextDocumentManipulatorInterface &manipulator, + int basePosition) const override; + QIcon icon() const override; + QString detail() const override; + bool isSnippet() const override; + bool isValid() const override; + quint64 hash() const override; + + LanguageServerProtocol::CompletionItem item() const; + QChar triggeredCommitCharacter() const; + + const QString &sortText() const; + bool hasSortText() const; + + bool operator <(const LanguageClientCompletionItem &other) const; + + bool isPerfectMatch(int pos, QTextDocument *doc) const; + +private: + LanguageServerProtocol::CompletionItem m_item; + mutable QChar m_triggeredCommitCharacter; + mutable QString m_sortText; + mutable QString m_filterText; +}; + } // namespace LanguageClient diff --git a/src/plugins/languageclient/languageclientfunctionhint.cpp b/src/plugins/languageclient/languageclientfunctionhint.cpp index 2092b758993..76a851d0cd4 100644 --- a/src/plugins/languageclient/languageclientfunctionhint.cpp +++ b/src/plugins/languageclient/languageclientfunctionhint.cpp @@ -81,9 +81,8 @@ QString FunctionHintProposalModel::text(int index) const + label.mid(end).toHtmlEscaped(); } -FunctionHintProcessor::FunctionHintProcessor(Client *client, const ProposalHandler &proposalHandler) +FunctionHintProcessor::FunctionHintProcessor(Client *client) : m_client(client) - , m_proposalHandler(proposalHandler) {} IAssistProposal *FunctionHintProcessor::perform(const AssistInterface *interface) @@ -117,26 +116,18 @@ void FunctionHintProcessor::handleSignatureResponse(const SignatureHelpRequest:: m_client->removeAssistProcessor(this); auto result = response.result().value_or(LanguageClientValue()); if (result.isNull()) { - processProposal(nullptr); + setAsyncProposalAvailable(nullptr); return; } const SignatureHelp &signatureHelp = result.value(); if (signatureHelp.signatures().isEmpty()) { - processProposal(nullptr); + setAsyncProposalAvailable(nullptr); } else { FunctionHintProposalModelPtr model(new FunctionHintProposalModel(signatureHelp)); - processProposal(new FunctionHintProposal(m_pos, model)); + setAsyncProposalAvailable(new FunctionHintProposal(m_pos, model)); } } -void FunctionHintProcessor::processProposal(IAssistProposal *proposal) -{ - if (m_proposalHandler) - m_proposalHandler(proposal); - else - setAsyncProposalAvailable(proposal); -} - FunctionHintAssistProvider::FunctionHintAssistProvider(Client *client) : CompletionAssistProvider(client) , m_client(client) @@ -145,7 +136,7 @@ FunctionHintAssistProvider::FunctionHintAssistProvider(Client *client) TextEditor::IAssistProcessor *FunctionHintAssistProvider::createProcessor( const AssistInterface *) const { - return new FunctionHintProcessor(m_client, m_proposalHandler); + return new FunctionHintProcessor(m_client); } IAssistProvider::RunType FunctionHintAssistProvider::runType() const diff --git a/src/plugins/languageclient/languageclientfunctionhint.h b/src/plugins/languageclient/languageclientfunctionhint.h index 5c12ef7ac62..fa74823e6c8 100644 --- a/src/plugins/languageclient/languageclientfunctionhint.h +++ b/src/plugins/languageclient/languageclientfunctionhint.h @@ -40,8 +40,6 @@ namespace LanguageClient { class Client; -using ProposalHandler = std::function; - class LANGUAGECLIENT_EXPORT FunctionHintAssistProvider : public TextEditor::CompletionAssistProvider { Q_OBJECT @@ -58,11 +56,8 @@ public: void setTriggerCharacters(const Utils::optional> &triggerChars); - void setProposalHandler(const ProposalHandler &handler) { m_proposalHandler = handler; } - private: QList m_triggerChars; - ProposalHandler m_proposalHandler; int m_activationCharSequenceLength = 0; Client *m_client = nullptr; // not owned }; @@ -70,7 +65,7 @@ private: class LANGUAGECLIENT_EXPORT FunctionHintProcessor : public TextEditor::IAssistProcessor { public: - explicit FunctionHintProcessor(Client *client, const ProposalHandler &proposalHandler); + explicit FunctionHintProcessor(Client *client); TextEditor::IAssistProposal *perform(const TextEditor::AssistInterface *interface) override; bool running() override { return m_currentRequest.has_value(); } bool needsRestart() const override { return true; } @@ -79,10 +74,8 @@ public: private: void handleSignatureResponse( const LanguageServerProtocol::SignatureHelpRequest::Response &response); - void processProposal(TextEditor::IAssistProposal *proposal); QPointer m_client; - const ProposalHandler m_proposalHandler; Utils::optional m_currentRequest; int m_pos = -1; }; diff --git a/src/plugins/mcusupport/wizards/application/wizard.json b/src/plugins/mcusupport/wizards/application/wizard.json index 99888785734..e9caecf4c86 100644 --- a/src/plugins/mcusupport/wizards/application/wizard.json +++ b/src/plugins/mcusupport/wizards/application/wizard.json @@ -7,6 +7,7 @@ "trDisplayName": "Mcu Support Application", "trDisplayCategory": "Application (Qt for MCU)", "icon": "../icon.png", + "iconKind": "Themed", "enabled": true, "options": diff --git a/src/plugins/mcusupport/wizards/icon.png b/src/plugins/mcusupport/wizards/icon.png index ef9160b74cf..9043bb1ae04 100644 Binary files a/src/plugins/mcusupport/wizards/icon.png and b/src/plugins/mcusupport/wizards/icon.png differ diff --git a/src/plugins/mcusupport/wizards/icon@2x.png b/src/plugins/mcusupport/wizards/icon@2x.png index 0b1e6f875a2..10b452daae0 100644 Binary files a/src/plugins/mcusupport/wizards/icon@2x.png and b/src/plugins/mcusupport/wizards/icon@2x.png differ diff --git a/src/plugins/mesonprojectmanager/tests/testmesoninfoparser.cpp b/src/plugins/mesonprojectmanager/tests/testmesoninfoparser.cpp index cbbf9042daf..e25fec5456e 100644 --- a/src/plugins/mesonprojectmanager/tests/testmesoninfoparser.cpp +++ b/src/plugins/mesonprojectmanager/tests/testmesoninfoparser.cpp @@ -27,7 +27,7 @@ #include "mesoninfoparser/mesoninfoparser.h" #include -#include +#include #include #include @@ -79,9 +79,8 @@ class AMesonInfoParser : public QObject private slots: void initTestCase() { - Utils::LauncherInterface::startLauncher( - QCoreApplication::instance()->applicationDirPath() + '/' - + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH)); + Utils::LauncherInterface::setPathToLauncher(qApp->applicationDirPath() + '/' + + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH)); } void shouldListTargets_data() @@ -121,11 +120,10 @@ private slots: void cleanupTestCase() { - Utils::LauncherInterface::stopLauncher(); + Utils::Singleton::deleteAll(); } private: - Utils::ProcessReaper processReaper; }; QTEST_MAIN(AMesonInfoParser) diff --git a/src/plugins/mesonprojectmanager/tests/testmesonwrapper.cpp b/src/plugins/mesonprojectmanager/tests/testmesonwrapper.cpp index c257434151c..7db2c49480d 100644 --- a/src/plugins/mesonprojectmanager/tests/testmesonwrapper.cpp +++ b/src/plugins/mesonprojectmanager/tests/testmesonwrapper.cpp @@ -26,7 +26,7 @@ #include "exewrappers/mesonwrapper.h" #include -#include +#include #include #include @@ -50,9 +50,8 @@ class AMesonWrapper : public QObject private slots: void initTestCase() { - Utils::LauncherInterface::startLauncher( - QCoreApplication::instance()->applicationDirPath() + '/' - + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH)); + Utils::LauncherInterface::setPathToLauncher(qApp->applicationDirPath() + '/' + + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH)); } void shouldFindMesonFromPATH() @@ -115,11 +114,10 @@ private slots: void cleanupTestCase() { - Utils::LauncherInterface::stopLauncher(); + Utils::Singleton::deleteAll(); } private: - Utils::ProcessReaper processReaper; }; QTEST_MAIN(AMesonWrapper) diff --git a/src/plugins/projectexplorer/customwizard/customwizardpage.cpp b/src/plugins/projectexplorer/customwizard/customwizardpage.cpp index 0e256bd9fc3..4348b0f72e3 100644 --- a/src/plugins/projectexplorer/customwizard/customwizardpage.cpp +++ b/src/plugins/projectexplorer/customwizard/customwizardpage.cpp @@ -303,7 +303,7 @@ void CustomWizardFieldPage::initializePage() { QWizardPage::initializePage(); clearError(); - foreach (const LineEditData &led, m_lineEdits) { + for (const LineEditData &led : qAsConst(m_lineEdits)) { if (!led.userChange.isNull()) { led.lineEdit->setText(led.userChange); } else if (!led.defaultText.isEmpty()) { @@ -314,7 +314,7 @@ void CustomWizardFieldPage::initializePage() if (!led.placeholderText.isEmpty()) led.lineEdit->setPlaceholderText(led.placeholderText); } - foreach (const TextEditData &ted, m_textEdits) { + for (const TextEditData &ted : qAsConst(m_textEdits)) { if (!ted.userChange.isNull()) { ted.textEdit->setText(ted.userChange); } else if (!ted.defaultText.isEmpty()) { @@ -323,13 +323,13 @@ void CustomWizardFieldPage::initializePage() ted.textEdit->setText(defaultText); } } - foreach (const PathChooserData &ped, m_pathChoosers) { + for (const PathChooserData &ped : qAsConst(m_pathChoosers)) { if (!ped.userChange.isNull()) { - ped.pathChooser->setPath(ped.userChange); + ped.pathChooser->setFilePath(FilePath::fromUserInput(ped.userChange)); } else if (!ped.defaultText.isEmpty()) { QString defaultText = ped.defaultText; CustomWizardContext::replaceFields(m_context->baseReplacements, &defaultText); - ped.pathChooser->setPath(defaultText); + ped.pathChooser->setFilePath(FilePath::fromUserInput(defaultText)); } } } diff --git a/src/plugins/projectexplorer/images/importasproject.png b/src/plugins/projectexplorer/images/importasproject.png index b75e0422f7f..6cee1958256 100644 Binary files a/src/plugins/projectexplorer/images/importasproject.png and b/src/plugins/projectexplorer/images/importasproject.png differ diff --git a/src/plugins/projectexplorer/images/importasproject@2x.png b/src/plugins/projectexplorer/images/importasproject@2x.png index 8c708b52e3b..e352692fc9e 100644 Binary files a/src/plugins/projectexplorer/images/importasproject@2x.png and b/src/plugins/projectexplorer/images/importasproject@2x.png differ diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp b/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp index bb8f24f03e2..579722a63d6 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp @@ -73,6 +73,7 @@ const char DISPLAY_NAME_KEY[] = "trDisplayName"; const char ICON_KEY[] = "icon"; const char ICON_TEXT_KEY[] = "iconText"; const char IMAGE_KEY[] = "image"; +const char ICON_KIND_KEY[] = "iconKind"; const char DESCRIPTION_KEY[] = "trDescription"; const char REQUIRED_FEATURES_KEY[] = "featuresRequired"; const char SUGGESTED_FEATURES_KEY[] = "featuresSuggested"; @@ -586,7 +587,12 @@ bool JsonWizardFactory::initialize(const QVariantMap &data, const FilePath &base return false; } const QString iconText = data.value(QLatin1String(ICON_TEXT_KEY)).toString(); - setIcon(strVal.isEmpty() ? QIcon() : QIcon(iconPath.toString()), iconText); + const bool iconIsThemed = data.value(QLatin1String(ICON_KIND_KEY)).toString() + .compare("Themed", Qt::CaseInsensitive) == 0; + setIcon(iconIsThemed ? themedIcon(iconPath) + : strVal.isEmpty() ? QIcon() + : QIcon(iconPath.toString()), + iconText); strVal = data.value(QLatin1String(IMAGE_KEY)).toString(); if (!strVal.isEmpty()) { diff --git a/src/plugins/projectexplorer/projectexplorericons.cpp b/src/plugins/projectexplorer/projectexplorericons.cpp index dbc92be002a..663e16a9ae4 100644 --- a/src/plugins/projectexplorer/projectexplorericons.cpp +++ b/src/plugins/projectexplorer/projectexplorericons.cpp @@ -64,8 +64,8 @@ const Icon DEVICE_DISCONNECTED_INDICATOR({ {":/utils/images/filledcircle.png", Theme::IconsStopColor}}, Icon::Tint); const Icon DEVICE_DISCONNECTED_INDICATOR_OVERLAY({ {":/projectexplorer/images/devicestatusindicator.png", Theme::IconsStopToolBarColor}}); -const Icon WIZARD_IMPORT_AS_PROJECT( - ":/projectexplorer/images/importasproject.png"); +const Icon WIZARD_IMPORT_AS_PROJECT({ + {":/projectexplorer/images/importasproject.png", Theme::PanelTextColorDark}}, Icon::Tint); const Icon DEBUG_START_FLAT({ {":/projectexplorer/images/run_mask.png", Theme::IconsRunToolBarColor}, diff --git a/src/plugins/projectexplorer/projectwelcomepage.cpp b/src/plugins/projectexplorer/projectwelcomepage.cpp index 64fc21f1c12..8cf3fc70f9a 100644 --- a/src/plugins/projectexplorer/projectwelcomepage.cpp +++ b/src/plugins/projectexplorer/projectwelcomepage.cpp @@ -562,8 +562,7 @@ public: auto manageSessionsButton = new WelcomePageButton(this); manageSessionsButton->setText(ProjectWelcomePage::tr("Manage")); - const Icon icon({{":/utils/images/settings.png", Theme::Welcome_ForegroundSecondaryColor}}, Icon::Tint); - manageSessionsButton->setIcon(icon.pixmap()); + manageSessionsButton->setIcon(pixmap("settings", Theme::Welcome_ForegroundSecondaryColor)); manageSessionsButton->setOnClicked([] { ProjectExplorerPlugin::showSessionManager(); }); auto newButton = new WelcomePageButton(this); @@ -614,14 +613,12 @@ public: auto vbox1 = new QVBoxLayout; vbox1->setContentsMargins(0, 0, 0, 0); - vbox1->addStrut(200); vbox1->addItem(hbox11); vbox1->addSpacing(16); vbox1->addWidget(sessionsList); auto vbox2 = new QVBoxLayout; vbox2->setContentsMargins(0, 0, 0, 0); - vbox1->addStrut(200); vbox2->addItem(hbox21); vbox2->addSpacing(16); vbox2->addWidget(projectsList); @@ -629,9 +626,10 @@ public: auto hbox = new QHBoxLayout(this); hbox->setContentsMargins(30, 27, 0, 27); hbox->addItem(vbox1); + hbox->setStretchFactor(vbox1, 1); hbox->addSpacing(16); hbox->addItem(vbox2); - hbox->setStretchFactor(vbox2, 2); + hbox->setStretchFactor(vbox2, 3); } SessionDelegate m_sessionDelegate; diff --git a/src/plugins/projectexplorer/simpleprojectwizard.cpp b/src/plugins/projectexplorer/simpleprojectwizard.cpp index dda273fc754..91cc18ab3f4 100644 --- a/src/plugins/projectexplorer/simpleprojectwizard.cpp +++ b/src/plugins/projectexplorer/simpleprojectwizard.cpp @@ -34,6 +34,7 @@ #include #include +#include #include #include #include @@ -169,7 +170,7 @@ SimpleProjectWizard::SimpleProjectWizard() { setSupportedProjectTypes({QmakeProjectManager::Constants::QMAKEPROJECT_ID, CMakeProjectManager::Constants::CMAKE_PROJECT_ID}); - setIcon(QIcon(QLatin1String(":/projectexplorer/images/importasproject.png"))); + setIcon(ProjectExplorer::Icons::WIZARD_IMPORT_AS_PROJECT.icon()); setDisplayName(tr("Import as qmake or cmake Project (Limited Functionality)")); setId("Z.DummyProFile"); setDescription(tr("Imports existing projects that do not use qmake, CMake, Qbs, Meson, or Autotools.

" diff --git a/src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetwizard.cpp b/src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetwizard.cpp index f1cc3b9151e..1675cbe9908 100644 --- a/src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetwizard.cpp +++ b/src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetwizard.cpp @@ -48,7 +48,7 @@ CustomWidgetWizard::CustomWidgetWizard() ProjectExplorer::Constants::QT_PROJECT_WIZARD_CATEGORY_DISPLAY)); setDisplayName(tr("Qt Custom Designer Widget")); setDescription(tr("Creates a Qt Custom Designer Widget or a Custom Widget Collection.")); - setIcon(QIcon(QLatin1String(":/wizards/images/gui.png"))); + setIcon(themedIcon(":/wizards/images/gui.png")); setRequiredFeatures({QtSupport::Constants::FEATURE_QWIDGETS}); } diff --git a/src/plugins/qmakeprojectmanager/wizards/images/gui.png b/src/plugins/qmakeprojectmanager/wizards/images/gui.png index fba6e608a09..b60008a3c26 100644 Binary files a/src/plugins/qmakeprojectmanager/wizards/images/gui.png and b/src/plugins/qmakeprojectmanager/wizards/images/gui.png differ diff --git a/src/plugins/qmakeprojectmanager/wizards/images/gui@2x.png b/src/plugins/qmakeprojectmanager/wizards/images/gui@2x.png index 1c20ad695b3..1ccd0931eca 100644 Binary files a/src/plugins/qmakeprojectmanager/wizards/images/gui@2x.png and b/src/plugins/qmakeprojectmanager/wizards/images/gui@2x.png differ diff --git a/src/plugins/qmakeprojectmanager/wizards/subdirsprojectwizard.cpp b/src/plugins/qmakeprojectmanager/wizards/subdirsprojectwizard.cpp index 7c51c471e5d..ca63700cd62 100644 --- a/src/plugins/qmakeprojectmanager/wizards/subdirsprojectwizard.cpp +++ b/src/plugins/qmakeprojectmanager/wizards/subdirsprojectwizard.cpp @@ -48,7 +48,7 @@ SubdirsProjectWizard::SubdirsProjectWizard() setDisplayName(tr("Subdirs Project")); setDescription(tr("Creates a qmake-based subdirs project. This allows you to group " "your projects in a tree structure.")); - setIcon(QIcon(QLatin1String(":/wizards/images/gui.png"))); + setIcon(themedIcon(":/wizards/images/gui.png")); setRequiredFeatures({QtSupport::Constants::FEATURE_QT_PREFIX}); } diff --git a/src/plugins/welcome/images/settings.png b/src/plugins/welcome/images/settings.png new file mode 100644 index 00000000000..bc4d07a7414 Binary files /dev/null and b/src/plugins/welcome/images/settings.png differ diff --git a/src/plugins/welcome/images/settings@2x.png b/src/plugins/welcome/images/settings@2x.png new file mode 100644 index 00000000000..698eb391b7c Binary files /dev/null and b/src/plugins/welcome/images/settings@2x.png differ diff --git a/src/plugins/welcome/welcome.qrc b/src/plugins/welcome/welcome.qrc index 018d398c1b0..70cf9cd9377 100644 --- a/src/plugins/welcome/welcome.qrc +++ b/src/plugins/welcome/welcome.qrc @@ -25,5 +25,7 @@ images/border.png images/download.png images/download@2x.png + images/settings.png + images/settings@2x.png diff --git a/src/shared/qbs b/src/shared/qbs index 27bd9ac836b..4592eff2898 160000 --- a/src/shared/qbs +++ b/src/shared/qbs @@ -1 +1 @@ -Subproject commit 27bd9ac836b5cd2937b8d19dfa32cb4ff617b73c +Subproject commit 4592eff289852e3e5a81596d1cc6b0c2488e6bc0 diff --git a/src/tools/icons/export.py b/src/tools/icons/export.py index bf8f0baa393..2ead1ceab4c 100644 --- a/src/tools/icons/export.py +++ b/src/tools/icons/export.py @@ -112,6 +112,8 @@ def printOutUnexported(svgIDs, scaleFactors): def exportPngs(svgIDs, svgFile, scaleFactors, inkscape): inkscapeProcess = subprocess.Popen([inkscape, '--shell'], stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, cwd=qtcRoot()) actions = ["file-open:" + svgFile] for id in svgIDs: diff --git a/src/tools/icons/qtcreatoricons.svg b/src/tools/icons/qtcreatoricons.svg index 0791aeb2575..af3ce0d8a03 100644 --- a/src/tools/icons/qtcreatoricons.svg +++ b/src/tools/icons/qtcreatoricons.svg @@ -517,90 +517,6 @@ fy="91.5" r="18.5" gradientUnits="userSpaceOnUse" /> - - - - - - - - - - - - - - - - - + style="fill:#000000;stroke-width:0.90625" /> @@ -1762,7 +1678,7 @@ height="100%" /> @@ -1779,7 +1695,7 @@ style="display:inline" x="0" y="0" - xlink:href="#transparentBackgroundRect_60_60" + xlink:href="#backgroundRect_60_60" id="use5906" width="100%" height="100%" @@ -1790,7 +1706,7 @@ height="44" width="44" id="rect9153" - style="fill:#6b7080" /> + style="fill:#000000" /> @@ -1816,13 +1732,13 @@ sodipodi:nodetypes="csccccccccccccccccccc" inkscape:connector-curvature="0" d="m 263.88612,170.56484 c -2.25619,-7.49215 -13.9354,-14.04789 -27.88611,-14.04789 -14.23027,0 -25.62993,6.55574 -27.88613,14.04789 l -2.69076,2.30637 -3.80901,-7.33845 -2.93538,-12.37053 5.03208,2.0967 c 1.376,-1.39235 2.35711,-2.47631 5.87076,-4.40307 l 3.35472,-6.91911 5.66109,4.40307 c 3.80983,-0.90692 7.59785,-1.85747 11.95119,-1.67736 L 236,141.42071 l 5.45142,5.24175 c 4.35334,-0.18011 8.14136,0.77044 11.95119,1.67736 l 5.66109,-4.40307 3.35472,6.91911 c 3.51365,1.92676 4.49476,3.01072 5.87076,4.40307 l 5.03208,-2.0967 -2.93538,12.37053 -3.80901,7.33845 z" - style="fill:#cecfd5" + style="fill:#a0a0a0" id="path3799" /> @@ -1833,7 +1749,7 @@ style="display:inline" x="0" y="0" - xlink:href="#transparentBackgroundRect_60_60" + xlink:href="#backgroundRect_60_60" id="use5906-4" width="100%" height="100%" /> @@ -1842,13 +1758,13 @@ inkscape:connector-curvature="0" id="rect8115-9-7-8-0" d="m 212.99412,157.98972 10,10 v 34 h -34 v -44 z" - style="fill:#dfe0e3;stroke:#6b7080;stroke-width:2;fill-opacity:1" /> + style="fill:#f0f0f0;fill-opacity:1;stroke:#000000;stroke-width:2" /> + style="fill:#000000;fill-opacity:1" /> @@ -1857,7 +1773,7 @@ style="display:inline" x="0" y="0" - xlink:href="#transparentBackgroundRect_60_60" + xlink:href="#backgroundRect_60_60" id="use5906-9" width="100%" height="100%" /> @@ -1887,13 +1803,13 @@ inkscape:connector-curvature="0" id="rect8389-0-7-1-1-30-0" d="m 214.11732,109.89875 -21.5,10.5 19,10.5 21,-12.5 z" - style="fill:#6b7080" /> + style="fill:#606060" /> + style="fill:#000000" /> @@ -1918,19 +1834,19 @@ id="wizardicons_puzzlepiece" transform="translate(93,-57.5)"> + style="display:inline;fill:#000000;fill-opacity:1" /> + style="fill:#000000" /> + style="fill:#a0a0a0" /> + style="fill:#d0d0d0;fill-opacity:1" /> @@ -2048,15 +1964,15 @@ inkscape:connector-curvature="0" id="path3357-6-2" d="m 284.44641,270.8572 c 0,0 -2.27003,-11.36616 3.42782,-15.38049 5.69783,-4.01433 17.08994,-3.43121 28.16377,-12.78788 -7.08902,12.04204 -7.24264,18.00316 -13.84237,24.63796 -6.59973,6.63476 -17.74922,3.53041 -17.74922,3.53041 z" - style="fill:#3a4055" /> + style="fill:#202020" /> + style="fill:#f0f0f0" /> @@ -2084,34 +2000,34 @@ @@ -2195,7 +2111,7 @@ style="display:inline" x="0" y="0" - xlink:href="#transparentBackgroundRect_60_60" + xlink:href="#backgroundRect_60_60" id="use5906-0" width="100%" height="100%" @@ -2204,25 +2120,25 @@ id="g5886" transform="matrix(0.78941483,0,0,0.78941483,101.83171,55.17776)"> @@ -2233,7 +2149,7 @@ style="display:inline" x="0" y="0" - xlink:href="#transparentBackgroundRect_60_60" + xlink:href="#backgroundRect_60_60" id="use5906-0-4" width="100%" height="100%" @@ -2245,30 +2161,29 @@ sodipodi:nodetypes="cccccccccc" inkscape:connector-curvature="0" d="m 591,256 v 1 h 40 v -1 z m 0,-32 v 28 h 40 v -28 z" - style="fill:#848895;fill-opacity:1" + style="fill:#000000;fill-opacity:1" id="path81" /> + transform="translate(0,-60)"> + transform="translate(60)"> + style="fill:#000000" /> + transform="translate(120)"> @@ -2326,7 +2239,7 @@ style="display:inline" x="0" y="0" - xlink:href="#transparentBackgroundRect_60_60" + xlink:href="#backgroundRect_60_60" id="use5906-0-4-3-5-6-5" width="100%" height="100%" @@ -2344,7 +2257,7 @@ x="0" style="display:inline" /> @@ -2401,7 +2314,7 @@ style="display:inline" x="0" y="0" - xlink:href="#transparentBackgroundRect_60_60" + xlink:href="#backgroundRect_60_60" id="use5906-0-4-3-5-6-3" width="100%" height="100%" @@ -2416,7 +2329,7 @@ height="100%" transform="translate(93.5,2)" /> @@ -2458,7 +2371,7 @@